Pourquoi cette solution GoLang est-elle plus rapide que la solution Java équivalente?

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

Après quelques efforts, un collègue et moi sums arrivés à deux solutions, l’une dans GoLang https://gist.github.com/walesey/e2427c28a859c4f7bc920c9af2858492#file-main-go-L57 et l’autre en Java https: // gist. github.com/boyter/42df7f203c0932e37980f7974c017ec5#file-puzzle-java-L63 avec la méthode de performance critique pour être à la fois playGames en Java et le jeu en GoLang (tous deux liés ci-dessus).

Le programme Go est presque une copie littérale de Java, mais son temps d’exécution est d’environ 6 secondes, tandis 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é en utilisant 1.7.5 et Java en utilisant la version 1.8.0_65 tous deux exécutés sur macOS Sierra 10.12.3 sur une MacBook Pro rétine de fin 2013 avec un processeur i5 de 2,6 GHz.

Pourquoi le programme Go est-il 5 fois plus rapide que le programme Java alors que la plupart des tests de performances indiquent que Java devrait être à peu près le même temps d’exécution? Il ne s’agit que de mathématiques de base en boucle, il semble donc qu’ils devraient fonctionner à peu près au même moment. Je pourrais comprendre une seconde ou deux pour l’heure de début de la JVM, mais cela semble désactivé.

Les deux programmes utilisent pratiquement 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 juste que pour n’importe quel nombre d’opérations de bouclage dans la boucle principale, Go lance des anneaux autour de Java.

Je comprends que c’est 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 telle différence)?

Sinon, comment devriez-vous structurer un programme Java pour tirer le meilleur parti d’une simple opération de boucle et de calcul?

EDIT – Grâce à Dolda2000, j’ai modifié la version Java. Il est maintenant à peu près la même vitesse que la version GoLang. En fait, le problème était que les jeux étaient créés, ce qui obligeait la version Java à simuler plus de jeux pour déterminer si le jeu allait assez longtemps. Avec les changements, il est maintenant d’environ 6 secondes et a rétabli ma confiance en Java.

Mise à jour – Voici un essai élargi qui traite du contexte de cette question plus en détail.

En fait, vos programmes ne sont pas aussi égaux que vous le croyez. Je les ai instrumentés pour voir combien de jeux (c’est-à-dire les tours d’enchères individuels) ils simulaient, et tandis que la version Go simulait 1 612 629 805 jeux, la version Java simulait 12 323 903 502 jeux, presque un ordre de grandeur.

Sur ma machine, en désactivant le multithreading pour obtenir des résultats plus prévisibles, le programme Java a été activé en 75 secondes environ et le programme Go en 12,5 secondes. Cela correspond au temps d’exécution total, il semble que le programme Java est en fait légèrement plus rapide par jeu simulé, à environ 6,1 ns, contre 7,8 ns pour le programme Go.

Cependant, on ne sait pas encore pourquoi ils simulent des nombres de jeux si différents. Peut-être que la façon dont la version Go génère les rounds se trouve simplement à trouver des terminaisons beaucoup plus rapides.

EDIT : En fait, cette dernière supposition a beaucoup de sens. La version Go commence par moduler les tours initiaux d’un jeu, tandis que la version Java commence par moduler les derniers tours d’un jeu (en d’autres termes, en regardant la liste des tours comme une liste de base-3 à 11 chiffres). Les nombres, la version Go est un peu endian, alors que la version Java est big-endian, pour ainsi dire), donc la version de 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 j’en suis sûr que je ne ressens pas le besoin.