Quelqu'un connaît-il la réponse et / ou a-t-il une opinion à ce sujet?
Puisque les tuples ne seraient normalement pas très grands, je suppose qu'il serait plus logique d'utiliser des structures que des classes pour ces derniers. Ce que vous dites?
Quelqu'un connaît-il la réponse et / ou a-t-il une opinion à ce sujet?
Puisque les tuples ne seraient normalement pas très grands, je suppose qu'il serait plus logique d'utiliser des structures que des classes pour ces derniers. Ce que vous dites?
Réponses:
Microsoft a créé tous les types de références de types de tuple dans un souci de simplicité.
Je pense personnellement que c'était une erreur. Les tuples avec plus de 4 champs sont très inhabituels et devraient de toute façon être remplacés par une alternative plus typée (comme un type d'enregistrement en F #) donc seuls les petits tuples sont d'un intérêt pratique. Mes propres tests de performance ont montré que les tuples sans boîte jusqu'à 512 octets pouvaient toujours être plus rapides que les tuples en boîte.
Bien que l'efficacité de la mémoire soit une préoccupation, je pense que le problème dominant est la surcharge du garbage collector .NET. L'allocation et la collecte sont très coûteuses sur .NET car son garbage collector n'a pas été très fortement optimisé (par exemple par rapport à la JVM). De plus, le .NET GC (poste de travail) par défaut n'a pas encore été parallélisé. Par conséquent, les programmes parallèles qui utilisent des tuples s'arrêtent alors que tous les cœurs se disputent le garbage collector partagé, détruisant l'évolutivité. Ce n'est pas seulement la préoccupation dominante mais, AFAIK, a été complètement négligé par Microsoft quand ils ont examiné ce problème.
Une autre préoccupation est la répartition virtuelle. Les types de référence prennent en charge les sous-types et, par conséquent, leurs membres sont généralement appelés via une distribution virtuelle. En revanche, les types valeur ne peuvent pas prendre en charge les sous-types, de sorte que l'invocation de membre est sans ambiguïté et peut toujours être effectuée comme un appel de fonction direct. La distribution virtuelle est extrêmement coûteuse sur le matériel moderne car le processeur ne peut pas prédire où le compteur de programme se terminera. La JVM fait de grands efforts pour optimiser la répartition virtuelle, mais pas .NET. Cependant, .NET fournit une évasion de la distribution virtuelle sous la forme de types valeur. Donc, représenter les tuples comme des types valeur pourrait, encore une fois, avoir considérablement amélioré les performances ici. Par exemple, appelerGetHashCode
sur un 2-tuple un million de fois prend 0,17 s, mais l'appeler sur une structure équivalente ne prend que 0,008 s, c'est-à-dire que le type valeur est 20 fois plus rapide que le type référence.
Une situation réelle où ces problèmes de performances avec les tuples surviennent fréquemment est l'utilisation de tuples comme clés dans les dictionnaires. En fait, je suis tombé sur ce fil en suivant un lien de la question Stack Overflow F # exécute mon algorithme plus lentement que Python! où le programme F # de l'auteur s'est avéré être plus lent que son Python précisément parce qu'il utilisait des tuples encadrés. Le déballage manuel à l'aide d'un struct
type écrit à la main rend son programme F # plusieurs fois plus rapide et plus rapide que Python. Ces problèmes ne se seraient jamais posés si les tuples étaient représentés par des types valeur et non par des types référence pour commencer ...
Tuple<_,...,_>
types auraient pu être scellés, auquel cas aucun envoi virtuel ne serait nécessaire bien qu'il s'agisse de types de référence. Je suis plus curieux de savoir pourquoi ils ne sont pas scellés que de savoir pourquoi ce sont des types de référence.
La raison est très probablement parce que seuls les plus petits tuples auraient du sens en tant que types valeur car ils auraient une petite empreinte mémoire. Les plus gros tuples (c'est-à-dire ceux avec plus de propriétés) souffriraient en fait de la performance puisqu'ils seraient plus grands que 16 octets.
Plutôt que de faire en sorte que certains tuples soient des types valeur et que d'autres soient des types de référence et obligent les développeurs à savoir lesquels, j'imagine que les gens de Microsoft ont pensé que faire d'eux tous les types de référence était plus simple.
Ah, soupçons confirmés! S'il vous plaît voir Building Tuple :
La première décision majeure était de savoir s'il fallait traiter les tuples comme un type de référence ou de valeur. Puisqu'ils sont immuables à chaque fois que vous souhaitez modifier les valeurs d'un tuple, vous devez en créer un nouveau. S'il s'agit de types référence, cela signifie qu'il peut y avoir beaucoup de déchets générés si vous modifiez des éléments dans un tuple dans une boucle serrée. Les tuples F # étaient des types de référence, mais l'équipe avait le sentiment qu'elle pouvait améliorer les performances si deux, et peut-être trois, tuples d'éléments étaient des types valeur à la place. Certaines équipes qui avaient créé des tuples internes avaient utilisé la valeur au lieu des types référence, car leurs scénarios étaient très sensibles à la création de nombreux objets gérés. Ils ont constaté que l'utilisation d'un type valeur leur donnait de meilleures performances. Dans notre premier projet de spécification de tuple, nous avons conservé les tuples à deux, trois et quatre éléments comme types valeur, le reste étant des types référence. Cependant, lors d'une réunion de conception qui incluait des représentants d'autres langues, il a été décidé que cette conception «scindée» serait déroutante, en raison de la sémantique légèrement différente entre les deux types. La cohérence du comportement et de la conception a été jugée plus prioritaire que les augmentations potentielles des performances. Sur la base de cette entrée, nous avons modifié la conception afin que tous les tuples soient des types de référence, bien que nous ayons demandé à l'équipe F # de faire une enquête sur les performances pour voir si elle rencontrait une accélération lors de l'utilisation d'un type valeur pour certaines tailles de tuples. Il avait un bon moyen de tester cela, puisque son compilateur, écrit en F #, était un bon exemple d'un grand programme qui utilisait des tuples dans une variété de scénarios. En fin de compte, l'équipe F # a constaté qu'elle n'obtenait pas d'amélioration des performances lorsque certains tuples étaient des types valeur au lieu de types référence. Cela nous a permis de nous sentir mieux dans notre décision d'utiliser des types de référence pour le tuple.
Si les types .NET System.Tuple <...> étaient définis comme des structures, ils ne seraient pas évolutifs. Par exemple, un tuple ternaire d'entiers longs est actuellement mis à l'échelle comme suit:
type Tuple3 = System.Tuple<int64, int64, int64>
type Tuple33 = System.Tuple<Tuple3, Tuple3, Tuple3>
sizeof<Tuple3> // Gets 4
sizeof<Tuple33> // Gets 4
Si le tuple ternaire était défini en tant que struct, le résultat serait le suivant (basé sur un exemple de test que j'ai implémenté):
sizeof<Tuple3> // Would get 32
sizeof<Tuple33> // Would get 104
Comme les tuples ont un support de syntaxe intégré en F #, et qu'ils sont utilisés très souvent dans ce langage, les tuples "struct" exposeraient les programmeurs F # au risque d'écrire des programmes inefficaces sans même en être conscients. Cela arriverait si facilement:
let t3 = 1L, 2L, 3L
let t33 = t3, t3, t3
À mon avis, les tuples «struct» entraîneraient une forte probabilité de créer des inefficacités significatives dans la programmation quotidienne. D'autre part, les tuples de "classe" actuellement existants provoquent également certaines inefficacités, comme mentionné par @Jon. Cependant, je pense que le produit de la "probabilité d'occurrence" par le "dommage potentiel" serait beaucoup plus élevé avec les structures qu'il ne l'est actuellement avec les classes. Par conséquent, la mise en œuvre actuelle est le moindre mal.
Idéalement, il y aurait à la fois des tuples "class" et des tuples "struct", tous deux avec support syntaxique en F #!
Modifier (2017-10-07)
Les tuples de structure sont désormais entièrement pris en charge comme suit:
ref
, ou peut ne pas aimer le fait que les soi-disant «structures immuables» ne le sont pas, surtout lorsqu'elles sont encadrées. C'est dommage que .net n'ait jamais implémenté le concept de passage de paramètres par un exécutable const ref
, car dans de nombreux cas, une telle sémantique est vraiment nécessaire.
Dictionary
, par exemple ici: stackoverflow.com/questions/5850243 /…
Pour 2-tuples, vous pouvez toujours utiliser le KeyValuePair <TKey, TValue> des versions antérieures du Common Type System. C'est un type de valeur.
Une clarification mineure à l'article de Matt Ellis serait que la différence de sémantique d'utilisation entre les types référence et valeur n'est "légère" que lorsque l'immuabilité est en vigueur (ce qui, bien sûr, serait le cas ici). Néanmoins, je pense qu'il aurait été préférable dans la conception BCL de ne pas introduire la confusion du passage de Tuple à un type de référence à un certain seuil.
Je ne sais pas mais si vous avez déjà utilisé F # Les tuples font partie du langage. Si j'ai créé un .dll et renvoyé un type de tuples, ce serait bien d'avoir un type pour le mettre. Je soupçonne maintenant que F # fait partie du langage (.Net 4), des modifications ont été apportées à CLR pour accueillir certaines structures communes en fa #
Depuis http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records
let scalarMultiply (s : float) (a, b, c) = (a * s, b * s, c * s);;
val scalarMultiply : float -> float * float * float -> float * float * float
scalarMultiply 5.0 (6.0, 10.0, 20.0);;
val it : float * float * float = (30.0, 50.0, 100.0)
ValueTuple<...>
. Voir la référence sur les types de tuple C #