Zmuszanie wielu wątków do używania wielu procesorów, gdy są one dostępne

Piszę program Java, który wykorzystuje dużo procesora ze względu na naturę tego, co robi. Jednak wiele z nich może działać równolegle i zrobiłem mój program wielowątkowy. Kiedy go uruchamiam, wydaje się, że używa tylko jednego procesora, dopóki nie potrzebuje więcej niż używa innego procesora - czy jest coś, co mogę zrobić w Javie, aby zmusić różne wątki do działania na różnych rdzeniach / procesorach?

Author: Raedwald, 2009-08-03

10 answers

Kiedy go uruchamiam, wydaje się, że używa jeden procesor, aż będzie potrzebował więcej niż używa innego procesora-czy jest coś co można zrobić w Javie, aby wymusić różne wątki do uruchomienia na różnych rdzenie/Procesory?

Interpretuję tę część twojego pytania jako oznaczającą, że już rozwiązałeś problem tworzenia aplikacji wielowątkowej. Mimo to nie od razu zaczyna używać wielu rdzeni.

ODPOWIEDŹ na "czy jest jakiś sposób na siłę ..." jest (AFAIK) nie bezpośrednio. Twój JVM i / lub system operacyjny hosta decydują o liczbie "natywnych" wątków i o tym, jak te wątki są mapowane na fizyczne procesory. Masz kilka opcji strojenia. Na przykład znalazłem tę stronę , która mówi o tym, jak dostroić wątek Javy na Solarisie. I ta strona mówi o innych rzeczach, które mogą spowolnić wielowątkową aplikację.

 29
Author: Stephen C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-04 05:59:32

Istnieją dwa podstawowe sposoby tworzenia wielu wątków w Javie. Każde logiczne zadanie, które tworzysz za pomocą tych metod, powinno działać na świeżym rdzeniu, gdy jest to potrzebne i dostępne.

Metoda pierwsza: definiuje obiekt Runnable lub Thread (który może przyjąć Runnable w konstruktorze) i uruchamia go wraz z wątkiem.metoda start (). Będzie on uruchamiany na dowolnym rdzeniu, który poda SYSTEM OPERACYJNY-generalnie tym mniej załadowanym.

Tutorial: Definiowanie i uruchamianie wątków

Metoda dwa: zdefiniuj obiekty implementujące interfejs Runnable (jeśli nie zwracają wartości) lub Callable (jeśli tak), który zawiera kod przetwarzania. Przekaż je jako zadania do usługi ExecutorService z Javy.util.pakiet współbieżny. Java.util./ align = "left" / Klasa Executors posiada kilka metod do tworzenia standardowych, użytecznych rodzajów ExecutorServices. Link {[9] } do tutoriala Executors.

Z własnego doświadczenia, Executors stałe i buforowane pule wątków są bardzo dobre, chociaż będziesz chciał poprawić liczbę wątków. Runtime.getRuntime ().availableProcessors () mogą być używane w czasie wykonywania do liczenia dostępnych rdzeni. Musisz zamknąć pule wątków po zakończeniu aplikacji, w przeciwnym razie aplikacja nie zostanie zamknięta, ponieważ wątki ThreadPool pozostają uruchomione.

Uzyskanie dobrej wydajności wielordzeniowej jest czasami trudne i pełne gotchas:

  • Wejście/Wyjście dysku spowalnia dużo podczas uruchamiania w równolegle. Tylko jeden wątek powinien odczytywać/zapisywać dysk w czas.
  • Synchronizacja obiektów zapewnia bezpieczeństwo operacji wielowątkowych, ale spowalnia pracę.
  • Jeśli zadania są zbyt trywialne (małe bity pracy, Wykonanie szybko) narzut zarządzania nimi w ExecutorService kosztuje więcej niż zyskujesz na wielu rdzeniach.
  • tworzenie nowych obiektów wątku jest powolne. ExecutorServices spróbuje ponownie użyć istniejących wątków, jeśli to możliwe.
  • wszelkiego rodzaju szalone rzeczy mogą się zdarzyć, gdy wiele wątków nad czymś pracuje. Zostawić Twój system jest prosty i staraj się logicznie wyróżniać zadania i nie wchodzić w interakcje.
[1]}Jeszcze jeden problem: kontrolowanie pracy jest trudne! Dobrą praktyką jest posiadanie jednego wątku menedżera, który tworzy i przesyła zadania, a następnie kilku roboczych wątków z kolejkami pracy (używając ExecutorService).

Poruszam tutaj kluczowe kwestie -- programowanie wielowątkowe jest uważane przez wielu ekspertów za jeden z najtrudniejszych tematów programowania. Jest nieintuicyjny, złożony i abstrakcje są często słabe.


Edit -- Example using ExecutorService:

public class TaskThreader {
    class DoStuff implements Callable {
       Object in;
       public Object call(){
         in = doStep1(in);
         in = doStep2(in);
         in = doStep3(in); 
         return in;
       }
       public DoStuff(Object input){
          in = input;
       }
    }

    public abstract Object doStep1(Object input);    
    public abstract Object doStep2(Object input);    
    public abstract Object doStep3(Object input);    

    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        ArrayList<Callable> tasks = new ArrayList<Callable>();
        for(Object input : inputs){
           tasks.add(new DoStuff(input));
        }
        List<Future> results = exec.invokeAll(tasks);
        exec.shutdown();
        for(Future f : results) {
           write(f.get());
        }
    }
}
 54
Author: BobMcGee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-04 00:07:52

Najpierw powinieneś udowodnić sobie, że twój program będzie działał szybciej na wielu rdzeniach. Wiele systemów operacyjnych wkłada wysiłek w uruchamianie wątków programów na tym samym rdzeniu , o ile to możliwe.

Uruchamianie na tym samym rdzeniu ma wiele zalet. Pamięć podręczna procesora jest gorąca, co oznacza, że dane dla tego programu są ładowane do procesora. Obiekty blokady / monitora / synchronizacji znajdują się w pamięci podręcznej procesora, co oznacza, że inne procesory nie muszą wykonywać operacji synchronizacji pamięci podręcznej autobus (drogi!).

Jedną z rzeczy, która może bardzo łatwo sprawić, że twój program będzie działał na tym samym procesorze przez cały czas, jest nadmierne użycie blokad i pamięci współdzielonej. Twoje wątki nie powinny ze sobą rozmawiać. Im rzadziej wątki używają tych samych obiektów w tej samej pamięci, tym częściej będą działać na różnych procesorach. Im częściej używają tej samej pamięci, tym częściej muszą blokować oczekiwanie na drugi wątek.

Gdy system operacyjny zobaczy jeden blok wątku dla innego wątku, uruchomi się ten wątek na tym samym procesorze, kiedy tylko może. Zmniejsza ilość pamięci, która porusza się po magistrali między procesorami. To chyba powoduje to, co widzisz w swoim programie.

 17
Author: Zan Lynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-14 19:04:28

Po pierwsze, proponuję przeczytać "współbieżność w praktyce" Briana Goetza.

Jest to zdecydowanie najlepsza książka opisująca współbieżne programowanie w Javie.

Współbieżność jest "łatwa do nauczenia, trudna do opanowania". Proponuję przeczytać dużo na ten temat przed przystąpieniem do niego. Bardzo łatwo jest uzyskać program wielowątkowy, aby działał poprawnie 99,9% czasu i nie powiódł się 0,1%. Jednak oto kilka wskazówek, które pomogą Ci zacząć: {]}

Istnieją dwa wspólne sposoby aby program używał więcej niż jednego rdzenia:

  1. uruchom program za pomocą wielu procesów. Przykładem jest Apache skompilowany z pre-Fork MPM, który przypisuje żądania do procesów potomnych. W programie wieloprocesowym pamięć nie jest domyślnie współdzielona. Można jednak mapować sekcje pamięci współdzielonej między procesami. Apache robi to za pomocą "tablicy wyników".
  2. Uczyń program wielowątkowym. W programie wielowątkowym cała pamięć sterty jest domyślnie współdzielona. Każdy wątek nadal ma własny stos, ale może uzyskać dostęp do dowolnej części sterty. Zazwyczaj większość programów Java jest wielowątkowa, a nie wieloprocesowa.

Na najniższym poziomie można tworzyć i niszczyć wątki . Java ułatwia tworzenie wątków w przenośny, wieloplatformowy sposób.

Ponieważ tworzenie i niszczenie wątków przez cały czas jest kosztowne, Java zawiera obecnieExecutorów do tworzenia pul wątków wielokrotnego użytku. Zadania mogą być przypisane do wykonawców, wynik można pobrać za pomocą przyszłego obiektu.

Zazwyczaj ma się zadanie, które można podzielić na mniejsze zadania, ale wyniki końcowe muszą być ponownie połączone. Na przykład, dzięki sortowaniu scalającemu, można podzielić listę na mniejsze i mniejsze części, aż każdy rdzeń będzie sortował. Ponieważ jednak każda sublista jest posortowana, musi zostać scalona, aby uzyskać ostateczną posortowaną listę. Ponieważ jest to kwestia "podziel i podbij" jest dość powszechna, Istnieje JSR framework , który może obsługiwać podstawową dystrybucję i łączenie. Ten framework będzie prawdopodobnie zawarty w Java 7.

 8
Author: brianegge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-04 06:36:29

Nie ma możliwości ustawienia powinowactwa procesora w Javie. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4234402

Jeśli musisz to zrobić, użyj JNI, aby utworzyć natywne wątki i ustawić ich powinowactwo.

 4
Author: Iouri Goussev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-06 23:16:20

Powinieneś napisać swój program do wykonania jego pracy w postaci partii wywołania przekazanego do ExecutorService i wykonanego za pomocą invokeAll(...).

Możesz następnie wybrać odpowiednią implementację w środowisku runtime z klasy Executors. Sugestią byłoby wezwanie wykonawców.newFixedThreadPool() z liczbą odpowiadającą mniej więcej liczbie rdzeni procesora, które mają być zajęte.

 1
Author: Thorbjørn Ravn Andersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 18:04:17

Najprostszą rzeczą do zrobienia jest rozbicie programu na wiele procesów. System operacyjny przydzieli je między rdzeniami.

Nieco trudniej jest rozbić program na wiele wątków i zaufać JVM, aby odpowiednio je przydzielić. To jest-ogólnie-to, co ludzie robią, aby korzystać z dostępnego sprzętu.


Edit

Jak program do wielokrotnego przetwarzania może być "łatwiejszy"? Oto krok w rurociągu.

public class SomeStep {
    public static void main( String args[] ) {
        BufferedReader stdin= new BufferedReader( System.in );
        BufferedWriter stdout= new BufferedWriter( System.out );
        String line= stdin.readLine();
        while( line != null ) {
             // process line, writing to stdout
             line = stdin.readLine();
        }
    }
}

Każdy krok w rurociągu jest podobnie ustrukturyzowany. 9 linii napowietrznych do dowolnego przetwarzania.

To może nie być absolutnie najskuteczniejsze. Ale to bardzo proste.

Ogólna struktura współbieżnych procesów nie jest problemem JVM. To problem z systemem operacyjnym, więc użyj powłoki.

java -cp pipline.jar FirstStep | java -cp pipline.jar SomeStep | java -cp pipline.jar LastStep

Pozostało tylko wypracować pewną serializację dla obiektów danych w potoku. Standardowa serializacja działa dobrze. Czytaj http://java.sun.com/developer/technicalArticles/Programming/serialization / Dla wskazówek, jak serializować. Możesz zastąpić BufferedReader i BufferedWriter na ObjectInputStream i ObjectOutputStream, aby to osiągnąć.

 1
Author: S.Lott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-05 10:22:25

Myślę, że ten problem jest związany z Java Parallel Proccesing Framework (JPPF). Za jego pomocą można uruchamiać różne zadania na różnych procesorach.

 1
Author: Nandika,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-14 18:47:30

Strojenie wydajności JVM zostało już wspomniane w Dlaczego ten kod Javy nie wykorzystuje wszystkich rdzeni procesora?. Zauważ, że dotyczy to tylko JVM, więc Twoja aplikacja musi już używać wątków (i mniej więcej "poprawnie"):

Http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf

 1
Author: ShiDoiSi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:29

Możesz użyć poniższego API z Executors z wersją Java 8

public static ExecutorService newWorkStealingPool()

Tworzy pulę wątków kradnących pracę przy użyciu wszystkich dostępnych procesorów jako docelowego poziomu równoległości.

Ze względu na mechanizm kradzieży pracy, bezczynne wątki kradną zadania z kolejki zadań zajętych wątków, a ogólna przepustowość wzrośnie.

Z grepcode , implementacja newWorkStealingPool jest następująca

/**
     * 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);
    }
 1
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-02 18:11:28