Wie skaliere ich Threads nach CPU-Kernen?

107

Ich möchte ein mathematisches Problem mit mehreren Threads in Java lösen. Mein mathematisches Problem kann in Arbeitseinheiten unterteilt werden, die ich in mehreren Threads gelöst haben möchte.

Ich möchte nicht, dass eine feste Anzahl von Threads daran arbeitet, sondern eine Anzahl von Threads, die der Anzahl der CPU-Kerne entspricht. Mein Problem ist, dass ich dafür kein einfaches Tutorial im Internet finden konnte. Ich habe nur Beispiele mit festen Threads gefunden.

Wie kann das gemacht werden? Können Sie Beispiele nennen?

Andreas Hornig
quelle

Antworten:

119

Sie können die Anzahl der Prozesse bestimmen, die für die Java Virtual Machine verfügbar sind, indem Sie die statische Runtime-Methode availableProcessors verwenden . Wenn Sie die Anzahl der verfügbaren Prozessoren festgelegt haben, erstellen Sie diese Anzahl von Threads und teilen Sie Ihre Arbeit entsprechend auf.

Update : Zur weiteren Verdeutlichung ist ein Thread nur ein Objekt in Java, sodass Sie ihn wie jedes andere Objekt erstellen können. Nehmen wir also an, Sie rufen die obige Methode auf und stellen fest, dass sie 2 Prozessoren zurückgibt. Genial. Jetzt können Sie eine Schleife erstellen, die einen neuen Thread generiert, die Arbeit für diesen Thread aufteilt und den Thread auslöst. Hier ist ein Pseudocode, um zu demonstrieren, was ich meine:

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();
}

Weitere Informationen zum Erstellen eines eigenen Threads finden Sie in diesem Tutorial . Möglicherweise möchten Sie auch Thread-Pooling für die Erstellung der Threads betrachten.

JasCav
quelle
17
Dies ist im Grunde genommen richtig, aber achten Sie auf die Leistung von Prozessoren, die mit Intels "Hyper-Threading" vermarktet werden. Auf einem Quad-Core wird dies 8 statt 4 zurückgeben, aber Ihre Leistung kann tatsächlich nach 4 Threads
sinken
Hallo, okay, wusste nicht, dass das möglich ist. Aber wie wird das gemacht, wenn ich eine Aufgabe in mehrere Arbeitseinheiten aufteile und eine Teillösung für den letzten Arbeitsschritt benötige? Wenn ich mehrere "yourThreads" habe, wie verwende ich join () dafür, weil ich nicht sehe, wie diese verschiedenen Threads unterscheidbar sind? :) Übrigens: Ihr Link zu Thread Pooling führte mich zu ibm.com/developerworks/library/j-jtp0730.html :)
Andreas Hornig
5
Schauen Sie sich das Beispiel hier an: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Hier erfahren Sie, wie Sie den Thread-Pool effizienter erstellen und verwalten können Anfangs komplizierter, aber wie bei den meisten Dingen ist es komplizierter, denn wenn es einfacher wäre, würden Sie einfach früher auf Einschränkungen stoßen.
Bill K
62

Sie möchten sich wahrscheinlich auch das java.util.concurrent-Framework für dieses Zeug ansehen. Etwas wie:

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

oder

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

Dies ist viel schöner als die Bewältigung Ihrer eigenen Thread-Pools usw.

DaveC
quelle
Hallo DaveC, hmmm, das habe ich noch nie gewusst, also werde ich mir das ansehen. Und es kann nach verfügbaren CPU-Kernen skaliert werden? Weil ich das in Ihren kurzen Beispielen nicht sehen kann. Viele Grüße, Andreas
Andreas Hornig
3
java.util.concurrent ist hoch skalierbar
Kristopher Ives
4
Ein Pool mit fester Größe und der Anzahl der verfügbaren Prozessoren ist häufig optimal für CPU-gebundene Prozesse. Das erste Beispiel hier ist alles, was Sie tun müssen.
Peter Lawrey
1
Wie im ersten Kommentar der akzeptierten Antwort angegeben, ist es aus zwei Gründen besser, die Hälfte der gemeldeten "Prozessoren" zu verwenden: 1. Wenn Sie Hyper-Threading haben, entspricht die tatsächliche Anzahl der Prozessoren der Hälfte der gemeldeten , und 2. es lässt etwas Rechenleistung für den Rest des Systems funktionieren (Betriebssystem und andere Programme).
Matthieu
10

Option 1:

newWorkStealingPool vonExecutors

public static ExecutorService newWorkStealingPool()

Erstellt einen Thread, der die Arbeit stiehlt, wobei alle verfügbaren Prozessoren als Zielparallelitätsstufe verwendet werden.

Mit dieser API müssen Sie nicht die Anzahl der Kerne übergeben ExecutorService.

Implementierung dieser API aus 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:

newFixedThreadPool API von Executorsoder other newXXX constructors, die zurückgibtExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

Ersetzen Sie nThreads durch Runtime.getRuntime().availableProcessors()

Option 3:

ThreadPoolExecutor

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

geben Runtime.getRuntime().availableProcessors()als Parameter an maximumPoolSize.

Ravindra Babu
quelle
4

Der Standardweg ist die Methode Runtime.getRuntime (). AvailableProcessors (). Bei den meisten Standard-CPUs haben Sie hier die optimale Thread-Anzahl (die nicht die tatsächliche CPU-Kernanzahl ist) zurückgegeben. Deshalb ist dies das, wonach Sie suchen.

Beispiel:

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

Vergessen Sie NICHT, den Executor-Dienst wie folgt herunterzufahren (sonst wird Ihr Programm nicht beendet):

service.shutdown();

Hier nur eine kurze Übersicht über das Einrichten eines zukunftsbasierten MT-Codes (zur Veranschaulichung offtopic):

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)));
    }

Dann müssen Sie verfolgen, wie viele Ergebnisse Sie erwarten, und diese wie folgt abrufen:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}
fl0w
quelle
2
Shutdown Call sollte endlich versucht werden
Christophe Roussy
1
@ChristopheRoussy du hast sehr recht, ich habe das Snippet entsprechend modifiziert, danke!
fl0w
3

In der Runtime-Klasse gibt es eine Methode namens availableProcessors (). Damit können Sie herausfinden, wie viele CPUs Sie haben. Da Ihr Programm CPU-gebunden ist, möchten Sie wahrscheinlich (höchstens) einen Thread pro verfügbarer CPU haben.

Eric Petroelje
quelle
Hallo Jason und Eric (ich benutze einen Kommentar für beide Antworten, weil er im Grunde der gleiche ist). Okay, das ist schön zu überprüfen, aber das wäre der erste Teil. Wenn ich die Kernanzahl habe, muss ich die Threads so variabel haben wie diese Anzahl von Kernen. Ich habe dieses Beispiel vor openbook.galileodesign.de/javainsel5/… (deutsch!) Versucht und es wird ein fester Thread verwendet. Aber ich möchte die gleiche Programmierung mit 2 Kernen in einer Dual-Core-Umgebung und 4 Kernen in einer Quad-Core-Umgebung haben. Ich möchte es nicht manuell ändern. Ist das möglich? VIELEN DANK! :)
Andreas Hornig
@Andreas - Siehe die Updates, die ich an meinem Beitrag vorgenommen habe. Ich denke, das wird helfen, das Problem zu klären.
JasCav