Je travaille sur une application Java pour résoudre une classe de problèmes d'optimisation numérique - des problèmes de programmation linéaire à grande échelle pour être plus précis. Un seul problème peut être divisé en sous-problèmes plus petits qui peuvent être résolus en parallèle. Comme il y a plus de sous-problèmes que de cœurs de processeur, j'utilise un ExecutorService et je définis chaque sous-problème comme un Callable qui est soumis à l'ExecutorService. La résolution d'un sous-problème nécessite d'appeler une bibliothèque native - un solveur de programmation linéaire dans ce cas.
Problème
Je peux exécuter l'application sur Unix et sur les systèmes Windows avec jusqu'à 44 cœurs physiques et jusqu'à 256 g de mémoire, mais les temps de calcul sur Windows sont d'un ordre de grandeur plus élevés que sur Linux pour les gros problèmes. Windows nécessite non seulement beaucoup plus de mémoire, mais l'utilisation du processeur au fil du temps passe de 25% au début à 5% après quelques heures. Voici une capture d'écran du gestionnaire de tâches sous Windows:
Observations
Ce que j'ai essayé
Questions
Pour Windows, le nombre de threads par processus est limité par l'espace d'adressage du processus (voir aussi Mark Russinovich - Repousser les limites de Windows: processus et threads ). Pensez que cela provoque des effets secondaires quand il se rapproche des limites (ralentissement des changements de contexte, fragmentation ...). Pour Windows, j'essayerais de diviser la charge de travail en un ensemble de processus. Pour un problème similaire que j'avais il y a des années, j'ai implémenté une bibliothèque Java pour le faire plus facilement (Java 8), jetez un œil si vous le souhaitez: Bibliothèque pour générer des tâches dans un processus externe .
On dirait que Windows met en cache de la mémoire dans le fichier d'échange, après qu'il n'a pas été modifié pendant un certain temps, et c'est pourquoi le processeur est goulot d'étranglement par la vitesse du disque
Vous pouvez le vérifier avec Process Explorer et vérifier la quantité de mémoire mise en cache
Je pense que cette différence de performance est due à la façon dont l'O.S. gère les threads. JVM cache toute différence de système d'exploitation. Il existe de nombreux sites où vous pouvez en lire plus, comme this , par exemple. Mais cela ne signifie pas que la différence disparaît.
Je suppose que vous exécutez sur Java 8+ JVM. Pour cette raison, je vous suggère d'essayer d'utiliser des fonctionnalités de programmation en flux et fonctionnelles. La programmation fonctionnelle est très utile lorsque vous avez de nombreux petits problèmes indépendants et vous souhaitez passer facilement de l'exécution séquentielle à l'exécution parallèle. La bonne nouvelle est que vous n'avez pas à définir de stratégie pour déterminer le nombre de threads que vous devez gérer (comme avec ExecutorService). Par exemple (tiré de - ici ):
package com.mkyong.Java8;
import Java.util.ArrayList;
import Java.util.List;
import Java.util.stream.IntStream;
import Java.util.stream.Stream;
public class ParallelExample4 {
public static void main(String[] args) {
long count = Stream.iterate(0, n -> n + 1)
.limit(1_000_000)
//.parallel() with this 23s, without this 1m 10s
.filter(ParallelExample4::isPrime)
.peek(x -> System.out.format("%s\t", x))
.count();
System.out.println("\nTotal: " + count);
}
public static boolean isPrime(int number) {
if (number <= 1) return false;
return !IntStream.rangeClosed(2, number / 2).anyMatch(i -> number % i == 0);
}
}
Résultat:
Pour les flux normaux, cela prend 1 minute 10 secondes. Pour les flux parallèles, cela prend 23 secondes. P.S Testé avec i7-7700, 16G RAM, WIndows 10
Donc, je vous suggère de lire sur la programmation des fonctions, stream, lambda function en Java et essayez d'implémenter un petit nombre de tests avec votre code (adapté pour fonctionner dans ce nouveau contexte).
Souhaitez-vous s'il vous plaît publier les statistiques du système? Le gestionnaire de tâches est assez bon pour fournir des indices si c'est le seul outil disponible. Il peut facilement dire si vos tâches attendent IO - ce qui semble être le coupable en fonction de ce que vous avez décrit. Cela peut être dû à un certain problème de gestion de la mémoire, ou la bibliothèque peut écrire des données temporaires sur le disque, etc.
Lorsque vous dites 25% d'utilisation du processeur, voulez-vous dire que seuls quelques cœurs sont occupés à travailler en même temps? (Il se peut que tous les cœurs fonctionnent de temps en temps, mais pas simultanément.) Vérifiez-vous combien de threads (ou processus) sont réellement créés dans le système? Le nombre est-il toujours supérieur au nombre de cœurs?
S'il y a suffisamment de threads, nombre d'entre eux attendent-ils quelque chose? Si vrai, vous pouvez essayer d'interrompre (ou joindre un débogueur) pour voir ce qu'ils attendent.