Je suis sur le point de créer 100 000 objets dans le code. Ce sont des petits, avec seulement 2 ou 3 propriétés. Je vais les mettre dans une liste générique et quand ils le sont, je les boucle et vérifie la valeur a
et peut-être la mise à jour b
.
Est-il plus rapide / meilleur de créer ces objets en tant que classe ou en tant que struct?
ÉDITER
une. Les propriétés sont des types de valeur (sauf la chaîne je pense?)
b. Ils pourraient (nous ne sommes pas encore sûrs) avoir une méthode de validation
MODIFIER 2
Je me demandais: les objets sur le tas et la pile sont-ils traités de manière égale par le garbage collector, ou cela fonctionne-t-il différemment?
Réponses:
Vous êtes la seule personne à pouvoir déterminer la réponse à cette question. Essayez les deux méthodes, mesurez une mesure de performance significative, centrée sur l'utilisateur et pertinente, puis vous saurez si le changement a un effet significatif sur les utilisateurs réels dans des scénarios pertinents.
Les structures consomment moins de mémoire de tas (parce qu'elles sont plus petites et plus facilement compactées, pas parce qu'elles sont «sur la pile»). Mais ils prennent plus de temps à copier qu'une copie de référence. Je ne sais pas quelles sont vos mesures de performance pour l'utilisation de la mémoire ou la vitesse; il y a un compromis ici et vous êtes la personne qui sait ce que c'est.
Peut-être classe, peut-être struct. En règle générale: Si l'objet est:
1. Petit
2. Logiquement une valeur immuable
3. Il y en a beaucoup
Alors j'envisagerais d'en faire une structure. Sinon, je m'en tiendrai à un type de référence.
Si vous avez besoin de muter un champ d'une structure, il est généralement préférable de créer un constructeur qui renvoie une nouvelle structure entière avec le champ correctement défini. C'est peut-être un peu plus lent (mesurez-le!) Mais logiquement beaucoup plus facile à raisonner.
Non , ils ne sont pas les mêmes car les objets de la pile sont les racines de la collection . Le ramasse-miettes n'a pas besoin de se demander "est-ce que cette chose sur la pile est vivante?" parce que la réponse à cette question est toujours "Oui, c'est sur la pile". (Maintenant, vous ne pouvez pas compter sur cela pour garder un objet en vie car la pile est un détail d'implémentation. La gigue est autorisée à introduire des optimisations qui, par exemple, enregistrent ce qui serait normalement une valeur de pile, et ensuite ce n'est jamais sur la pile donc le GC ne sait pas qu'il est toujours en vie. Un objet enregistré peut voir ses descendants collectés de manière agressive, dès que le registre qui le contient ne sera pas relu.)
Mais le garbage collector doit traiter les objets de la pile comme vivants, de la même manière qu'il traite tout objet connu comme vivant. L'objet sur la pile peut faire référence à des objets alloués au tas qui doivent être maintenus en vie, de sorte que le GC doit traiter les objets de pile comme des objets alloués au tas vivants aux fins de déterminer l'ensemble en direct. Mais évidemment, ils ne sont pas traités comme des "objets vivants" dans le but de compacter le tas, car ils ne sont pas sur le tas en premier lieu.
Est-ce clair?
la source
readonly
) pour permettre des optimisations. Je ne laisserais pas cela affecter un choix sur la mutabilité (je suis un passionné des détails d'efficacité en théorie, mais en pratique, mon premier pas vers l'efficacité est toujours d'essayer d'avoir une garantie d'exactitude aussi simple que possible et donc de ne pas avoir à le faire. gaspiller les cycles du processeur et les cycles cérébraux sur les chèques et les cas de pointe, et le fait d'être correctement mutable ou immuable y contribue), mais cela contrerait toute réaction instinctive à votre affirmation que l'immuabilité peut être plus lente.Parfois,
struct
vous n'avez pas besoin d'appeler le constructeur new () et d'assigner directement les champs, ce qui le rend beaucoup plus rapide que d'habitude.Exemple:
est environ 2 à 3 fois plus rapide que
où
Value
est unstruct
avec deux champs (id
etisValid
).D'autre part, les éléments doivent être déplacés ou les types de valeur sélectionnés tout ce que la copie va vous ralentir. Pour obtenir la réponse exacte, je suppose que vous devez profiler votre code et le tester.
la source
list
, étant donné que le code indiqué ne fonctionnera pas avec unList<Value>
.Les structures peuvent sembler similaires aux classes, mais il existe des différences importantes dont vous devez être conscient. Tout d'abord, les classes sont des types référence et les structs sont des types valeur. En utilisant des structures, vous pouvez créer des objets qui se comportent comme les types intégrés et profiter de leurs avantages également.
Lorsque vous appelez l'opérateur New sur une classe, il sera alloué sur le tas. Cependant, lorsque vous instanciez une structure, elle est créée sur la pile. Cela entraînera des gains de performance. De plus, vous ne traiterez pas de références à une instance d'une structure comme vous le feriez avec des classes. Vous travaillerez directement avec l'instance struct. Pour cette raison, lors du passage d'un struct à une méthode, il est passé par valeur au lieu de comme référence.
Plus ici:
http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx
la source
Les tableaux de structures sont représentés sur le tas dans un bloc contigu de mémoire, tandis qu'un tableau d'objets est représenté comme un bloc contigu de références avec les objets réels eux-mêmes ailleurs sur le tas, nécessitant ainsi de la mémoire pour les objets et pour leurs références de tableau .
Dans ce cas, comme vous les placez dans a
List<>
(et que aList<>
est sauvegardé sur un tableau), il serait plus efficace, en termes de mémoire, d'utiliser des structures.(Attention cependant, les grands tableaux trouveront leur chemin sur le tas d'objets volumineux où, si leur durée de vie est longue, ils peuvent avoir un effet négatif sur la gestion de la mémoire de votre processus. N'oubliez pas non plus que la mémoire n'est pas la seule considération.)
la source
ref
mots clés pour gérer cela.S'ils ont une sémantique de valeur, vous devriez probablement utiliser une structure. S'ils ont une sémantique de référence, vous devriez probablement utiliser une classe. Il existe des exceptions, qui penchent principalement vers la création d'une classe même lorsqu'il existe une sémantique de valeur, mais à partir de là.
Quant à votre deuxième édition, le GC ne traite que du tas, mais il y a beaucoup plus d'espace de tas que d'espace de pile, donc mettre des choses sur la pile n'est pas toujours une victoire. En plus de cela, une liste de struct-types et une liste de class-types seront sur le tas de toute façon, donc ce n'est pas pertinent dans ce cas.
Éditer:
Je commence à considérer le terme mal comme nocif. Après tout, rendre une classe mutable est une mauvaise idée si elle n'est pas activement nécessaire, et je n'exclurais jamais d'utiliser une structure mutable. C'est une mauvaise idée si souvent qu'elle est presque toujours une mauvaise idée, mais la plupart du temps, cela ne coïncide pas avec la sémantique des valeurs, donc cela n'a tout simplement pas de sens d'utiliser une structure dans le cas donné.
Il peut y avoir des exceptions raisonnables avec les structures imbriquées privées, où toutes les utilisations de cette structure sont donc limitées à une portée très limitée. Cela ne s'applique pas ici cependant.
Vraiment, je pense que "ça mute donc c'est une mauvaise structure" n'est pas beaucoup mieux que de parler du tas et de la pile (ce qui a au moins un impact sur les performances, même si celui-ci est souvent déformé). "Il mute, donc cela n'a probablement pas de sens de le considérer comme ayant une sémantique de valeur, donc c'est une mauvaise structure" n'est que légèrement différent, mais surtout je pense.
la source
La meilleure solution est de mesurer, mesurer à nouveau, puis mesurer un peu plus. Il peut y avoir des détails sur ce que vous faites qui peuvent rendre difficile une réponse simplifiée et facile comme «utiliser des structures» ou «utiliser des classes».
la source
Une structure n'est, en son cœur, ni plus ni moins qu'une agrégation de champs. Dans .NET, il est possible pour une structure de "faire semblant" d'être un objet, et pour chaque type de structure .NET définit implicitement un type d'objet de tas avec les mêmes champs et méthodes qui - étant un objet de tas - se comporteront comme un objet . Une variable qui contient une référence à un tel objet de tas (structure "en boîte") présentera une sémantique de référence, mais celle qui contient directement une structure est simplement une agrégation de variables.
Je pense qu'une grande partie de la confusion struct-versus-class provient du fait que les structures ont deux cas d'utilisation très différents, qui devraient avoir des directives de conception très différentes, mais les directives MS ne font pas de distinction entre elles. Parfois, il y a un besoin de quelque chose qui se comporte comme un objet; dans ce cas, les directives MS sont assez raisonnables, bien que la "limite de 16 octets" devrait probablement être plus comme 24-32. Parfois, cependant, il faut une agrégation de variables. Une structure utilisée à cette fin devrait simplement consister en un ensemble de champs publics, et éventuellement un
Equals
remplacement, unToString
remplacement etIEquatable(itsType).Equals
la mise en oeuvre. Les structures qui sont utilisées comme agrégations de champs ne sont pas des objets et ne doivent pas prétendre l'être. Du point de vue de la structure, la signification du champ ne doit être ni plus ni moins que «la dernière chose écrite dans ce champ». Toute signification supplémentaire doit être déterminée par le code client.Par exemple, si une structure d'agrégation de variables a des membres
Minimum
etMaximum
, la structure elle-même ne doit pas le promettreMinimum <= Maximum
. Le code qui reçoit une telle structure en tant que paramètre doit se comporter comme s'il avait été transmis séparémentMinimum
etMaximum
valeurs. Une exigence quiMinimum
n'est pas supérieure àMaximum
doit être considérée comme une exigence selon laquelle unMinimum
paramètre n'est pas supérieur à un paramètre passé séparémentMaximum
.Un modèle utile à considérer parfois est d'avoir une
ExposedHolder<T>
classe définie quelque chose comme:Si on a un
List<ExposedHolder<someStruct>>
, oùsomeStruct
est une structure d'agrégation de variables, on peut faire des choses commemyList[3].Value.someField += 7;
, mais donnermyList[3].Value
à un autre code lui donnera le contenuValue
plutôt que de lui donner un moyen de le modifier. En revanche, si on utilisait aList<someStruct>
, il faudrait utiliservar temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Si l'on utilisait un type de classe mutable, exposer le contenu demyList[3]
à un code extérieur nécessiterait de copier tous les champs vers un autre objet. Si l'on utilisait un type de classe immuable, ou une structure "de style objet", il serait nécessaire de construire une nouvelle instance qui ressemblerait à l'myList[3]
exception de cellesomeField
qui était différente, puis de stocker cette nouvelle instance dans la liste.Une note supplémentaire: si vous stockez un grand nombre de choses similaires, il peut être bon de les stocker dans des tableaux de structures éventuellement imbriqués, en essayant de préférence de garder la taille de chaque tableau entre 1K et 64K environ. Les tableaux de structures sont spéciaux, en ce que l'indexation donnera une référence directe à une structure à l'intérieur, donc on peut dire "a [12] .x = 5;". Bien que l'on puisse définir des objets de type tableau, C # ne leur permet pas de partager une telle syntaxe avec des tableaux.
la source
Utilisez des cours.
Sur une note générale. Pourquoi ne pas mettre à jour la valeur b au fur et à mesure que vous les créez?
la source
D'un point de vue c ++, je suis d'accord qu'il sera plus lent de modifier les propriétés d'une structure par rapport à une classe. Mais je pense qu'ils seront plus rapides à lire en raison de l'allocation de la structure sur la pile au lieu du tas. La lecture des données à partir du tas nécessite plus de contrôles qu'à partir de la pile.
la source
Eh bien, si vous optez pour struct après tout, supprimez la chaîne et utilisez un tampon de caractères ou d'octets de taille fixe.
C'est re: la performance.
la source