64 votes

Pourquoi une structure .NET doit-elle être inférieure à 16 octets ?

J'ai lu à plusieurs endroits que la taille maximale d'une instance pour une structure devait être de 16 octets.

Mais je ne vois pas d'où vient ce chiffre (16).

En naviguant sur le net, j'ai trouvé certains qui suggèrent que c'est un nombre approximatif pour de bonnes performances, mais Microsoft parle comme si c'était une limite supérieure stricte. (par ex. MSDN )

Quelqu'un a-t-il une réponse définitive à la question de savoir pourquoi il s'agit de 16 octets ?

77voto

jalf Points 142628

Il s'agit simplement d'une règle empirique de performance.

Le fait est que, comme les types valeur sont transmis par valeur, la taille entière de la structure doit être copiée si elle est transmise à une fonction, alors que pour un type référence, seule la référence (4 octets) doit être copiée. Une structure peut cependant faire gagner un peu de temps parce que vous supprimez une couche d'indirection, donc même si elle est plus grande que ces 4 octets, elle peut être plus efficace que le passage d'une référence. Mais à un certain point, elle devient si grande que le coût de la copie devient perceptible. En règle générale, cela se produit autour de 16 octets. 16 est choisi parce que c'est un nombre rond, une puissance de deux, et les alternatives sont soit 8 (ce qui est trop petit, et rendrait les structures presque inutiles), soit 32 (à ce point le coût de la copie de la structure est déjà problématique si vous utilisez les structures pour des raisons de performance).

Mais en fin de compte, c'est un conseil de performance. Il répond à la question suivante : "Qu'est-ce qui serait le plus efficace à utiliser ? Un struct ou une classe ?". Mais il ne répond pas à la question de savoir "lequel correspond le mieux à mon domaine de problèmes".

Les structures et les classes se comportent différemment. Si vous avez besoin du comportement d'un struct, alors je dirais qu'il faut en faire un struct, quelle que soit sa taille. Du moins jusqu'à ce que vous rencontriez des problèmes de performance, que vous profiliez votre code et que vous découvriez que votre structure est un problème.

votre lien dit même que c'est juste une question de performance :

Si une ou plusieurs de ces conditions ne sont ne sont pas remplies, créez un type de référence au lieu d'une structure. Le non-respect de cette directive peut avoir un impact avoir un impact négatif sur les performances.

2 votes

Oui, le lien indique bien qu'il s'agit d'une question de performance, mais il est également assez fort dans le langage qu'il utilise, à savoir "Ne pas définir une structure...". Ils auraient pu dire "Il n'est pas recommandé...".

3 votes

C'est vrai, la formulation semble un peu forte. Mais cela pourrait être pour souligner que les classes allouées au tas ne sont pas lentes (comme les programmeurs venant de C/C++ pourraient s'y attendre).

4 votes

Une réponse probable à ce chiffre précis est qu'une structure de 16 octets est encore assez petite pour tenir sur le bus mémoire du CPU, ou pour être copiée dans le cadre d'une instruction SIMD. Les structures plus grandes deviennent plus complexes à copier ou à lire/écrire.

32voto

Guffa Points 308133

Si une structure n'est pas plus grande que 16 octets, elle peut être copiée avec quelques instructions simples du processeur. Si elle est plus grande, une boucle est utilisée pour copier la structure.

Tant que la structure n'est pas plus grande que 16 octets, le processeur doit effectuer à peu près le même travail lors de la copie de la structure que lors de la copie d'une référence. Si la structure est plus grande, vous perdez l'avantage de la structure en termes de performances, et vous devriez généralement la remplacer par une classe.

2 votes

Je ne suis pas un gourou de l'assemblage x86, je suis probablement un n00b complet en fait - mais je suis vraiment curieux, pourriez-vous mettre à jour votre réponse avec un exemple de code pour montrer cela ? Le fait que le processeur fonctionne en mode 32 bits ou 64 bits a-t-il une importance ?

1 votes

@Goyuix : Il y aura quelques différences de performance entre le code 32 bits et 64 bits bien sûr, mais cela suit les mêmes principes. J'ai fait un test de performance il y a quelque temps. Le code est juste beaucoup de structs et beaucoup de boucles, donc ce n'est pas si intéressant, mais vous pouvez voir le résultat ici : stackoverflow.com/questions/2437925/

1 votes

Il me semble que c'est la seule bonne réponse, et je ne sais pas pourquoi elle a été le plus souvent négligée. Le tableau de comparaison des performances de votre autre réponse est très révélateur.

24voto

Marc Gravell Points 482669

Le chiffre de la taille provient en grande partie du temps qu'il faut pour copier la structure sur la pile, par exemple pour la passer à une méthode. Si vous dépassez cette taille, vous consommez beaucoup d'espace sur la pile et de cycles CPU pour la simple copie des données, alors qu'une référence à une classe immuable (même avec déréférencement) pourrait être beaucoup plus efficace.

0 votes

En utilisant les dernières fonctionnalités de C#7.2, je suppose que vous pouvez vous débarrasser de la plupart de ces déchets de copie de données (lorsqu'ils sont utilisés correctement) ?

8voto

supercat Points 25534

Comme d'autres réponses l'ont noté, le coût de la copie d'une structure de plus de 16 octets est nettement supérieur à celui de la copie d'une structure de 16 octets ou moins. Il est important de noter, cependant, que copier une structure de tout taille particulière une fois sera une fraction du coût de la création d'une nouvelle instance d'objet de classe de même taille. Si une structure est appelée à être copiée de nombreuses fois au cours de sa vie, et que la sémantique de type valeur n'est pas particulièrement nécessaire, un objet de classe peut être préférable. Si, toutefois, une structure n'est copiée qu'une ou deux fois, cette copie sera probablement moins coûteuse que la création d'un nouvel objet de classe. Le seuil de rentabilité du nombre de copies pour lequel un objet de classe devient moins cher varie en fonction de la taille de la structure ou de l'objet en question, mais il est beaucoup plus élevé pour les objets de 16 octets ou moins que pour les objets de plus de 16 octets.

BTW, un autre point qui mérite d'être mentionné est que le coût du passage d'un struct en tant que ref est indépendant de la taille de la structure. Dans de nombreux cas, des performances optimales peuvent être obtenues en utilisant des types de valeurs et en les passant par le biais de ref . Il faut faire attention à ne pas utiliser les propriétés ou readonly des types de structures, cependant, car l'accès à l'un ou l'autre de ces champs créera une copie temporaire implicite de la structure en question.

1 votes

Je ne connaissais pas cette partie sur l'utilisation des propriétés ou readonly champs. Pouvez-vous m'indiquer un lien pour une lecture plus approfondie ?

1 votes

@Justin : Un getter de propriété n'est rien d'autre qu'une méthode dont le type de retour est le type de la propriété en question. En tant que telle, la propriété doit copier les informations dans n'importe quel registre ou emplacement mémoire utilisé pour la valeur de retour. Les champs en lecture seule sont copiés lors de l'accès à toute propriété ou méthode, car le compilateur ne peut pas savoir si la méthode appelée peut tenter de modifier la structure sur laquelle elle est invoquée. Contrairement à C#, .net ne dispose d'aucun mécanisme pour empêcher une méthode struct de modifier la structure qui lui est transmise ; Microsoft aurait pu gérer cette situation de deux manières :

1 votes

(1) Autoriser les structures en lecture seule à être passées directement aux méthodes, et espérer que personne n'essaie de les passer aux méthodes mutantes, ou (2) faire une copie temporaire d'une structure en lecture seule, et passer cette copie à la méthode appelée. Notez que (2) sera plus lent que (1), et ne produira généralement pas un comportement correct dans les cas où (1) ne le ferait pas aussi ; ce que (2) fait est de changer la nature du comportement incorrect quand une méthode tente de modifier une structure en lecture seule.

2voto

Raj Rao Points 3630

Voici un scénario où les structs peuvent présenter des performances supérieures :

Lorsque vous avez besoin de créer des milliers d'instances. Dans ce cas, si vous utilisiez une classe, vous devriez d'abord allouer le tableau pour contenir les milliers d'instances, puis allouer chaque instance dans une boucle. Au lieu de cela, si vous utilisez des structures, les milliers d'instances deviennent disponibles immédiatement après avoir alloué le tableau qui va les contenir.

En outre, les structs sont extrêmement utiles lorsque vous avez besoin de faire de l'interopérabilité ou que vous souhaitez plonger dans du code non sécurisé pour des raisons de performance.

Comme toujours, il y a un compromis à faire et il faut analyser ce que l'on fait pour déterminer la meilleure façon de mettre en œuvre quelque chose.

ps : Ce scénario s'est présenté lorsque j'ai travaillé avec des données LIDAR où il pouvait y avoir des millions de points représentant x, y, z et d'autres attributs pour les données au sol. Ces données devaient être chargées en mémoire pour effectuer des calculs intensifs afin d'obtenir toutes sortes de résultats.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X