web-dev-qa-db-fra.com

Pourquoi cette solution GoLang est-elle plus rapide que l'équivalent Java Solution?

Récemment au travail, nous jouions avec la question de quiz suivante posée par IBM https://www.research.ibm.com/haifa/ponderthis/challenges/May2015.html

Après un peu d'effort, un collègue et moi sommes arrivés à deux solutions, l'une dans GoLang https://Gist.github.com/walesey/e2427c28a859c4f7bc920c9af2858492#file-main-go-L57 et l'autre dans Java https://Gist.github.com/boyter/42df7f203c0932e37980f7974c017ec5#file-puzzle-Java-L6 avec la méthode critique de performance pour les deux étant des jeux de jeu dans Java et jeu dans GoLang (tous deux liés ci-dessus).

Le programme Go est presque une copie littérale de celui de Java, et pourtant son temps d'exécution est de ~ 6 secondes alors que celui de Java est d'environ ~ 26 secondes (sur ma machine locale). Des nombres similaires ont été répliqués sur quelques autres machines, le programme Go étant environ 5 fois plus rapide.

Le programme Go est compilé à l'aide de 1.7.5 et Java à l'aide de la version 1.8.0_65, tous deux exécutés sur macOS Sierra 10.12.3 sur un Macbook Pro Retina fin 2013 avec un processeur i5 à 2,6 GHz.

Pourquoi le programme Go est-il 5 fois plus rapide que celui de Java alors que la plupart des benchmarks indiquent que Java devrait à peu près le même temps d'exécution? Il s'agit simplement de mathématiques de base dans une boucle, il semble donc qu'elles devraient fonctionner à peu près en même temps. Je pourrais comprendre une seconde environ pour l'heure de démarrage de la JVM, mais cela semble éteint.

Les deux programmes utilisent à peu près la même boucle. Toutes les permutations possibles des résultats du jeu sont créées et répétées pour chaque montant de départ. Il semble simplement que pour un certain nombre d'opérations de bouclage dans la boucle principale, Go exécute des anneaux autour de Java.

Je comprends qu'il s'agit d'un "micro" benchmark, mais je me demande pourquoi exactement le code Go surpasse massivement le code Java. Est-ce juste que Go for simple loops/math est plus efficace et donc plus rapide? Est-il capable de dérouler la boucle peut-être (bien que cela semble peu susceptible de produire une différence aussi énorme)?

Sinon, comment structurer un programme Java pour tirer le meilleur parti d'une simple boucle et d'une opération mathématique?

[~ # ~] modifier [~ # ~] - Grâce à Dolda2000, j'ai modifié la version Java. Il est maintenant à peu près à la même vitesse que la version GoLang. En effet, le problème était que les jeux ont été créés obligeant la version Java à simuler plus de jeux pour déterminer si le jeu a duré assez longtemps. Avec les changements, il fonctionne en environ 6 secondes maintenant et a rétabli ma foi en Java.

Mise à jour - Voici un essai élargi qui discute le fond de cette question plus en détail.

39
Ben Boyter

Il s'avère que vos programmes ne sont pas aussi égaux que vous le pensez. Je les ai instrumentés pour voir combien de jeux (c'est-à-dire des tours de paris individuels) ils ont simulés, et tandis que la version Go simulait 1 612 629 805 jeux, la Java simulait 12 323 903 502 jeux, presque un ordre de grandeur de plus.

Sur ma machine, désactivant le multithreading pour des résultats plus prévisibles, le programme Java cadencé sur environ 75 secondes et le programme Go sur 12,5 secondes. En comparant cela avec le temps d'exécution total, il semble que le Java est en fait légèrement plus rapide par jeu simulé, à environ 6,1 ns, contre 7,8 ns pour le Go programme.

Je ne sais pas encore pourquoi ils simulent un nombre de jeux si différent. Peut-être que la façon dont la version Go génère les tours se trouve simplement pour trouver des terminaisons beaucoup plus rapides.

EDIT : En fait, cette dernière supposition a beaucoup de sens. La version Go commence en modulant les premiers tours d'un jeu, tandis que la version Java commence en modulant les derniers tours d'un jeu (en d'autres termes, en regardant la liste des tours comme liste des nombres de base 3 croissants à 11 chiffres, la version Go est peu endienne, tandis que la version Java version est big-endian, pour ainsi dire), donc la Java devra simuler à travers des démarrages beaucoup plus identiques pour arriver aux variations qui se terminent. Je n'ai pas essayé de vérifier cette hypothèse, mais je suis assez sûr à ce sujet que je ne ressens pas le besoin de .

47
Dolda2000