Jak skalować wątki według rdzeni procesora?

107

Chcę rozwiązać problem matematyczny z wieloma wątkami w Javie. mój problem matematyczny można podzielić na jednostki pracy, które chcę rozwiązać w kilku wątkach.

Nie chcę, aby pracowała nad nim stała liczba wątków, ale zamiast tego liczba wątków odpowiadająca liczbie rdzeni procesora. Mój problem polega na tym, że nie mogłem znaleźć w internecie prostego poradnika na ten temat. Znalazłem tylko przykłady ze stałymi wątkami.

Jak to zrobić? Czy możesz podać przykłady?

Andreas Hornig
źródło

Odpowiedzi:

120

Liczbę procesów dostępnych dla wirtualnej maszyny języka Java można określić za pomocą statycznej metody Runtime, availableProcessors . Po określeniu liczby dostępnych procesorów utwórz odpowiednią liczbę wątków i odpowiednio podziel pracę.

Aktualizacja : Aby jeszcze bardziej wyjaśnić, wątek jest po prostu obiektem w Javie, więc możesz go utworzyć tak, jak każdy inny obiekt. Powiedzmy więc, że wywołujesz powyższą metodę i stwierdzasz, że zwraca ona 2 procesory. Niesamowite. Teraz możesz utworzyć pętlę, która generuje nowy wątek, oddziela pracę dla tego wątku i odpala z wątku. Oto kilka psuedokodów, aby pokazać, o co mi chodzi:

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

Aby uzyskać więcej informacji na temat tworzenia własnego wątku, przejdź do tego samouczka . Możesz również zajrzeć do Puli wątków w celu utworzenia wątków.

JasCav
źródło
17
Jest to w zasadzie poprawne, ale należy uważać na wydajność procesorów sprzedawanych z technologią „hyper-threading” firmy Intel. Na czterordzeniowym rdzeniu zwróci to 8 zamiast 4, ale wydajność może faktycznie zacząć spadać po 4 wątkach - więc moje własne testy porównawcze mówią mi :)
xcut
Cześć ok, nie wiedziałem, że to jest możliwe. ale kiedy podzielę jedno zadanie na kilka jednostek roboczych i potrzebuję rozwiązania wszystkich części do końcowego etapu pracy, jak to się robi? Kiedy mam kilka "twoich wątków", jak mam do tego użyć join (), ponieważ nie widzę, jak można rozróżnić te kilka wątków? :) Przy okazji: Twój link do Thread Pooling prowadzi mnie do ibm.com/developerworks/library/j-jtp0730.html :)
Andreas Hornig
5
Spójrz na przykład tutaj: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Podaje on bardziej usprawniony sposób tworzenia i zarządzania pulą wątków ... Może się wydawać na początku bardziej skomplikowane, ale tak jak w przypadku większości rzeczy, jest to bardziej skomplikowane, ponieważ gdyby było prostsze, wcześniej po prostu osiągnąłbyś ograniczenia.
Bill K
62

Prawdopodobnie zechcesz również przyjrzeć się platformie java.util.concurrent, aby uzyskać takie informacje. Coś jak:

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

lub

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

Jest to o wiele przyjemniejsze niż radzenie sobie z własnymi pulami wątków itp.

DaveC
źródło
Cześć DaveC, hmmm, nie wiedziałem tego wcześniej, więc przyjrzę się temu. Czy można go skalować zgodnie z dostępnymi rdzeniami procesora? Ponieważ nie widzę tego w krótkich przykładach. Z poważaniem, Andreas
Andreas Hornig
3
java.util.concurrent jest wysoce skalowalny
Kristopher Ives
4
Pula o stałym rozmiarze z liczbą dostępnych procesorów jest często optymalna dla procesów związanych z procesorem. Pierwszy przykład to wszystko, co musisz zrobić.
Peter Lawrey
1
Jak stwierdzono w pierwszym komentarzu do zaakceptowanej odpowiedzi, lepiej byłoby użyć połowy liczby zgłoszonych „procesorów” z dwóch powodów: 1. jeśli masz hiperwątkowość, rzeczywista liczba procesorów jest o połowę niższa od zgłaszanej oraz 2. umożliwia działanie pozostałej części systemu (system operacyjny i inne programy) pewnej mocy obliczeniowej.
Matthieu
10

Opcja 1:

newWorkStealingPool zExecutors

public static ExecutorService newWorkStealingPool()

Tworzy pulę wątków do kradzieży pracy przy użyciu wszystkich dostępnych procesorów jako docelowego poziomu równoległości.

Dzięki temu interfejsowi API nie musisz przekazywać liczby rdzeni do ExecutorService.

Implementacja tego API z 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);
    }

Opcja 2:

newFixedThreadPool API z Executorslub other newXXX constructors, który zwracaExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

zamień nThreads na Runtime.getRuntime().availableProcessors()

Opcja 3:

ThreadPoolExecutor

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

przekazać Runtime.getRuntime().availableProcessors()jako parametr do maximumPoolSize.

Ravindra babu
źródło
4

Standardowym sposobem jest metoda Runtime.getRuntime (). AvailableProcessors (). W przypadku większości standardowych procesorów zwróci się tutaj optymalną liczbę wątków (która nie jest rzeczywistą liczbą rdzeni procesora). Dlatego właśnie tego szukasz.

Przykład:

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

NIE zapomnij zamknąć usługi executora w ten sposób (inaczej twój program się nie zakończy):

service.shutdown();

Oto krótki opis, jak skonfigurować przyszły kod MT (offtopic, dla ilustracji):

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

Następnie musisz śledzić liczbę spodziewanych wyników i pobierać je w następujący sposób:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}
fl0w
źródło
2
Wezwanie do zamknięcia systemu powinno zostać ostatecznie wypróbowane
Christophe Roussy
1
@ChristopheRoussy masz rację, odpowiednio zmodyfikowałem fragment, dziękuję!
fl0w
3

W klasie Runtime istnieje metoda o nazwie availableProcessors (). Możesz użyć tego, aby dowiedzieć się, ile masz procesorów. Ponieważ twój program jest związany z procesorem, prawdopodobnie chciałbyś mieć (co najwyżej) jeden wątek na dostępny procesor.

Eric Petroelje
źródło
Cześć Jason i Eric (używam jednego komentarza dla obu twoich odpowiedzi, ponieważ jest zasadniczo taki sam). ok, fajnie to sprawdzić, ale to byłaby pierwsza część. Kiedy mam liczbę rdzeni, muszę mieć wątki tak zmienne, jak ta liczba rdzeni. Wypróbowałem ten przykład przed openbook.galileodesign.de/javainsel5/… (niemiecki!) I używa on ustalonego wątku. Ale chcę mieć takie samo programowanie, używając 2 rdzeni w środowisku dwurdzeniowym i 4 rdzeni w środowisku czterordzeniowym. Nie chcę zmieniać tego ręcznie. czy to możliwe? DZIĘKI! :)
Andreas Hornig
@Andreas - zobacz aktualizacje, które wprowadziłem do mojego postu. Myślę, że to pomoże wyjaśnić problem.
JasCav