Je viens de remarquer que tous les langages de programmation OO modernes avec lesquels je suis au moins quelque peu familiarisé (essentiellement Java, C # et D) autorisent les tableaux covariants. Autrement dit, un tableau de chaînes est un tableau d'objets:
Object[] arr = new String[2]; // Java, C# and D allow this
Les tableaux covariants sont un trou dans le système de types statique. Elles rendent possibles les erreurs de type qui ne peuvent pas être détectées lors de la compilation. Chaque écriture dans un tableau doit donc être vérifiée à l'exécution:
arr[0] = "hello"; // ok
arr[1] = new Object(); // ArrayStoreException
Cela semble être une terrible performance si je fais beaucoup de magasins de matrices.
C ++ n'a pas de tableaux covariants, il n'est donc pas nécessaire d'effectuer une telle vérification à l'exécution, ce qui signifie qu'il n'y a aucune pénalité de performance.
Une analyse est-elle effectuée pour réduire le nombre de vérifications d'exécution nécessaires? Par exemple, si je dis:
arr[1] = arr[0];
on pourrait dire que le magasin ne peut pas échouer. Je suis sûr qu'il y a beaucoup d'autres optimisations possibles auxquelles je n'ai pas pensé.
Les compilateurs modernes effectuent-ils ce type d'optimisation ou dois-je accepter le fait que, par exemple, un Quicksort effectue toujours des vérifications d'exécution inutiles O (n log n)?
Les langages OO modernes peuvent-ils éviter les frais généraux générés par la prise en charge de tableaux de co-variantes?
Réponses:
D n'a pas de tableaux covariants. Cela leur permettait avant la dernière version ( dmd 2.057 ), mais ce bogue a été corrigé.
Un tableau en D est en réalité juste une structure avec un pointeur et une longueur:
La vérification des limites est effectuée normalement lors de l'indexation d'un tableau, mais elle est supprimée lors de la compilation avec
-release
. Ainsi, en mode release, il n'y a pas de réelle différence de performances entre les tableaux en C / C ++ et ceux en D.la source
T[dim]
peut être converti implicitement à une des options suivantes: ...U[]
... Un tableau dynamiqueT[]
peut être converti implicitement à une des opérations suivantes:U[]
. .. oùU
est une classe de base deT
. "const U[]
, car dans ce cas, vous ne pouvez pas affecter un type incorrect aux éléments du tableau, mais vous ne pouvezT[]
certainement pas convertir enU[]
tant que ce dernierU[]
est modifiable. Autoriser les tableaux covariants comme auparavant était un sérieux défaut de conception qui a maintenant été corrigé.Oui, une optimisation cruciale est la suivante:
En C #, cette classe ne peut être un sur-type pour aucun type, vous pouvez donc éviter la vérification d'un tableau de type
Foo
.Et à la deuxième question, dans F # les co-variantes ne sont pas autorisées (mais je suppose que la vérification restera dans le CLR sauf si elle est jugée inutile dans les optimisations à l'exécution).
https://stackoverflow.com/questions/7339013/array-covariance-in-f
Un problème quelque peu lié est la vérification des tableaux. Cela pourrait être une lecture intéressante (mais ancienne) sur les optimisations effectuées dans le CLR (la covariance est également mentionnée à un endroit): http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds -check-élimination-dans-le-clr.aspx
la source
val a = Array("st"); val b: Array[Any] = a
est illégale. (Cependant, les tableaux dans Scala sont ... magiques spéciales ... en raison de la JVM sous-jacente utilisée.)Réponse Java:
Je suppose que vous n'avez pas réellement comparé le code, n'est-ce pas? En général, 90% des conversions dynamiques en Java sont gratuites car le JIT peut les élier (le tri rapide devrait en être un bon exemple) et le reste est une
ld/cmp/br
séquence absolument prévisible (sinon, pourquoi diable votre code est-il lancé?) toutes ces exceptions de distribution dynamique?).Nous effectuons la charge beaucoup plus tôt que la comparaison réelle, la branche est correctement prédite dans 99,9999% (statistiques composées!) De tous les cas, de sorte que nous ne bloquons pas le pipeline (en supposant que la charge ne soit pas saturée, si pas bien, ce sera visible, mais alors la charge est nécessaire de toute façon). Par conséquent, le coût est d'un cycle d'horloge SI le JIT ne peut pas du tout éviter la vérification.
Quelques frais généraux? Bien sûr, mais je doute que vous le remarquiez jamais ..
Pour aider à appuyer ma réponse, veuillez consulter cet article de blog du Dr. Cliff Click traitant des performances de Java contre C.
la source
D n'autorise pas les tableaux covariants.
Comme vous le dites, ce serait un trou dans le système de types de permettre cela.
Vous pouvez être pardonné pour cette erreur, car ce bogue n’a été corrigé que dans la dernière version de DMD, publiée le 13 décembre.
L'accès aux tableaux en D est aussi rapide qu'en C ou C ++.
la source
De test, j'ai fait sur un ordinateur portable pas cher, la différence entre l'utilisation
int[]
etInteger[]
est d'environ 1,0 ns. La différence est probablement due à la vérification supplémentaire pour le type.Généralement, les objets ne sont utilisés que pour la logique de niveau supérieur lorsque tous les ns ne comptent pas. Si vous devez enregistrer tous les ns, j’éviterai d’utiliser des constructions de niveau supérieur telles que Objects. Les affectations à elles seules risquent d’être très peu importantes dans tout programme réel. par exemple, la création d'un nouvel objet sur la même machine correspond à 5 ns.
Les appels à compareTo risquent d'être beaucoup plus coûteux, surtout si vous utilisez un objet complexe tel que String.
la source
Vous avez demandé d'autres langues OO modernes? Bien, Delphi évite complètement ce problème en étant
string
un primitif, pas un objet. Ainsi, un tableau de chaînes est exactement un tableau de chaînes et rien d’autre, et toutes les opérations qu’ils effectuent sont aussi rapides que le code natif, sans surcharge de vérification de type.Cependant, les tableaux de chaînes ne sont pas utilisés très souvent. Les programmeurs Delphi ont tendance à privilégier la
TStringList
classe. Pour un minimum de temps système, il fournit un ensemble de méthodes de groupe de chaînes qui sont utiles dans de nombreuses situations, si bien que la classe a été comparée à un couteau militaire suisse. Il est donc idiomatique d'utiliser un objet liste de chaînes au lieu d'un tableau de chaînes.Comme pour d’autres objets en général, le problème n’existe pas car dans Delphi, comme en C ++, les tableaux ne sont pas covariants, afin d’éviter le type de trous du système de types décrits ici.
la source
Les performances du processeur ne sont pas monotones, ce qui signifie que des programmes plus longs peuvent être plus rapides que des programmes plus courts (cela dépend du processeur et cela vaut pour les architectures communes x86 et amd64). Il est donc possible qu'un programme effectuant des vérifications liées sur des tableaux soit en réalité plus rapide que le programme déduit de la précédente en supprimant ces vérifications liées.
La raison de ce comportement est que la vérification liée modifie l'alignement du code en mémoire, modifie la fréquence des accès au cache, etc.
Alors oui, tenez compte du fait que Quicksort effectue toujours O (n log n) des contrôles parasites et optimise après le profilage.
la source
Scala est un langage OO qui a des tableaux invariants plutôt que covariants. Il cible la machine virtuelle Java. Par conséquent, il n’ya aucun gain de performances, mais il évite les erreurs communes à Java et à C # qui compromettent la sécurité de type pour des raisons de compatibilité avec les versions antérieures.
la source