Disons que le goulot d'étranglement de mon programme Java est vraiment quelques boucles serrées pour calculer un tas de produits scalaires vectoriels. Oui j'ai profilé, oui c'est le goulot d'étranglement, oui c'est significatif, oui c'est juste comme ça que l'algorithme est, oui j'ai exécuté Proguard pour optimiser le byte code, etc.
Le travail consiste essentiellement en des produits scalaires. Comme dans, j'en ai deux float[50]
et je dois calculer la somme des produits par paire. Je sais que des jeux d'instructions de processeur existent pour effectuer ce genre d'opérations rapidement et en masse, comme SSE ou MMX.
Oui, je peux probablement y accéder en écrivant du code natif dans JNI. L'appel JNI s'avère assez cher.
Je sais que vous ne pouvez pas garantir ce qu'un JIT compilera ou non. Quelqu'un a-t-il déjà entendu parler d'un code de génération JIT utilisant ces instructions? et si oui, y a-t-il quelque chose dans le code Java qui aide à le rendre compilable de cette façon?
Probablement un "non"; vaut la peine de demander.
la source
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation
. Vous aurez besoin d'un programme qui exécute la méthode vectorisable suffisamment de fois pour la rendre "chaude".Réponses:
Donc, fondamentalement, vous voulez que votre code s'exécute plus rapidement. JNI est la réponse. Je sais que vous avez dit que cela n'a pas fonctionné pour vous, mais laissez-moi vous montrer que vous vous trompez.
Voici
Dot.java
:et
Dot.h
:Nous pouvons compiler et exécuter cela avec JavaCPP en utilisant cette commande:
Avec un processeur Intel (R) Core (TM) i7-7700HQ à 2,80 GHz, Fedora 30, GCC 9.1.1 et OpenJDK 8 ou 11, j'obtiens ce type de sortie:
Ou environ 2,4 fois plus rapide. Nous devons utiliser des tampons NIO directs au lieu de baies, mais HotSpot peut accéder aux tampons NIO directs aussi rapidement que des baies . En revanche, le déroulement manuel de la boucle n'apporte pas une amélioration mesurable des performances, dans ce cas.
la source
Pour répondre à une partie du scepticisme exprimé par d'autres ici, je suggère à quiconque souhaite se prouver à lui-même ou à d'autres d'utiliser la méthode suivante:
Exemple:
Le résultat avec et sans le drapeau (sur un ordinateur portable récent Haswell, Oracle JDK 8u60): -XX: + UseSuperWord: 475,073 ± 44,579 ns / op (nanosecondes par opération) -XX: -UseSuperWord: 3376,364 ± 233,211 ns / op
L'assemblage pour la boucle chaude est un peu difficile à formater et à coller ici, mais voici un extrait (hsdis.so ne parvient pas à formater certaines des instructions vectorielles AVX2, j'ai donc exécuté avec -XX: UseAVX = 1): -XX: + UseSuperWord (avec '-prof perfasm: intelSyntax = true')
Amusez-vous à prendre d'assaut le château!
la source
Dans les versions HotSpot commençant par Java 7u40, le compilateur de serveur prend en charge la vectorisation automatique. Selon JDK-6340864
Cependant, cela ne semble vrai que pour les «boucles simples» - du moins pour le moment. Par exemple, l'accumulation d'un tableau ne peut pas encore être vectorisée JDK-7192383
la source
Voici un bel article sur l'expérimentation des instructions Java et SIMD écrites par mon ami: http://prestodb.rocks/code/simd/
Son résultat général est que vous pouvez vous attendre à ce que JIT utilise certaines opérations SSE en 1.8 (et d'autres en 1.9). Bien que vous ne devriez pas vous attendre à grand-chose et que vous devez être prudent.
la source
Vous pouvez écrire le noyau OpenCl pour faire le calcul et l'exécuter à partir de java http://www.jocl.org/ .
Le code peut être exécuté sur le CPU et / ou le GPU et le langage OpenCL prend également en charge les types vectoriels, vous devriez donc être en mesure de tirer explicitement parti des instructions SSE3 / 4, par exemple.
la source
Jetez un œil à la comparaison des performances entre Java et JNI pour une implémentation optimale des micro-noyaux de calcul . Ils montrent que le compilateur de serveur Java HotSpot VM prend en charge la vectorisation automatique à l'aide du parallélisme de niveau super-mot, qui est limité à de simples cas de parallélisme à l'intérieur de la boucle. Cet article vous indiquera également si la taille de vos données est suffisamment grande pour justifier une route JNI.
la source
Je suppose que vous avez écrit cette question avant de découvrir netlib-java ;-) il fournit exactement l'API native dont vous avez besoin, avec des implémentations optimisées pour la machine, et n'a aucun coût à la limite native grâce à l'épinglage de la mémoire.
la source
Je ne pense pas que la plupart des machines virtuelles soient suffisamment intelligentes pour ce type d'optimisation. Pour être juste, la plupart des optimisations sont beaucoup plus simples, comme le décalage au lieu de la multiplication quand une puissance de deux. Le projet mono a introduit son propre vecteur et d'autres méthodes avec des supports natifs pour améliorer les performances.
la source