Comment mettre à l'échelle les threads en fonction des cœurs de processeur?

107

Je souhaite résoudre un problème mathématique avec plusieurs threads en Java. mon problème mathématique peut être séparé en unités de travail, que je souhaite résoudre en plusieurs threads.

Je ne veux pas avoir un nombre fixe de threads qui y travaillent, mais plutôt une quantité de threads correspondant à la quantité de cœurs de processeur. Mon problème est que je n'ai pas trouvé de tutoriel facile sur Internet pour cela. Tout ce que j'ai trouvé, ce sont des exemples avec des fils fixes.

Comment cela peut-il être fait? Pouvez-vous donner des exemples?

Andreas Hornig
la source

Réponses:

120

Vous pouvez déterminer le nombre de processus disponibles sur la machine virtuelle Java à l'aide de la méthode statique Runtime, availableProcessors . Une fois que vous avez déterminé le nombre de processeurs disponibles, créez ce nombre de threads et divisez votre travail en conséquence.

Mise à jour : pour clarifier davantage, un thread n'est qu'un objet en Java, vous pouvez donc le créer comme vous le feriez pour tout autre objet. Donc, disons que vous appelez la méthode ci-dessus et constatez qu'elle renvoie 2 processeurs. Impressionnant. Maintenant, vous pouvez créer une boucle qui génère un nouveau thread, divise le travail pour ce thread et déclenche le thread. Voici quelques psuedocode pour démontrer ce que je veux dire:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

Pour plus d'informations sur la création de votre propre fil de discussion, consultez ce didacticiel . En outre, vous souhaiterez peut-être consulter Thread Pooling pour la création des threads.

JasCav
la source
17
C'est fondamentalement correct, mais faites attention aux performances des processeurs commercialisés avec "l'hyper-threading" d'Intel. Sur un quad-core, cela retournera 8 au lieu de 4, mais vos performances peuvent commencer à chuter après 4 threads - donc mes propres benchmarks me le disent :)
xcut
Salut, d'accord, je ne savais pas que c'était possible. mais lorsque je divise une tâche en plusieurs unités de travail et que j'ai besoin d'une solution partielle pour l'étape de travail finale, comment cela se fait-il? Quand j'ai plusieurs "yourThreads", comment utiliser join () pour cela, parce que je ne vois pas, comment ces différents threads se distinguent-ils? :) BTW: votre lien vers Thread Pooling me conduit à ibm.com/developerworks/library/j-jtp0730.html :)
Andreas Hornig
5
Regardez l'exemple ici: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Cela vous indiquera une façon plus simple de créer et de gérer le pool de threads ... Cela peut sembler plus compliqué au début, mais comme pour la plupart des choses, c'est plus compliqué car si c'était plus simple, vous atteindriez les limitations plus tôt.
Bill K
62

Vous voudrez probablement regarder le framework java.util.concurrent pour ce genre de choses aussi. Quelque chose comme:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

ou

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

C'est beaucoup plus agréable que de gérer vos propres pools de threads, etc.

DaveC
la source
Salut DaveC, hmmm, je ne savais pas ça avant, donc je vais jeter un oeil à ceci. Et il peut être mis à l'échelle en fonction des cœurs de processeur disponibles? Parce que je ne peux pas voir cela dans vos courts exemples. Meilleures salutations, Andreas
Andreas Hornig
3
java.util.concurrent est hautement évolutif
Kristopher Ives
4
Un pool de taille fixe avec le nombre de processeurs disponibles est souvent optimal pour les processus liés au processeur. Le premier exemple ici est tout ce que vous devez faire.
Peter Lawrey
1
Comme indiqué dans le premier commentaire de la réponse acceptée, il serait préférable d'utiliser la moitié du nombre de "processeurs" signalés, pour deux raisons: 1. si vous avez un hyper-threading, le nombre réel de processeurs est la moitié de ce qui est rapporté , et 2. il laisse une certaine puissance de traitement au reste du système pour fonctionner (OS et autres programmes).
Matthieu
10

Option 1:

nouveauWorkStealingPool deExecutors

public static ExecutorService newWorkStealingPool()

Crée un pool de threads volants en utilisant tous les processeurs disponibles comme niveau de parallélisme cible.

Avec cette API, vous n'avez pas besoin de transmettre le nombre de cœurs à ExecutorService.

Implémentation de cette API depuis grepcode

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

Option 2:

API newFixedThreadPool de Executorsou other newXXX constructors, qui renvoieExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

remplacer nThreads par Runtime.getRuntime().availableProcessors()

Option 3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

passer Runtime.getRuntime().availableProcessors()en paramètre à maximumPoolSize.

Ravindra babu
la source
4

La méthode standard est la méthode Runtime.getRuntime (). AvailableProcessors (). Sur la plupart des processeurs standard, vous aurez renvoyé ici le nombre de threads optimal (qui n'est pas le nombre réel de cœurs de processeur). C'est donc ce que vous recherchez.

Exemple:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

N'oubliez PAS d'arrêter le service exécuteur comme ceci (ou votre programme ne se fermera pas):

service.shutdown();

Voici juste un bref aperçu de la configuration d'un futur code MT (hors-sujet, à titre d'illustration):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

Ensuite, vous devez suivre le nombre de résultats attendus et les récupérer comme suit:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}
fl0w
la source
2
appel d'arrêt devrait être mis en essai enfin
Christophe Roussy
1
@ChristopheRoussy vous avez tout à fait raison, j'ai modifié l'extrait en conséquence, merci!
fl0w
3

Sur la classe Runtime, il existe une méthode appelée availableProcessors (). Vous pouvez l'utiliser pour déterminer le nombre de processeurs dont vous disposez. Puisque votre programme est lié au CPU, vous voudrez probablement avoir (au plus) un thread par CPU disponible.

Eric Petroelje
la source
Salut Jason et Eric (j'utilise un commentaire pour vos deux réponses, car c'est fondamentalement la même chose). d'accord, c'est bien de vérifier, mais ce serait la première partie. Quand j'ai le nombre de cœurs, je dois avoir les threads aussi variables que cette quantité de cœurs. J'ai essayé cet exemple avant openbook.galileodesign.de/javainsel5 / ... (allemand!) Et il utilise un fil fixe. Mais je veux avoir la même programmation en utilisant 2 cœurs dans un environnement double cœur et 4 cœurs dans un environnement quadricœur. Je ne veux pas le modifier manuellement. Est-ce possible? THX! :)
Andreas Hornig
@Andreas - Voir les mises à jour que j'ai apportées à mon message. Je pense que cela aidera à clarifier la question.
JasCav