Comment puis-je obtenir un parallélisme maximal et utiliser un maximum de CPU avec Parallel.ForEach?

Il existe une fonction C # A(arg1, arg2) qui doit être appelée souvent. Pour ce faire le plus rapide, j’utilise la programmation parallèle.

Prenons l’exemple du code suivant:

 long totalCalls = 2000000; int threads = Environment.ProcessorCount; ParallelOptions options = new ParallelOptions(); options.MaxDegreeOfParallelism = threads; Parallel.ForEach(Enumerable.Range(1, threads), options, range => { for (int i = 0; i < total / threads; i++) { // init arg1 and arg2 var value = A(arg1, agr2); // do something with value } }); 

Maintenant, le problème est que cela n’augmente pas avec l’augmentation du nombre de cœurs; Par exemple, sur 8 cœurs, il utilise 80% de la CPU et sur 16 cœurs, il utilise 40 à 50% de la CPU. Je veux utiliser le processeur au maximum.

Vous pouvez supposer que A(arg1, arg2) contient en interne un calcul complexe, mais il n’a pas d’opération IO ou liée au réseau, et il n’y a pas non plus de locking de thread. Quelles sont les autres possibilités de savoir quelle partie du code empêche le lecteur de fonctionner en parallèle?

J’ai aussi essayé d’augmenter le degré de parallélisme, par exemple

 int threads = Environment.ProcessorCount * 2; // AND int threads = Environment.ProcessorCount * 4; // etc. 

Mais ce n’était d’aucune aide.

Mise à jour 1 – Si je lance le même code en remplaçant A() par une simple fonction qui consiste à calculer le nombre premier, il utilise 100 CPU et s’intègre bien. Donc, cela prouve que l’autre morceau de code est correct. Maintenant, issue pourrait être dans la fonction originale A() . J’ai besoin d’un moyen de détecter ce problème qui provoque une sorte de séquençage.

Vous avez déterminé que le code en A est le problème.

Il y a un problème très commun: le ramassage des ordures. Configurez votre application dans app.config pour utiliser le serveur de GC simultané. Le poste de travail GC a tendance à sérialiser l’exécution. L’effet est grave.

Si ce n’est pas le problème, mettez le débogueur en pause à quelques resockets et regardez la fenêtre Debug -> Parallel Stacks . Là, vous pouvez voir ce que font vos discussions. Rechercher des ressources et des conflits communs. Par exemple, si vous trouvez plusieurs threads en attente d’un verrou, c’est votre problème.

Une autre technique de débogage intéressante consiste à commenter le code. Une fois que la limite d’évolutivité a disparu, vous savez quel code l’a provoquée.