Database
 sql >> Baza danych >  >> RDS >> Database

Podstawy programowania równoległego z platformą rozwidlenia/połączenia w Javie

Wraz z pojawieniem się w ostatnich latach wielordzeniowych procesorów programowanie równoległe jest sposobem na pełne wykorzystanie nowych koni roboczych przetwarzania. Programowanie równoległe odnosi się do równoczesnego wykonywania procesów ze względu na dostępność wielu rdzeni przetwarzania. Zasadniczo prowadzi to do ogromnego wzrostu wydajności i wydajności programów w przeciwieństwie do liniowego wykonywania jednordzeniowego, a nawet wielowątkowości. Struktura Fork/Join jest częścią interfejsu Java concurrency API. Ta struktura umożliwia programistom zrównoleglenie algorytmów. Ten artykuł bada koncepcję programowania równoległego za pomocą platformy Fork/Join Framework dostępnej w Javie.

Przegląd

Programowanie równoległe ma znacznie szerszy wydźwięk i niewątpliwie jest rozległym obszarem do rozwinięcia w kilku linijkach. Sedno sprawy jest dość proste, ale operacyjnie dużo trudniejsze do osiągnięcia. Mówiąc prościej, programowanie równoległe oznacza pisanie programów, które wykorzystują więcej niż jeden procesor do wykonania zadania, to wszystko! Zgadnij co; brzmi znajomo, prawda? Niemal rymuje się z ideą wielowątkowości. Ale zauważ, że jest między nimi kilka ważnych różnic. Z pozoru są takie same, ale nurt jest zupełnie inny. W rzeczywistości wprowadzono wielowątkowość, aby zapewnić rodzaj iluzji przetwarzania równoległego bez żadnego rzeczywistego wykonywania równoległego. To, co naprawdę robi wielowątkowość, polega na tym, że kradnie czas bezczynności procesora i wykorzystuje go na swoją korzyść.

Krótko mówiąc, wielowątkowość to zbiór dyskretnych jednostek logicznych zadań, które działają, aby wykorzystać swój udział w czasie procesora, podczas gdy inny wątek może tymczasowo czekać na, powiedzmy, dane wejściowe użytkownika. Czas bezczynności procesora jest optymalnie dzielony między konkurujące wątki. Jeśli jest tylko jeden procesor, jest on współdzielony czasowo. Jeśli istnieje wiele rdzeni procesora, są one również przez cały czas współdzielone. Dlatego optymalny program wielowątkowy wypiera wydajność procesora dzięki sprytnemu mechanizmowi dzielenia czasu. Zasadniczo jest to zawsze jeden wątek korzystający z jednego procesora, podczas gdy inny wątek czeka. Dzieje się to w subtelny sposób, dzięki czemu użytkownik ma wrażenie przetwarzania równoległego, podczas którego w rzeczywistości przetwarzanie odbywa się w krótkim odstępie czasu. Największą zaletą wielowątkowości jest to, że jest to technika umożliwiająca maksymalne wykorzystanie zasobów przetwarzania. Teraz ten pomysł jest całkiem przydatny i może być używany w dowolnym zestawie środowisk, niezależnie od tego, czy ma pojedynczy procesor, czy wiele procesorów. Pomysł jest taki sam.

Z drugiej strony programowanie równoległe oznacza, że ​​istnieje wiele dedykowanych procesorów, które są połączone równolegle przez programistę. Ten rodzaj programowania jest zoptymalizowany pod kątem wielordzeniowego środowiska procesora. Większość dzisiejszych maszyn korzysta z procesorów wielordzeniowych. Dlatego programowanie równoległe jest w dzisiejszych czasach dość istotne. Nawet najtańsza maszyna jest montowana z wielordzeniowymi procesorami. Spójrz na urządzenia ręczne; nawet one są wielordzeniowe. Chociaż z wielordzeniowymi procesorami wszystko wydaje się nie do zniesienia, tutaj jest też inna strona tej historii. Czy więcej rdzeni procesora oznacza szybsze lub wydajniejsze przetwarzanie? Nie zawsze! Filozofia chciwa „im więcej, tym weselej” nie dotyczy komputerów ani życia. Ale są tam, nieuchronnie – dual, quad, octa i tak dalej. Są tam głównie dlatego, że ich chcemy, a nie dlatego, że ich potrzebujemy, przynajmniej w większości przypadków. W rzeczywistości utrzymanie nawet jednego procesora w codziennej pracy jest stosunkowo trudne. Jednak procesory wielordzeniowe mają swoje zastosowanie w szczególnych okolicznościach, takich jak serwery, gry itp. lub rozwiązywanie dużych problemów. Problem posiadania wielu procesorów polega na tym, że wymaga pamięci, która musi odpowiadać szybkością mocy obliczeniowej, a także błyskawicznym kanałom danych i innym akcesoriom. Krótko mówiąc, wiele rdzeni procesora w codziennym przetwarzaniu zapewnia poprawę wydajności, która nie może przewyższyć ilości zasobów potrzebnych do jej wykorzystania. W rezultacie otrzymujemy niewykorzystaną, kosztowną maszynę, być może przeznaczoną tylko do prezentacji.

Programowanie równoległe

W przeciwieństwie do wielowątkowości, gdzie każde zadanie jest dyskretną jednostką logiczną większego zadania, zadania programowania równoległego są niezależne, a kolejność ich wykonywania nie ma znaczenia. Zadania są definiowane w zależności od funkcji, jaką pełnią lub danych wykorzystywanych do przetwarzania; nazywa się to równoległością funkcjonalną lub równoległość danych , odpowiednio. W paralelizmie funkcjonalnym każdy procesor działa na swojej części problemu, podczas gdy w paralelizmie danych procesor pracuje na swojej części danych. Programowanie równoległe jest odpowiednie dla większej bazy problemów, która nie mieści się w jednej architekturze procesora lub problem jest tak duży, że nie można go rozwiązać w rozsądnym oszacowaniu czasu. W rezultacie zadania, gdy są rozdzielone między procesory, mogą stosunkowo szybko uzyskać wynik.

Szkielet rozwidlenia/połączenia

Fork/Join Framework jest zdefiniowany w java.util.concurrent pakiet. Zawiera kilka klas i interfejsów obsługujących programowanie równoległe. Przede wszystkim upraszcza proces tworzenia wielu wątków, ich zastosowania oraz automatyzuje mechanizm alokacji procesów między wiele procesorów. Zauważalna różnica między programowaniem wielowątkowym i równoległym w tym frameworku jest bardzo podobna do tego, o czym wspomnieliśmy wcześniej. Tutaj część przetwarzająca jest zoptymalizowana do korzystania z wielu procesorów, w przeciwieństwie do wielowątkowości, gdzie czas bezczynności pojedynczego procesora jest zoptymalizowany na podstawie czasu współdzielonego. Dodatkową zaletą tego frameworka jest użycie wielowątkowości w środowisku wykonywania równoległego. Nie zaszkodzi.

W tej strukturze istnieją cztery podstawowe klasy:

  • ForkJoinTask: Jest to klasa abstrakcyjna, która definiuje zadanie. Zazwyczaj zadanie jest tworzone za pomocą fork() metoda zdefiniowana w tej klasie. To zadanie jest prawie podobne do normalnego wątku utworzonego za pomocą wątku klasy, ale jest od niej lżejszy. Zastosowany mechanizm polega na tym, że umożliwia zarządzanie dużą liczbą zadań za pomocą niewielkiej liczby rzeczywistych wątków, które dołączają do ForkJoinPool . widelec() Metoda umożliwia asynchroniczne wykonanie zadania wywołującego. join() Metoda umożliwia oczekiwanie na ostateczne zakończenie zadania, na którym jest wywoływana. Istnieje inna metoda, zwana invoke() , który łączy widelec i dołącz operacje w jedno połączenie.
  • ForkJoinPool: Ta klasa zapewnia wspólną pulę do zarządzania wykonywaniem ForkJoinTask zadania. Zasadniczo stanowi punkt wejścia dla zgłoszeń z innych niż ForkJoinTask klientów, a także zarządzanie i monitorowanie operacji.
  • Działanie rekurencyjne: Jest to również abstrakcyjne rozszerzenie ForkJoinTask klasa. Zazwyczaj rozszerzamy tę klasę, aby utworzyć zadanie, które nie zwraca wyniku lub ma unieważnienie typ zwrotu. compute() metoda zdefiniowana w tej klasie jest zastępowana, aby uwzględnić kod obliczeniowy zadania.
  • Zadanie rekursywne: To kolejne abstrakcyjne rozszerzenie ForkJoinTask klasa. Rozszerzamy tę klasę, aby utworzyć zadanie, które zwraca wynik. I podobnie jak ResursiveAction, zawiera również chronioną abstrakcyjną metodę obliczeniową() metoda. Ta metoda jest zastępowana, aby uwzględnić część obliczeniową zadania.

Strategia ramowa rozwidlenia/połączenia

Ta struktura wykorzystuje rekurencyjną metodę dziel i zwyciężaj strategia wdrażania przetwarzania równoległego. Zasadniczo dzieli zadanie na mniejsze podzadania; następnie każde podzadanie jest dalej podzielone na podzadania. Ten proces jest stosowany rekurencyjnie w każdym zadaniu, dopóki nie będzie wystarczająco mały, aby można go było obsługiwać sekwencyjnie. Załóżmy, że mamy zwiększyć wartości tablicy N liczby. To jest zadanie. Teraz możemy podzielić tablicę przez dwa tworząc dwa podzadania. Podziel każdy z nich ponownie na dwa kolejne podzadania i tak dalej. W ten sposób możemy zastosować metodę dziel i zwyciężaj strategia rekurencyjnie, aż zadania zostaną wyodrębnione w problem jednostkowy. Ten problem jednostki może być następnie wykonywany równolegle przez dostępne procesory wielordzeniowe. W środowisku nierównoległym musieliśmy przejść przez całą tablicę i wykonać przetwarzanie po kolei. Jest to zdecydowanie nieefektywne podejście z punktu widzenia przetwarzania równoległego. Ale prawdziwe pytanie brzmi:czy każdy problem można podzielić i pokonać ? Zdecydowanie nie! Istnieją jednak problemy, które często wiążą się z jakimś rodzajem tablicy, gromadzeniem lub grupowaniem danych, które szczególnie pasują do tego podejścia. Nawiasem mówiąc, istnieją problemy, które mogą nie wykorzystywać zbierania danych, ale można je zoptymalizować pod kątem wykorzystania strategii programowania równoległego. Jaki rodzaj problemów obliczeniowych nadaje się do przetwarzania równoległego lub omówienie algorytmu równoległego jest poza zakresem tego artykułu. Zobaczmy szybki przykład zastosowania Fork/Join Framework.

Szybki przykład

To bardzo prosty przykład, który daje pomysł na implementację równoległości w Javie za pomocą Fork/Join Framework.

package org.mano.example;
import java.util.concurrent.RecursiveAction;
public class CustomRecursiveAction extends
      RecursiveAction {
   final int THRESHOLD = 2;
   double [] numbers;
   int indexStart, indexLast;
   CustomRecursiveAction(double [] n, int s, int l) {
      numbers = n;
      indexStart = s;
      indexLast = l;
   }
   @Override
   protected void compute() {
      if ((indexLast - indexStart) > THRESHOLD)
         for (int i = indexStart; i < indexLast; i++)
            numbers [i] = numbers [i] + Math.random();
         else
            invokeAll (new CustomRecursiveAction(numbers,
               indexStart, (indexStart - indexLast) / 2),
               new CustomRecursiveAction(numbers,
                  (indexStart - indexLast) / 2,
                     indexLast));
   }
}

package org.mano.example;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
public class Main {
   public static void main(String[] args) {
      final int SIZE = 10;
      ForkJoinPool pool = new ForkJoinPool();
      double na[] = new double [SIZE];
      System.out.println("initialized random values :");
      for (int i = 0; i < na.length; i++) {
         na[i] = (double) i + Math.random();
         System.out.format("%.4f ", na[i]);
      }
      System.out.println();
      CustomRecursiveAction task = new
         CustomRecursiveAction(na, 0, na.length);
      pool.invoke(task);
      System.out.println("Changed values :");
      for (inti = 0; i < 10; i++)
      System.out.format("%.4f ", na[i]);
      System.out.println();
   }
}

Wniosek

Jest to zwięzły opis programowania równoległego i jego obsługi w Javie. Powszechnie wiadomo, że posiadanie N rdzenie nie zrobią wszystkiego N razy szybciej. Tylko część aplikacji Java efektywnie korzysta z tej funkcji. Kod programowania równoległego to trudna ramka. Ponadto efektywne programy równoległe muszą uwzględniać takie kwestie, jak równoważenie obciążenia, komunikacja między zadaniami równoległymi i tym podobne. Istnieje kilka algorytmów, które lepiej pasują do wykonywania równoległego, ale wiele nie. W każdym razie w Java API nie brakuje wsparcia. Zawsze możemy majstrować przy interfejsach API, aby dowiedzieć się, co jest najlepsze. Udanego kodowania 🙂


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Rozwiązania wyzwań generatora serii liczb – Część 2

  2. Modele baz danych dla handlu elektronicznego Część 1:Biuletyn

  3. Trendy ScyllaDB – jak użytkownicy wdrażają bazę danych Big Data w czasie rzeczywistym

  4. Model danych płacowych

  5. Wprowadzenie do zatrzasków