101 votes

Les compilateurs JIT de la machine virtuelle Java génèrent-ils du code utilisant des instructions à virgule flottante vectorisées?

Disons que le goulot d'étranglement de mon programme Java est vraiment une boucles serrées pour calculer un tas de vecteur point des produits. Oui, j'ai profilé, oui, c'est le goulot d'étranglement, oui c'est important, oui c'est juste la façon dont l'algorithme est, oui j'ai couru Proguard pour optimiser le code octet, etc.

Le travail est, essentiellement, point des produits. Comme, j'ai deux float[50] et j'ai besoin de calculer la somme des paires de produits. Je sais d'instruction du processeur ensembles existent pour effectuer ces opérations rapidement et en vrac, à l'instar de l'ESS ou MMX.

Oui, je peux sans doute accéder à écrire du code natif dans la JNI. La JNI appel s'avère être assez cher.

Je sais que vous ne pouvez pas garantir qu'un JIT compiler ou pas compiler. Quelqu'un a déjà entendu parler d'un JIT de générer du code qui utilise ces instructions? et si oui, est-il rien sur le code Java qui permet de faire compilable de cette façon?

Probablement un "non"; la peine de demander.

46voto

Samuel Audet Points 2234

Donc, fondamentalement, vous voulez que votre code s'exécute plus rapidement. JNI est la réponse. Je sais que vous avez dit, il ne fonctionne pas pour vous, mais permettez-moi de vous montrer que vous avez tort.

Voici Dot.java:

import java.nio.FloatBuffer;
import com.googlecode.javacpp.*;
import com.googlecode.javacpp.annotation.*;

@Platform(include="Dot.h", options="fastfpu")
public class Dot {
    static { Loader.load(); }

    static float[] a = new float[50], b = new float[50];
    static float dot() {
        float sum = 0;
        for (int i = 0; i < 50; i++) {
            sum += a[i]*b[i];
        }
        return sum;
    }
    static native @MemberGetter FloatPointer ac();
    static native @MemberGetter FloatPointer bc();
    static native float dotc();

    public static void main(String[] args) {
        FloatBuffer ab = ac().capacity(50).asBuffer();
        FloatBuffer bb = bc().capacity(50).asBuffer();

        for (int i = 0; i < 10000000; i++) {
            a[i%50] = b[i%50] = dot();
            float sum = dotc();
            ab.put(i%50, sum);
            bb.put(i%50, sum);
        }
        long t1 = System.nanoTime();
        for (int i = 0; i < 10000000; i++) {
            a[i%50] = b[i%50] = dot();
        }
        long t2 = System.nanoTime();
        for (int i = 0; i < 10000000; i++) {
            float sum = dotc();
            ab.put(i%50, sum);
            bb.put(i%50, sum);
        }
        long t3 = System.nanoTime();
        System.out.println("dot(): " + (t2 - t1)/10000000 + " ns");
        System.out.println("dotc(): "  + (t3 - t2)/10000000 + " ns");
    }
}

et Dot.h:

float ac[50], bc[50];

inline float dotc() {
    float sum = 0;
    for (int i = 0; i < 50; i++) {
        sum += ac[i]*bc[i];
    }
    return sum;
}

Nous pouvons compiler et l'exécuter avec JavaCPP à l'aide des commandes de la ligne de ceux-ci:

$ javac -cp javacpp.jar Dot.java
$ java -jar javacpp.jar Dot
$ java -cp javacpp.jar:. Dot

Avec la valeur par défaut des trucs à partir de Fedora 15 sur un processeur Intel Core i7 870 @ 2.93 GHz-je obtenir ce genre de résultat:

dot(): 41 ns
dotc(): 26 ns

Ou à peu près 1,5 fois plus rapide. Nous avons besoin d'utiliser direct NIO tampons au lieu de tableaux, mais HotSpot peut accès direct NIO tampons aussi vite que des tableaux.

Mise à jour:

Cependant, si nous dérouler les boucles de 5 fois, les chiffres baissent à la suivante:

dot(): 9 ns
dotc(): 18 ns

Donc, oui, la JNI appel surcharge de ~13 ns fait devient le goulot d'étranglement ici...

42voto

Nitsan Wakart Points 613

Au moins selon ce document , il est possible à l'aide du compilateur JIT pour identifier votre code comme un vecteur de l'opération. Il exige quelque allusion, mais il est au moins possible. "Il est évident que la plupart de l'amélioration vient à la suite de persuader le JIT de reconnaître les lieux où SIMD opérations peuvent être écrites comme des instructions d'accès à l'ESS registres. Par l'activation de ce comportement, le montant de l'arithmétique à virgule flottante par cycle augmente et par conséquent la vitesse à laquelle le calcul du produit augmente"

26voto

Vedran Points 81

Dans les versions HotSpot commençant par Java 7u40, le compilateur de serveurs prend en charge la vectorisation automatique. Selon JDK-6340864

Cependant, cela ne semble être 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

4voto

Mikael Lepistö Points 1040

Vous pouvez écrire le noyau OpenCl pour faire l’informatique et l’exécuter à partir de java http://www.jocl.org/ .

Le code peut être exécuté sur le processeur et / ou le processeur graphique et le langage OpenCL prend également en charge les types de vecteurs. Vous devez donc pouvoir tirer un avantage explicite des instructions SSE3 / 4, par exemple.

-6voto

mP. Points 7516

Je ne crois pas que la plupart des machines virtuelles soient suffisamment intelligentes pour réaliser ce type d'optimisation. Pour être juste, la plupart des optimisations sont beaucoup plus simples, telles que le décalage au lieu de la multiplication par 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X