27 votes

Coût du verrouillage en .NET vs Java

Je jouais avec les Perturbateurs cadre et de son port .NET plate-forme et a trouvé un cas intéressant. Peut-être que je passe à côté de quelque chose, donc je suis à la recherche de l'aide du tout-puissant de la Communauté.

        long iterations = 500*1000*1000;
        long testValue = 1;

        //.NET 4.0. Release build. Mean time - 26 secs;
        object lockObject = new object();
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            lock (lockObject)
            {
                testValue++;    
            }
        }
        sw.Stop();

        //Java 6.25. Default JVM params. Mean time - 17 secs.
        Object lock = new Object();
        long start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++)
        {
                synchronized (lock)
                {
                    testValue++;
                }
        }
        long stop = System.currentTimeMillis();

Il semble que l'acquisition du verrou dans le scénario avec un signle fil dans .Les coûts NETS de seulement 50% de plus qu'en Java. J'ai d'abord été suspect à des minuteries, mais j'ai couru le même test pour quelques temps avec des résultats autour mentionné ci-dessus des valeurs moyennes. Ensuite, j'ai été suspect à synchronisée bloc de code, mais il n'est rien de plus qu' monitorenter / monitorexit octet code d'instructions - la même chose que le verrouillage de mot-clé dans .NET. Toutes les autres idées pourquoi prendre un verrou est si cher dans l' .NET vs Java?

19voto

Jon Skeet Points 692016

Oui, c'est comme prendre un uncontended de verrouillage est plus cher .NET que dans Java. (Les résultats sur mon netbook sont légèrement plus dramatique encore.)

Il y a différents aspects de la performance qui sera plus rapide sur une plate-forme de l'autre, parfois à cette mesure. Le HotSpot JIT et la .NET JIT sont assez radicalement différentes, de diverses façons, en particulier parce que l' .NET JIT ne fonctionne une fois sur l'IL, alors que le HotSpot est en mesure d'optimiser de plus en plus comme un morceau de code est exécuté de plus en plus souvent.

La question importante est de savoir si c'est vraiment important. Si votre vie réelle application passe vraiment acquiert un uncontented de verrouillage de 500 millions de fois chaque minute, il probablement est importante et vous devriez probablement revoir la conception de votre application un peu. Si votre vie réelle demande en fait un réel travail à l'intérieur de la serrure (ou entre les acquisitions de la serrure), alors il est peu probable d'être un véritable goulot d'étranglement.

J'ai récemment trouvé deux .NET pièges (première partie; partie deux) que je vais avoir à travailler en rond que je suis en train d'écrire un "système de niveau de la bibliothèque" et ils ont fait une grande différence quand une application a un lot de date/heure de l'analyse - mais ce genre de micro-optimisation est rarement la peine de le faire.

8voto

Peter Lawrey Points 229686

La première chose à retenir à propos de micro-benchmarks, c'est que Java est particulièrement bon à l'identification et à l'élimination code qui permet de ne pas faire n'importe quoi. J'ai trouvé que, encore et encore, Java n'inutile du code plus rapide que n'importe quelle autre langue. ;)

Si Java est surprenant rapide par rapport à une autre langue à la première question; le code de tout faire à distance utile? (ou même de le regarder comme s'il pouvait être utile)

Java a tendance à boucle dérouler plus que d'habitude. Il peut également combiner les verrous. Que votre test est incontestable et ne rien faire de votre code, c'est comme à ressembler à quelque chose comme.

for (int i = 0; i < iterations; i+=8) {
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
    synchronized (lock) {
        testValue++;
    }
}

qui devient

for (int i = 0; i < iterations; i+=8) {
    synchronized (lock) {
        testValue++;
        testValue++;
        testValue++;
        testValue++;
        testValue++;
        testValue++;
        testValue++;
        testValue++;
    }
}

depuis testValue n'est pas utilisé.

for (int i = 0; i < iterations; i+=8) {
    synchronized (lock) {
    }
}

et enfin

{ }

3voto

Steve McLeod Points 19016

Est la variable "testValue' locale à une méthode? Si oui, il est possible que le JRE a détecté que le verrouillage n'est pas nécessaire puisque la variable est locale à un fil et n'est donc pas du tout le verrouillage.

C'est expliqué ici.

Pour montrer combien il est difficile de dire ce que l'optimisation de la JVM décide de le faire - et quand il décide de le faire - à l'examen de ces résultats de l'exécution de votre code trois fois de suite:

public static void main(String[] args) {
  System.out.println("Java version: " + System.getProperty("java.version"));
  System.out.println("First call : " + doIt(500 * 1000 * 1000, 1)); // 14 secs
  System.out.println("Second call: " + doIt(500 * 1000 * 1000, 1)); // 1 sec
  System.out.println("Third call : " + doIt(500 * 1000 * 1000, 1)); // 0.4 secs
}

private static String doIt(final long iterations, long testValue) {
    Object lock = new Object();
    long start = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
        synchronized (lock) {
            testValue++;
        }
    }
    long stop = System.currentTimeMillis();
    return (stop - start) + " ms, result = " + testValue;
}

Ces résultats sont donc difficiles à expliquer, je pense que seule une JVM ingénieur pourrait aider à faire la lumière.

1voto

irreputable Points 25577

Rappelez-vous, les deux sont extrêmement rapide, nous parlons de 50 cycles CPU pour lock-lire-écrire-déverrouiller ici.

En Java, je l'ai comparé avec une simulation de mettre en uncontended cas

volatile int waitingList=0;

    AtomicInteger x = new AtomicInteger(0);
    for (int i = 0; i < iterations; i++)
    {
        while( ! x.compareAndSet(0, 1) )
            ;

        testValue++;

        if(waitingList!=0)
            ;
        x.set(0);
    }

Ce nu de l'os de la simulation est un peu plus rapide que l' synchronized version, temps de prise est 15/17.

Qui montre que dans votre cas de test, Java n'ont pas fou optimisations, honnêtement fait verrouiller en lecture-mise à jour-débloquer pour chaque itération. Cependant, Java impl est aussi rapide que le bare bone impl; il ne peut pas être plus rapide.

Bien que le C#'s impl est également à proximité du moins, il ne semble un ou deux choses plus que Java. Je ne suis pas familier avec le C#, mais c'est probablement ce qui indique qu'une partie de la sémantique de la différence, de sorte que C# a faire quelque chose.

0voto

user1469065 Points 1

Quand j'ai étudié de verrouillage de synchronisation/frais il y a quelques années en Java j'ai fini avec une grande question comment verrouillage touchées sur-performance également pour les autres threads d'accéder à tout type de mémoire. Ce qui peut être touché est le cache du PROCESSEUR, surtout sur un multi-processeur de l'ordinateur et dépend de la façon dont le PROCESSEUR de l'architecture poignées de synchronisation de la mémoire cache. Je crois que la performance n'est pas affectée sur une unique moderne de l'architecture CPU, mais je n'en suis pas sûr.

De toute façon, dans le doute, surtout quand multi-processus d'ordinateurs peuvent être utilisés pour héberger le logiciel, il peut être la peine de mettre un verrou sur un niveau supérieur au cours de plusieurs opérations.

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