Pourquoi les compilateurs sont-ils si stupides?

Je me demande toujours pourquoi les compilateurs ne peuvent pas comprendre des choses simples et évidentes pour l’œil humain. Ils font beaucoup d’optimisations simples, mais jamais quelque chose d’un peu complexe. Par exemple, ce code prend environ 6 secondes sur mon ordinateur pour imprimer la valeur zéro (en utilisant java 1.6):

int x = 0; for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) { x += x + x + x + x + x; } System.out.println(x); 

Il est tout à fait évident que x n’est jamais modifié, donc peu importe la fréquence à laquelle vous ajoutez 0 à lui-même, il rest nul. Donc, le compilateur pourrait en théorie remplacer cela par System.out.println (0).

Ou mieux encore, cela prend 23 secondes:

 public int slow() { Ssortingng s = "x"; for (int i = 0; i < 100000; ++i) { s += "x"; } return 10; } 

Tout d’abord, le compilateur peut remarquer que je crée actuellement une chaîne de 100000 “x”, de sorte qu’il pourrait utiliser automatiquement SsortingngBuilder ou même le remplacer directement par la chaîne résultante, car elle est toujours la même. Deuxièmement, il ne reconnaît pas que je n’utilise pas la chaîne du tout, de sorte que toute la boucle pourrait être supprimée!

Pourquoi, après tant de travail, les compilateurs rapides sont-ils toujours aussi stupides?

EDIT : Bien sûr, ce sont des exemples stupides qui ne devraient jamais être utilisés nulle part. Mais chaque fois que je dois réécrire un code beau et très lisible en quelque chose illisible pour que le compilateur soit satisfait et produise du code rapide, je me demande pourquoi les compilateurs ou un autre outil automatisé ne peut pas faire ce travail pour moi.

Oh, je ne sais pas. Parfois, les compilateurs sont assez intelligents. Considérons le programme C suivant:

 #include  /* printf() */ int factorial(int n) { return n == 0 ? 1 : n * factorial(n - 1); } int main() { int n = 10; printf("factorial(%d) = %d\n", n, factorial(n)); return 0; } 

Sur ma version de GCC (4.3.2 sur les tests Debian ), compilée sans optimizaitons, ou -O1, elle génère du code pour factorial () comme vous vous en doutez, en utilisant un appel récursif pour calculer la valeur. Mais sur -O2, il fait quelque chose d’intéressant: il comstack jusqu’à une boucle serrée:

  factorial: .LFB13: testl %edi, %edi movl $1, %eax je .L3 .p2align 4,,10 .p2align 3 .L4: imull %edi, %eax subl $1, %edi jne .L4 .L3: rep ret 

Assez impressionnant. L’appel récursif (même pas récursif) a été complètement éliminé, donc factorial utilise maintenant l’espace de stack O (1) au lieu de O (N). Et même si je n’ai qu’une connaissance très superficielle de l’assemblage x86 (en fait AMD64 dans ce cas, mais je ne pense pas qu’aucune des extensions AMD64 soit utilisée ci-dessus), je doute que vous puissiez écrire une meilleure version à la main. Mais ce qui m’a vraiment frappé, c’est le code généré sur -O3. L’implémentation de factorial est restée la même. Mais main () a changé:

  main: .LFB14: subq $8, %rsp .LCFI0: movl $3628800, %edx movl $10, %esi movl $.LC0, %edi xorl %eax, %eax call printf xorl %eax, %eax addq $8, %rsp ret 

Voir la ligne movl $3628800, %edx ? gcc est pré-calcul factoriel (10) au moment de la compilation. Il n’appelle même pas factorial (). Incroyable. Mon chapeau est à l’équipe de développement de GCC.

Bien sûr, tous les avertissements habituels s’appliquent, il ne s’agit que d’un exemple, l’optimisation prématurée est la racine de tout mal, etc., mais elle montre que les compilateurs sont souvent plus intelligents que vous ne le pensez. Si vous pensez pouvoir faire un meilleur travail à la main, vous avez certainement tort.

(Adapté d’un article sur mon blog )

À mon avis, je ne crois pas que le compilateur ait pour tâche de réparer ce qui est, honnêtement, un mauvais codage. Vous avez, de manière explicite, dit au compilateur que vous voulez exécuter cette première boucle. C’est la même chose que:

 x = 0 sleep 6 // Let's assume this is defined somewhere. print x 

Je ne voudrais pas que le compilateur supprime mon énoncé de sleep simplement parce qu’il n’a rien fait. Vous pouvez faire valoir que la déclaration de sumil est une demande explicite de délai alors que votre exemple ne l’est pas. Mais ensuite, vous autoriserez le compilateur à prendre des décisions de très haut niveau sur ce que votre code doit faire, et je pense que c’est une mauvaise chose.

Code, et le compilateur qui le traite, sont des outils et vous devez être un outil si vous voulez les utiliser efficacement. Combien de tronçonneuses 12 “refuseront d’essayer de couper un arbre de 30”? Combien de forets passeront automatiquement en mode marteau s’ils détectent un mur en béton?

Aucun, je pense, et c’est parce que le coût de la conception de ce produit dans le produit serait horrible pour commencer. Mais, plus important encore, vous ne devriez pas utiliser des exercices ou des scies à chaîne si vous ne savez pas ce que vous faites. Par exemple: si vous ne savez pas ce qu’est un rebond (un moyen très facile pour un débutant de se déshabiller), restz loin des scies à chaîne jusqu’à ce que vous le fassiez.

Je suis tout à fait pour permettre aux compilateurs de suggérer des améliorations, mais je préfère conserver le contrôle moi-même. Le compilateur ne devrait pas décider unilatéralement qu’une boucle est inutile.

Par exemple, j’ai effectué des boucles de synchronisation dans des systèmes embarqués où la vitesse d’horloge de la CPU est connue avec précision, mais aucun dispositif de synchronisation fiable n’est disponible. Dans ce cas, vous pouvez calculer précisément combien de temps une boucle donnée prendra et l’utiliser pour contrôler la fréquence des événements. Cela ne fonctionnerait pas si le compilateur (ou l’assembleur dans ce cas) décidait que ma boucle était inutile et optimisait son existence.

Cela dit, permettez-moi de vous raconter une vieille histoire d’un compilateur VAX FORTRAN qui faisait l’object d’une évaluation des performances et il a été constaté qu’il était beaucoup plus rapide que son plus proche concurrent.

Il s’avère que le compilateur a remarqué que le résultat des boucles de référence n’était utilisé nulle part ailleurs et a optimisé les boucles en un oubli.

Du sharepoint vue du C / C ++:

Votre premier exemple sera optimisé par la plupart des compilateurs. Si le compilateur Java de Sun exécute réellement cette boucle, c’est le défaut du compilateur, mais sachez que tout compilateur C, C ++ ou Fortran post 1990 élimine complètement une telle boucle.

Votre deuxième exemple ne peut pas être optimisé dans la plupart des langues, car l’allocation de mémoire se produit comme un effet secondaire de la concaténation des chaînes. Si un compilateur optimisait le code, le modèle d’allocation de mémoire changerait, ce qui pourrait entraîner des effets que le programmeur essaie d’éviter. La fragmentation de la mémoire et les problèmes associés sont des problèmes auxquels les programmeurs intégrés sont confrontés chaque jour.

Globalement, je suis satisfait des optimisations que les compilateurs peuvent faire ces jours-ci.

Les compilateurs sont conçus pour être prévisibles . Cela peut les rendre stupides de temps en temps, mais ça va. Les objectives de l’auteur du compilateur sont les suivants:

  • Vous devriez pouvoir regarder votre code et faire des prévisions raisonnables sur ses performances.

  • De petites modifications apscopes au code ne doivent pas entraîner de différences de performances importantes.

  • Si une petite modification ressemble au programmeur, cela devrait améliorer les performances, mais au moins ne pas dégrader les performances (à moins que des choses surprenantes ne se produisent dans le matériel).

Tous ces critères militent contre les optimisations «magiques» qui ne s’appliquent qu’aux cas d’angle.


Vos deux exemples ont une variable mise à jour dans une boucle mais non utilisée ailleurs . Ce cas est en fait assez difficile à prendre en compte, sauf si vous utilisez une sorte de framework qui peut combiner l’élimination du code mort avec d’autres optimisations telles que la propagation de la copie ou la propagation constante. Pour un simple optimiseur de stream de données, la variable n’a pas l’air morte. Pour comprendre pourquoi ce problème est difficile, voir l’ article de Lerner, Grove et Chambers dans POPL 2002 , qui utilise cet exemple et explique pourquoi c’est difficile.

Le compilateur HotSpot JIT n’optimise que le code exécuté depuis un certain temps. Au moment où votre code est chaud, la boucle a déjà été lancée et le compilateur JIT doit attendre la prochaine saisie de la méthode pour rechercher des moyens d’optimiser la boucle. Si vous appelez la méthode plusieurs fois, vous obtiendrez peut-être de meilleures performances.

Ceci est couvert dans la FAQ de HotSpot , sous la question “J’écris une simple boucle pour chronométrer une opération simple et elle est lente. Qu’est-ce que je fais mal?”.

Sérieusement? Pourquoi quelqu’un écrirait-il un code réel comme celui-là? IMHO, le code, pas le compilateur est l’entité “stupide” ici. Je suis tout à fait heureux que les auteurs de compilateurs ne prennent pas la peine d’essayer d’optimiser quelque chose comme ça.

Edit / Clarification: Je sais que le code dans la question est un exemple, mais cela prouve mon sharepoint vue: vous devez soit essayer, soit être assez ignorant pour écrire un code extrêmement inefficace comme celui-ci. Ce n’est pas le travail du compilateur de tenir notre main pour ne pas écrire un code horrible. En tant que personnes qui écrivent le code, nous avons la responsabilité d’en savoir plus sur nos outils pour rédiger de manière efficace et claire.

Eh bien, je ne peux parler que de C ++, car je suis un débutant de Java totalement. En C ++, les compilateurs sont libres de ne pas tenir compte des exigences linguistiques imposées par la norme, à condition que le comportement observable soit tel que le compilateur émule effectivement toutes les règles placées par la norme. Le comportement observable est défini comme toute lecture et écriture sur des données volatiles et des appels aux fonctions de bibliothèque . Considère ceci:

 extern int x; // defined elsewhere for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) { x += x + x + x + x + x; } return x; 

Le compilateur C ++ est autorisé à optimiser ce morceau de code et il suffit d'append la valeur correcte à x qui résulterait de cette boucle une fois, car le code se comporte comme si la boucle ne s'était jamais produite, et aucune fonction volatile cela pourrait causer des effets secondaires nécessaires. Considérons maintenant les variables volatiles:

 extern volatile int x; // defined elsewhere for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) { x += x + x + x + x + x; } return x; 

Le compilateur n'est plus autorisé à faire la même optimisation, car il ne peut pas prouver que les effets secondaires causés par l'écriture sur x ne pourraient pas affecter le comportement observable du programme. Après tout, x pourrait être défini sur une cellule mémoire surveillée par un périphérique matériel qui se déclencherait à chaque écriture.


En parlant de Java, j'ai testé votre boucle, et il arrive que le compilateur Java GNU ( gcj ) prenne énormément de temps pour terminer votre boucle (cela ne s'est tout simplement pas terminé et je l'ai tué). J'ai activé les indicateurs d'optimisation (-O2) et il s'est avéré qu'il imprimait 0 immédiatement:

 [js@HOST2 java]$ gcj --main=Optimize -O2 Optimize.java [js@HOST2 java]$ ./a.out 0 [js@HOST2 java]$ 

Peut-être que cette observation pourrait être utile dans ce fil? Pourquoi ça arrive si vite pour gcj? Eh bien, une des raisons est certainement que gcj comstack en code machine, et qu'il n'a donc aucune possibilité d'optimiser ce code en fonction du comportement d'exécution du code. Il rassemble toutes ses forces et essaie d'optimiser autant qu'il peut au moment de la compilation. Une machine virtuelle, cependant, peut comstackr le code Just in Time, comme le montre cette sortie de java pour ce code:

 class Optimize { private static int doIt() { int x = 0; for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) { x += x + x + x + x + x; } return x; } public static void main(String[] args) { for(int i=0;i<5;i++) { doIt(); } } } 

Sortie pour java -XX:+PrintCompilation Optimize :

 1 java.lang.Ssortingng::hashCode (60 bytes) 1% Optimize::doIt @ 4 (30 bytes) 2 Optimize::doIt (30 bytes) 

Comme nous le voyons, JIT comstack la fonction doIt 2 fois. Basé sur l'observation de la première exécution, il le comstack une seconde fois. Mais il se trouve que la taille du bytecode est deux fois supérieure, ce qui suggère que la boucle est toujours en place.

Comme le montre un autre programmeur, le temps d'exécution de certaines boucles mortes est même augmenté dans certains cas pour le code compilé ultérieurement. Il a signalé un bug qui peut être lu ici et qui date du 24 octobre 2008.

Sur votre premier exemple, c’est une optimisation qui ne fonctionne que si la valeur est zéro. L’instruction if supplémentaire dans le compilateur nécessaire pour rechercher cette clause rarement vue ne vaut peut-être pas la peine (car elle devra vérifier cela sur chaque variable). En outre, qu’en est-il de ceci:

 int x = 1; int y = 1; int z = x - y; for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) { z += z + z + z + z + z; } System.out.println(z); 

C'est toujours évidemment la même chose, mais il y a maintenant un cas supplémentaire que nous devons coder dans le compilateur. Il y a juste un nombre infini de façons dont cela peut finir par être zéro, ce qui ne vaut pas la peine d'être codé, et je suppose que vous pourriez dire que si vous voulez en avoir un, vous pourriez tout aussi bien les avoir tous.

Certaines optimisations prennent en compte le deuxième exemple que vous avez publié, mais je pense que je l'ai vu davantage dans les langages fonctionnels et pas tellement en Java. La grande chose qui rend difficile dans les nouvelles langues est le patch de singe . Maintenant, += peut avoir un effet secondaire, ce qui signifie que si nous l'optimisons, c'est potentiellement faux (par exemple, l'ajout de fonctionnalités à += qui imprime la valeur actuelle signifie un programme complètement différent).

Mais tout se résume à la même chose: il y a trop de cas à vérifier pour éviter tout effet secondaire susceptible de modifier l'état final du programme.

Il est juste plus facile de prendre un moment supplémentaire et de s’assurer que ce que vous écrivez est ce que vous voulez vraiment que l’ordinateur fasse. 🙂

Les compilateurs en général sont très intelligents.

Ce que vous devez prendre en compte, c’est qu’ils doivent prendre en compte toutes les exceptions ou situations possibles où l’optimisation ou la refactorisation du code pourrait entraîner des effets secondaires indésirables.

Des éléments tels que les programmes threadés, l’alias de pointeur, le code lié de manière dynamic et les effets secondaires (appels système / allocation de mémoire), etc. rendent très difficile le refactoring proactif.

Même si votre exemple est simple, il peut toujours y avoir des situations difficiles à prendre en compte.

En ce qui concerne votre argument SsortingngBuilder, ce n’est PAS un travail de compilateur pour choisir les structures de données à utiliser pour vous.

Si vous voulez des optimisations plus puissantes, déplacez-vous vers un langage plus fortement typé tel que fortran ou haskell, où les compilateurs reçoivent beaucoup plus d’informations pour travailler.

La plupart des cours qui enseignent les compilateurs / l’optimisation (même de façon académique) donnent une idée de la façon dont il est très difficile de faire des optimisatons formels, plutôt que de pirater des cas spécifiques.

Je pense que vous sous-estimez la quantité de travail nécessaire pour vous assurer qu’un morceau de code n’affecte pas un autre morceau de code. Avec juste un petit changement à vos exemples, x, i et s pourraient tous pointer vers la même mémoire. Une fois que l’une des variables est un pointeur, il est beaucoup plus difficile de dire quel code peut avoir des effets secondaires, selon le sharepoint vue.

De plus, je pense que les gens qui programment des compleurs préfèrent passer du temps à faire des optimisations moins faciles à réaliser pour les humains.

Parce que nous ne sums pas encore là. Vous pourriez tout aussi facilement demander: “Pourquoi ai-je encore besoin d’écrire des programmes … pourquoi ne puis-je pas simplement insérer le document d’exigences et demander à l’ordinateur d’écrire l’application pour moi?”

Les auteurs de compilateurs passent du temps sur les petites choses, car ce sont les types de choses que les programmeurs d’applications manquent souvent.

En outre, ils ne peuvent pas assumer trop (peut-être que votre boucle était une sorte de retard de ghetto ou quelque chose)?

C’est une course aux armements éternelle entre les auteurs de compilateurs et les programmeurs.

Les exemples non élaborés fonctionnent très bien – la plupart des compilateurs optimisent en effet le code évidemment inutile.

Les examens contrôlés contiendront toujours le compilateur. La preuve, le cas échéant, que tout programmeur est plus intelligent que n’importe quel programme.

À l’avenir, vous aurez besoin d’exemples plus artificiels que ceux que vous avez publiés ici.

Comme d’autres ont abordé correctement la première partie de votre question, j’essaierai d’aborder la deuxième partie, à savoir “utilise automatiquement SsortingngBuilder à la place”.

Il y a plusieurs bonnes raisons de ne pas faire ce que vous proposez, mais le facteur le plus important dans la pratique est que l’optimiseur s’exécute longtemps après que le code source a été digéré et oublié. Les optimiseurs opèrent généralement soit sur le code d’octet généré (ou assemblage, code à trois adresses, code machine, etc.), soit sur les arborescences de syntaxe abstraites résultant de l’parsing du code. Les optimiseurs ne connaissent généralement rien des bibliothèques d’exécution (ou des bibliothèques du tout), et fonctionnent plutôt au niveau des instructions (c’est-à-dire, stream de contrôle de bas niveau et allocation de registre).

Deuxièmement, comme les bibliothèques évoluent beaucoup plus rapidement que les langages (en Java, notamment), il serait très difficile de savoir ce qu’il est possible d’adopter comme composant de la bibliothèque. Aussi probablement impossible, car cet optimiseur proposé devrait comprendre précisément vos intentions et les intentions de chaque composant de bibliothèque disponible et trouver une correspondance entre elles.

Enfin, comme d’autres l’ont déjà dit (je pense), le rédacteur du compilateur / optimiseur peut raisonnablement supposer que le programmeur qui écrit le code d’entrée n’est pas mort de cerveau. Ce serait une perte de temps que de consacrer des efforts considérables à la réalisation de ces cas particuliers lorsque d’autres optimisations plus générales se multiplient. En outre, comme d’autres l’ont également mentionné, un code apparemment irréfléchi peut avoir un objective réel (un locking de rotation, une attente chargée avant un rendement au niveau du système, etc.) et le compilateur doit respecter ce que le programmeur demande (si c’est syntaxiquement et sémantiquement valide).

Avez-vous compilé pour libérer le code? Je pense qu’un bon compilateur détecte dans votre deuxième exemple que la chaîne n’est jamais utilisée et supprime la boucle entière.

En fait, Java devrait utiliser le générateur de chaînes dans votre deuxième exemple.

Le problème de base en essayant d’optimiser ces exemples est que cela nécessiterait une démonstration de théorème . Ce qui signifie que le compilateur devra construire une preuve mathématique de ce que vous êtes réellement en train de faire. Et ce n’est pas une mince tâche du tout. En fait, être capable de prouver que tout le code a vraiment un effet est équivalent au problème d’arrêt.

Bien sûr, vous pouvez trouver des exemples sortingviaux, mais le nombre d’exemples sortingviaux est illimité. Vous pouvez toujours penser à autre chose, il n’y a donc aucun moyen de les attraper tous.

Bien sûr, il est possible de prouver que certains codes n’ont aucun effet, comme dans vos exemples. Ce que vous voulez faire, c’est que le compilateur optimise tous les problèmes qui peuvent être prouvés inutilisés en temps P.

Mais de toute façon, c’est une tonne de travail et ça ne vous donne pas grand chose. Les gens passent beaucoup de temps à essayer de trouver des moyens d’empêcher les programmes d’avoir des bogues, et les systèmes de type Java et Scala tentent d’empêcher les bogues. , Pour autant que je sache.

Vous voudrez peut-être vous pencher sur Haskel, qui, selon moi, a la théorie la plus avancée, bien que je ne sois pas certain de cela. Je ne le sais pas moi-même.

La plupart du temps, vous vous demandez pourquoi le compilateur Java est si stupide, car la plupart des autres compilateurs de langage sont beaucoup plus intelligents.

La raison de la stupidité des compilateurs Java est historique. Premièrement, les implémentations java d’origine étaient basées sur un interpréteur et les performances étaient sans importance. Deuxièmement, bon nombre des tests java originaux étaient problématiques à optimiser. Je me souviens d’un sharepoint repère qui ressemblait beaucoup à votre deuxième exemple. Malheureusement, si le compilateur optimisait la boucle, le benchmark obtiendrait une exception de division par zéro lorsqu’il tenterait de diviser le nombre de lignes de base par le temps écoulé pour calculer son score de performance. Donc, lorsque vous écrivez un compilateur Java optimisé, vous devez faire très attention à ne pas optimiser certaines choses, car les utilisateurs peuvent alors prétendre que votre compilateur est cassé.

Il est presque considéré comme une mauvaise pratique d’optimiser ce genre de choses lors de la compilation en bytecode JVM. Le javac de Sun a quelques optimisations de base, comme le scalac , le groovyc , etc. En bref, tout ce qui est vraiment spécifique au langage peut être optimisé au sein du compilateur. Cependant, des choses comme celles-là, qui sont manifestement conçues de manière à ne pas être liées à la langue, passeront inaperçues.

La raison en est que cela permet à HotSpot d’avoir une vue beaucoup plus cohérente du bytecode et de ses patterns. Si les compilateurs commencent à chercher des cas extrêmes, cela réduit la capacité de la VM à optimiser le cas général qui peut ne pas apparaître au moment de la compilation. Steve Yeggie aime à en parler: l’optimisation est souvent plus facile lorsqu’elle est exécutée à l’exécution par une machine virtuelle intelligente. Il va même jusqu’à prétendre que HotSpot supprime les optimisations de javac. Bien que je ne sache pas si c’est vrai, cela ne me surprendra pas.

En résumé: les compilateurs ciblant les machines virtuelles ont un ensemble de critères très différent, en particulier dans le domaine de l’optimisation et du moment opportun. Ne pas blâmer les rédacteurs du compilateur pour avoir laissé le travail à la JVM beaucoup plus performante. Comme cela a été souligné à plusieurs resockets sur ce sujet, les compilateurs modernes ciblant l’architecture native (comme la famille gcc ) sont extrêmement intelligents, produisant du code obscurément rapide grâce à des optimisations très intelligentes.

Je n’ai jamais vu l’intérêt de l’élimination du code mort en premier lieu. Pourquoi le programmeur l’a-t-il écrit? Si vous voulez faire quelque chose à propos du code mort, déclarez-le comme une erreur de compilation! Cela signifie presque certainement que le programmeur a fait une erreur – et pour les quelques cas qui ne le sont pas, une directive du compilateur pour utiliser une variable serait la bonne réponse. Si je mets du code mort dans une routine, je veux qu’il soit exécuté – je prévois probablement d’inspecter les résultats dans le débogueur.

The case where the comstackr could do some good is pulling out loop invariants. Sometimes clarity says to code the calculation in the loop and having the comstackr pull such things out would be good.

Comstackrs that can do ssortingct-aliasing optimizations, will optimize first example out. Voir ici

Second example can’t be optimized because the slowest part here is memory allocation/reallocation and operator+= is redefined into a function that does the memory stuff. Different implementations of ssortingngs use different allocation strategies.

I myself also would rather like to have malloc(100000) than thousand malloc(100) too when doing s += “s”; but right now that thing is out of scope of comstackrs and has to be optimized by people. This is what D language sortinges to solve by introducing pure functions .

As mentioned here in other answers, perl does second example in less than a second because it allocates more memory than requested just in case more memory will be needed later.

In release mode VS 2010 C++ this doesnt take any time to run. However debug mode is another story.

 #include  int main() { int x = 0; for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) { x += x + x + x + x + x; } printf("%d", x); } 

Absolute optimization is an undecidable problem, that means, there is no Turing machine (and, therefore, no computer program) that can yield the optimal version of ANY given program.

Some simple optimizations can be (and, in fact, are) done, but, in the examples you gave…

  1. To detect that your first program always prints zero, the comstackr would have to detect that x remains constant despite all the loop iterations. How can you explain (I know, it’s not the best word, but I can’t come up with another) that to a comstackr?

  2. How can the comstackr know that the SsortingngBuilder is the right tool for the job without ANY reference to it?

In a real-world application, if efficiency is critical in a part of your application, it must be written in a low-level language like C. (Haha, seriously, I wrote this?)

This is an example of procedural code v. functional code.

You have detailed a procedure for the comstackr to follow, so the optimisations are going to be based around the procedure detailed and will minimise any side effects or not optimise where it will not be doing what you expect. This makes it easier to debug.

If you put in a functional description of what you want eg. SQL then you are giving the comstackr a wide range of options to optimise.

Perhaps some type of code analysis would be able to find this type of issue or profiling at run-time, but then you will want to change the source to something more sensible.

Because comstackr writers try add optimizations for things that matter (I hope) and that are measured in *Stone benchmarks (I fear).

There are zillions of other possible code fragments like yours, which do nothing and could be optimized with increasing effort on the comstackr writer, but which are hardly ever encountered.

What I feel embarrassing is that even today most comstackrs generate code to check for the switchValue being greater than 255 for a dense or almost full switch on an unsigned character. That adds 2 instructions to most bytecode interpreter’s inner loop.

I hate to bring this up on such an old question (how did I get here, anyway?), but I think part of this might be something of a holdout from the days of the Commodore 64.

In the early 1980s, everything ran on a fixed clock. There was no Turbo Boosting and code was always created for a specific system with a specific processor and specific memory, etc. In Commodore BASIC, the standard method for implementing delay s looked a lot like:

 10 FOR X = 1 TO 1000 20 NEXT : REM 1-SECOND DELAY 

(Actually, in practice, it more closely resembled 10FORX=1TO1000:NEXT , but you know what I mean.)

If they were to optimize this, it would break everything—nothing would ever be timed. I don’t know of any examples, but I’m sure there are lots of little things like this scattered through the history of comstackd languages that prevented things from being optimized.

Admittedly, these non-optimizations aren’t necessary today. There’s probably, however, some unspoken rule among comstackr developers not to optimize things like this. I wouldn’t know.

Just be glad that your code is optimized somewhat, unlike code on the C64. Displaying a bitmap on the C64 could take up to 60 seconds with the most efficient BASIC loops; thus, most games, etc. were written in machine language. Writing games in machine language isn’t fun.

Juste mes pensées.

Premise: I studied comstackrs at university.

The javac comstackr is extremely stupid and performs absolutely no optimization because it relies on the java runtime to do them. The runtime will catch that thing and optimize it, but it will catch it only after the function is executed a few thousand times.

If you use a better comstackr (like gcc) enabling optimizations, it will optimize your code, because it’s quite an obvious optimization to do.

Comstackrs are as smart as we make them. I don’t know too many programmers who would bother writing a comstackr that would check for constructs such as the ones you used. Most concentrate on more typical ways to improve performance.

It is possible that someday we will have software, including comstackrs, that can actually learn and grow. When that day comes most, maybe all, programmers will be out of job.

The meaning of your two examples is pointless, useless and only made to fool the comstackr.

The comstackr is not capable (and should not be) to see the meaning of a method, a loop or a program. That is where you get into the picture. You create a method for a certain functionality/meaning, no matter how stupid it is. It’s the same case for simple problems or extreme complex programs.

In your case the comstackr might optimize it, because it “thinks” it should be optimized in another way but why stay there?

Extreme other situation. We have a smart comstackr compiling Windows. Tons of code to comstack. But if it’s smart, it boils it down to 3 lines of code…

 "starting windows" "enjoy freecell/solitaire" "shutting down windows" 

The rest of the code is obsolete, because it’s never used, touched, accessed. Do we really want that?

It forces you (the programmer) to think about what you’re writing. Forcing comstackrs to do your work for you doesn’t help anyone: it makes the comstackrs much more complex (and slower!), and it makes you stupider and less attentive to your code.