Java 8 du flux: pourquoi parallèle flux est plus lent?
Je suis à jouer avec Java 8 de cours d'eau et ne peut pas comprendre les résultats que j'obtiens. J'ai 2 core CPU (Intel i73520M), Windows 8 64 bits et 64 bits de Java 8 update 5. Je suis en train de faire simple carte au-dessus de flux/courant parallèle de Chaînes et a trouvé que la version parallèle est un peu plus lent.
Function<Stream<String>, Long> timeOperation = (Stream<String> stream) -> {
long time1 = System.nanoTime();
final List<String> list =
stream
.map(String::toLowerCase)
.collect(Collectors.toList());
long time2 = System.nanoTime();
return time2 - time1;
};
Consumer<Stream<String>> printTime = stream ->
System.out.println(timeOperation.apply(stream) / 1000000f);
String[] array = new String[1000000];
Arrays.fill(array, "AbabagalamagA");
printTime.accept(Arrays.stream(array)); //prints around 600
printTime.accept(Arrays.stream(array).parallel()); //prints around 900
Ne devrait pas la version parallèle être plus rapide, compte tenu du fait que j'ai 2 cœurs de PROCESSEUR?
Quelqu'un pourrait-il me donner un indice pourquoi version parallèle est plus lent?
Vous devez vous connecter pour publier un commentaire.
Il y a plusieurs questions se passe ici en parallèle, comme elle l'avait fait.
La première est que la résolution d'un problème en parallèle implique toujours d'effectuer plus de travail que de le faire de façon séquentielle. Frais généraux sont impliqués de diviser le travail entre plusieurs threads et de l'adhésion ou de fusionner les résultats. Des problèmes comme la conversion de chaînes courtes afin de diminuer les cas sont assez petits qu'ils sont en danger d'être submergé par le parallèle fractionnement des frais généraux.
Le deuxième problème est que l'analyse comparative programme Java est très subtil, et il est très facile d'obtenir des résultats étranges. Deux questions sont à la compilation JIT et l'élimination du code mort. Court de repères souvent terminer avant ou au cours de compilation JIT, ils ne sont donc pas de mesure de débit de pointe, et en effet, ils pourraient être en mesure de l'équipe elle-même. Lorsque la compilation se produit est quelque peu non-déterministe, donc il peut provoquer des résultats varient énormément en tant que bien.
Pour les petits, synthétique repères, la charge de travail souvent calcule des résultats qui sont jetés. Les compilateurs JIT sont assez bien à la détection et l'élimination de code qui ne produisent pas de résultats, qui sont utilisés n'importe où. Ce n'est probablement pas qui se passe dans cette affaire, mais si vous bricoler avec d'autres synthétique de travail, il peut certainement se produire. Bien sûr, si le JIT élimine l'indice de référence de la charge de travail, il rend le test inutile.
Je recommande fortement d'utiliser un référentiel comme JMH au lieu de la main, le laminage à un de vos propres. JMH a des équipements pour aider à éviter les courants d'analyse comparative des pièges, y compris les présentes, et il est assez facile de configurer et d'exécuter. Voici votre référence converti pour utiliser JMH:
J'ai couru ce à l'aide de la commande:
(Les options indiquent cinq warmup itérations, cinq référence itérations, et une fourche JVM.) Au cours de son exécution, JMH émet beaucoup de messages détaillés, que j'ai ramené. Le résumé des résultats sont comme suit.
De constater que les résultats sont dans l'ops par seconde, donc il semble que le parallèle était environ trois fois plus rapide que l'ordre d'exécution. Mais ma machine n'a que deux cœurs. Hmmm. Et l'erreur moyenne par run est en fait plus grande que la moyenne d'exécution! WAT? Quelque chose de louche se passe ici.
Cela nous amène à un troisième point. En regardant de plus près la charge de travail, nous pouvons voir qu'il alloue un nouvel objet String pour chaque entrée, et il recueille également les résultats dans une liste, ce qui implique beaucoup de réaffectation et de la copie. Je suppose que cela va résulter en une bonne quantité de la collecte des ordures. Nous pouvons voir cela en exécutant de nouveau le test de la cg messages activé:
Cela donne des résultats tels que:
Remarque: les lignes commençant par
#
sont normales JMH lignes de sortie. Tout le reste sont des GC messages. C'est le premier des cinq de chauffe itérations, qui précède de cinq référence itérations. Le GC messages a continué dans la même veine pendant le reste de la itérations. Je pense qu'il est sûr de dire que les performances mesurées est dominé par GC frais généraux et que les résultats présentés ne doivent pas être cru.À ce stade, il est difficile de savoir quoi faire. Ceci est purement synthétique de la charge de travail. Il implique clairement que très peu de CPU de temps à faire le travail réel par rapport à la répartition et de la copie. Il est difficile de dire ce que vous êtes vraiment essayer de mesurer ici. Une approche serait de trouver un travail qui est dans un certain sens, plus "réel". Une autre approche serait de changer le tas et GC paramètres pour éviter GC lors de l'indice de référence exécuter.
Quand vous faites des repères, vous devriez prêter attention à la compilation JIT, et que la durée comportements peuvent changer, en fonction du montant de JIT code compilé les chemins d'accès. Si j'ajoute une phase de préchauffage de votre programme de test, la version parallèle est un peu plus rapide que la version séquentielle. Voici les résultats:
Le fragment de code suivant contient le code source complet que j'ai utilisé pour ce test.
Utilisation de plusieurs threads pour traiter vos données de configuration initiale des coûts, par exemple, l'initialisation du pool de threads. Ces coûts peuvent l'emporter sur les gains provenant de l'utilisation de ces threads, en particulier si le moteur d'exécution est déjà très faible. En outre, si il y a un conflit, par exemple, d'autres threads en cours d'exécution, processus d'arrière-plan, etc., les performances de traitement parallèle peut encore diminuer.
Ce problème n'est pas nouveau pour le traitement en parallèle. Cet article donne quelques détails dans la lumière de Java 8
parallel()
et un peu plus de choses à prendre en compte: http://java.dzone.com/articles/think-twice-using-java-8