Question

Quel est le volume lu de la ThreadLocal variable plus lente que du champ normal?

Plus concrètement, la création d'objet simple est-elle plus rapide ou plus lente que l'accès à la ThreadLocal<MessageDigest> variable?

Je suppose que c'est assez rapide pour que l'instance MessageDigest soit beaucoup plus rapide que la création d'une instance de <=> à chaque fois. Mais est-ce que cela s'applique aussi à byte [10] ou byte [1000] par exemple?

Modifier: La question est de savoir ce qui se passe réellement lorsque vous appelez <=>? S'il ne s'agit que d'un champ, comme d'un autre, la réponse serait & "C'est toujours le plus rapide &"; Non?

Était-ce utile?

La solution

L'exécution de tests de performance non publiés, ThreadLocal.get nécessite environ 35 cycles par itération sur ma machine. Pas beaucoup. Dans l'implémentation de Sun, une carte de hachage de sondage linéaire personnalisée entre Thread mappe ThreadLocal s à des valeurs. Comme il n’est accessible que par un seul thread, cela peut être très rapide.

L'attribution de petits objets prend un nombre de cycles similaire. Toutefois, en raison de l'épuisement du cache, il est possible que les chiffres soient légèrement inférieurs dans une boucle serrée.

La construction de MessageDigest sera probablement relativement coûteuse. Il a pas mal d'état et la construction passe par le mécanisme Provider SPI. Vous pourrez peut-être optimiser, par exemple, en clonant ou en fournissant le <=>.

Ce n’est peut-être pas simplement parce que la mise en cache de <=> la création est plus rapide que la création que cela signifie que les performances du système vont augmenter. Vous aurez des frais généraux supplémentaires liés à GC qui ralentissent tout.

À moins que votre application utilise très fréquemment <=>, vous pouvez envisager d'utiliser plutôt un cache thread-safe conventionnel.

Autres conseils

En 2009, certaines machines virtuelles Java ont implémenté ThreadLocal à l'aide d'un HashMap non synchronisé dans l'objet Thread.currentThread (). Cela le rendait extrêmement rapide (bien que pas aussi rapide que d'utiliser un accès de champ régulier, bien sûr), ainsi que de s'assurer que l'objet ThreadLocal était bien rangé à la mort du Thread. En mettant à jour cette réponse en 2016, il semble que la plupart (toutes?) Des nouvelles machines virtuelles utilisent une ThreadLocalMap avec une analyse linéaire. Je ne suis pas sûr de la performance de ces & # 8211; mais je ne peux pas imaginer que cela est bien pire que la mise en œuvre antérieure.

Bien entendu, nouvel objet () est également très rapide de nos jours, et les récupérateurs d'ordures sont également très efficaces pour récupérer des objets éphémères.

Sauf si vous êtes certain que la création d'objet coûtera cher ou si vous devez conserver certains états thread par thread, il vaut mieux opter pour la solution d'allocation simple lorsque nécessaire, et pour basculer uniquement vers un ThreadLocal. mise en œuvre lorsqu'un profileur vous dit que vous en avez besoin.

Bonne question, je me suis posé la question récemment. Pour vous donner des chiffres précis, utilisez les repères ci-dessous (dans Scala, compilés pour créer pratiquement les mêmes octets que le code Java équivalent):

var cnt: String = ""
val tlocal = new java.lang.ThreadLocal[String] {
  override def initialValue = ""
}

def loop_heap_write = {                                                                                                                           
  var i = 0                                                                                                                                       
  val until = totalwork / threadnum                                                                                                               
  while (i < until) {                                                                                                                             
    if (cnt ne "") cnt = "!"                                                                                                                      
    i += 1                                                                                                                                        
  }                                                                                                                                               
  cnt                                                                                                                                          
} 

def threadlocal = {
  var i = 0
  val until = totalwork / threadnum
  while (i < until) {
    if (tlocal.get eq null) i = until + i + 1
    i += 1
  }
  if (i > until) println("thread local value was null " + i)
}

disponible ici , ont été réalisés sur un processeur double cœur AMD 4x de 2,8 GHz et un processeur i7 à quatre coeurs avec hyperthreading (2,67 GHz).

Ce sont les chiffres:

i7

Spécifications: Intel i7 2x quad-core @ 2,67 GHz Testez: scala.threads.TarallelTests

Nom du test: loop_heap_read

Nombre de fils: 1 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  9.0069 9.0036 9.0017 9.0084 9.0074 (moy = 9.1034 min = 8.9986 max = 21.0306)

Nombre de fils: 2 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  4,5563 4,7128 4,5663 4,5617 4,5724 (moyenne = 4,6337 min = 4,5509 max = 13,9476)

Nombre de fils: 4 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  2.3946 2.3979 2.3934 2.3937 2.3964 (avg = 2.5113 min = 2.3884 max = 13.5496)

Nombre de fils: 8 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  2,44479 2,4362 2,4323 2,44472 2,4383 (avg = 2,5562 min = 2,4166 max = 10,3726)

Nom du test: threadlocal

Nombre de fils: 1 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  91.1741 90.8978 90.6181 90.6200 90.6113 (moy = 91.0291 min = 90.6000 max = 129.7501)

Nombre de fils: 2 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  45,3838 45,3858 45,6676 45,3772 45,3839 (moyenne = 46,0555 min = 45,3726 max = 90,7108)

Nombre de fils: 4 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  22.8118 22.8135 59.1753 22.8229 22.8172 (avg = 23.9752 min = 22.7951 max = 59.1753)

Nombre de fils: 8 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  22.2965 22.2415 22.3438 22.3109 22.4460 (avg = 23.2676 min = 22.2346 max = 50.3583)

DMLA

Spécifications: AMD 8220 4x double cœur à 2,8 GHz Testez: scala.threads.TarallelTests

Nom du test: loop_heap_read

Travail total: 20000000 Nombre de fils: 1 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  12,625 12,631 12,634 12,632 12,628 (avg = 12,7333 min = 12,619 max = 26,698)

Nom du test: loop_heap_read Travail total: 20000000

Temps d'exécution: (montrant les 5 derniers)  6.412 6.424 6.408 6.397 6.43 (moy = 6,5367 min = 6,393 max = 19,716)

Nombre de fils: 4 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  3.385 4.298 9.7 6.535 3.385 (moyenne = 5.6079 min = 3.354 max = 21.603)

Nombre de fils: 8 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  5,389 5,795 10,818 3,823 3,824 (moyenne = 5,5810 min = 2,405 max = 19,755)

Nom du test: threadlocal

Nombre de fils: 1 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  200.217 207.335 200.241 207.342 200.23 (moy = 202.2424 min = 200.184 max = 245.369)

Nombre de fils: 2 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  100.208 100.199 100.211 103.781 100.215 (moy = 102.2238 min = 100.192 max = 129.505)

Nombre de fils: 4 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  62.101 67.629 62.087 52.021 55.766(avg = 65,6361 min = 50,282 max = 167,433)

Nombre de fils: 8 Total des tests: 200

Temps d'exécution: (montrant les 5 derniers)  40.672 74.301 34.434 41.549 28.119 (avg = 54.7701 min = 28.119 max = 94.424)

Résumé

Un thread local correspond à environ 10 à 20 fois celui de la lecture du tas. Il semble également bien s’adapter à cette implémentation JVM et à ces architectures avec le nombre de processeurs.

Voilà un autre test. Les résultats montrent que ThreadLocal est un peu plus lent qu'un champ normal, mais dans le même ordre. Environ 12% plus lent

public class Test {
private static final int N = 100000000;
private static int fieldExecTime = 0;
private static int threadLocalExecTime = 0;

public static void main(String[] args) throws InterruptedException {
    int execs = 10;
    for (int i = 0; i < execs; i++) {
        new FieldExample().run(i);
        new ThreadLocaldExample().run(i);
    }
    System.out.println("Field avg:"+(fieldExecTime / execs));
    System.out.println("ThreadLocal avg:"+(threadLocalExecTime / execs));
}

private static class FieldExample {
    private Map<String,String> map = new HashMap<String, String>();

    public void run(int z) {
        System.out.println(z+"-Running  field sample");
        long start = System.currentTimeMillis();
        for (int i = 0; i < N; i++){
            String s = Integer.toString(i);
            map.put(s,"a");
            map.remove(s);
        }
        long end = System.currentTimeMillis();
        long t = (end - start);
        fieldExecTime += t;
        System.out.println(z+"-End field sample:"+t);
    }
}

private static class ThreadLocaldExample{
    private ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<Map<String,String>>() {
        @Override protected Map<String, String> initialValue() {
            return new HashMap<String, String>();
        }
    };

    public void run(int z) {
        System.out.println(z+"-Running thread local sample");
        long start = System.currentTimeMillis();
        for (int i = 0; i < N; i++){
            String s = Integer.toString(i);
            myThreadLocal.get().put(s, "a");
            myThreadLocal.get().remove(s);
        }
        long end = System.currentTimeMillis();
        long t = (end - start);
        threadLocalExecTime += t;
        System.out.println(z+"-End thread local sample:"+t);
    }
}
}'

Sortie:

0-Échantillon de terrain en cours d'exécution

Échantillon de champ 0-End: 6044

Exemple local de thread en cours d'exécution

Échantillon local de thread 0-End: 6015

1-Échantillon de terrain en cours

Échantillon de champ 1-End: 5095

Échantillon local d'un thread en cours d'exécution

Échantillon local de thread 1-End: 5720

Échantillon de terrain en cours d'exécution

Échantillon de champ 2-End: 4842

Exemple local de thread en cours d'exécution

Échantillon local de thread 2-End: 5835

Échantillon de terrain en cours d'exécution

Échantillon de champ à 3 extrémités: 4674

Exemple local de thread en cours d'exécution

Échantillon local d'unité d'exécution à 3 extrémités: 5287

Échantillon de terrain en cours d'exécution

Échantillon de champ 4-End: 4849

Exemple local de thread en cours d'exécution

Échantillon local d'unités d'exécution à 4 extrémités: 5309

Échantillon de terrain en cours d'exécution

Échantillon de champ 5-End: 4781

Exemple local de thread en cours d'exécution

Échantillon local d'unités d'exécution à 5 extrémités: 5330

Échantillon de terrain en cours d'exécution

Échantillon de champ 6-End: 5294

Exemple local de thread en cours d'exécution

Échantillon local d'unités d'exécution à 6 extrémités: 5511

7-Échantillon de terrain en cours d'exécution

Échantillon de champ 7-End: 5119

Exemple local de thread en cours d'exécution

Échantillon local d'unité d'exécution 7-End: 5793

Échantillon de terrain en cours d'exécution

Échantillon de champ 8-End: 4977

Exemple local de thread en cours d'exécution

Échantillon local d'unités d'exécution à 8 extrémités: 6374

Échantillon de terrain en cours d'exécution 9

Échantillon de champ 9-End: 4841

Échantillon local de thread en cours d'exécution 9

Échantillon local d'unité d'exécution 9-End: 5471

terrain moyen: 5051

ThreadLocal moy: 5664

Env:

openjdk version " 1.8.0_131 "

Intel & # 174; Noyau & # 8482; CPU i7-7500U à 2,70 GHz & # 215; 4

Ubuntu 16.04 LTS

@Pete est le test correct avant l'optimisation.

Je serais très surpris que la construction d'un MessageDigest ait un coût important en termes de frais généraux par rapport à son utilisation réelle.

Manquer en utilisant ThreadLocal peut être une source de fuites et de références pendantes, dont le cycle de vie n’est pas clair, en général, je n’utilise jamais ThreadLocal sans un plan très clair sur le moment où une ressource particulière sera supprimée.

Construisez-le et mesurez-le.

De plus, vous n'avez besoin que d'un seul threadlocal si vous encapsulez le comportement de digestion du message dans un objet. Si vous avez besoin d'un MessageDigest local et d'un octet local [1000] à certaines fins, créez un objet avec un champ messageDigest et un champ d'octet [] et placez cet objet dans le ThreadLocal plutôt que les deux individuellement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top