sql >> Databáze >  >> RDS >> Database

Základy paralelního programování s rámcem Fork/Join v Javě

S příchodem vícejádrových CPU v posledních letech je paralelní programování způsob, jak plně využít nové pracovní koně pro zpracování. Paralelní programování se týká souběžného provádění procesů v důsledku dostupnosti více procesorových jader. To v podstatě vede k ohromnému nárůstu výkonu a efektivity programů na rozdíl od lineárního jednojádrového provádění nebo dokonce multithreadingu. Rámec Fork/Join je součástí Java Concurrency API. Tento rámec umožňuje programátorům paralelizovat algoritmy. Tento článek zkoumá koncept paralelního programování s pomocí rozhraní Fork/Join Framework dostupného v Javě.

Přehled

Paralelní programování má mnohem širší význam a nepochybně jde o rozsáhlou oblast, kterou je třeba rozpracovat v několika řádcích. Jádro věci je docela jednoduché, ale provozně mnohem obtížnější dosáhnout. Jednoduše řečeno, paralelní programování znamená psát programy, které k dokončení úlohy používají více než jeden procesor, to je vše! Hádej co; zní to povědomě, že? Skoro se to rýmuje s myšlenkou multithreadingu. Všimněte si však, že mezi nimi existuje několik důležitých rozdílů. Na povrchu jsou stejné, ale spodní proud je naprosto odlišný. Ve skutečnosti bylo multithreading zavedeno, aby poskytlo jakousi iluzi paralelního zpracování bez skutečného paralelního provádění. Multithreading ve skutečnosti dělá to, že krade čas nečinnosti CPU a využívá jej ve svůj prospěch.

Stručně řečeno, multithreading je sbírka diskrétních logických jednotek úloh, které se spouštějí, aby získaly svůj podíl na CPU, zatímco jiné vlákno může dočasně čekat, řekněme, na nějaký uživatelský vstup. Nečinný čas CPU je optimálně sdílen mezi konkurenčními vlákny. Pokud existuje pouze jeden CPU, je to čas sdílený. Pokud existuje více jader CPU, jsou také všechna sdílená. Optimální vícevláknový program tedy vytlačí výkon CPU chytrým mechanismem sdílení času. V podstatě je to vždy jedno vlákno využívající jeden CPU, zatímco jiné vlákno čeká. To se děje jemným způsobem, že uživatel získá pocit paralelního zpracování, kde ve skutečnosti probíhá zpracování v rychlém sledu. Největší výhodou multithreadingu je to, že jde o techniku, která ze zdrojů zpracování vytěží maximum. Nyní je tato myšlenka docela užitečná a lze ji použít v jakékoli sadě prostředí, ať už má jeden CPU nebo více CPU. Myšlenka je stejná.

Paralelní programování na druhé straně znamená, že existuje několik vyhrazených CPU, které programátor využívá paralelně. Tento typ programování je optimalizován pro prostředí vícejádrového CPU. Většina dnešních strojů používá vícejádrové CPU. Proto je v dnešní době paralelní programování docela aktuální. I ten nejlevnější stroj je vybaven vícejádrovými CPU. Podívejte se na ruční zařízení; i když jsou vícejádrové. I když se vše s vícejádrovými procesory zdá drsné, je zde také druhá stránka věci. Znamená více jader CPU rychlejší nebo efektivnější výpočet? Ne vždy! Chamtivá filozofie „čím více, tím lépe“ neplatí pro výpočetní techniku ​​ani v životě. Ale jsou tam bez povšimnutí – duální, quad, octa a tak dále. Jsou tam většinou proto, že je chceme, a ne proto, že je potřebujeme, alespoň ve většině případů. Ve skutečnosti je poměrně obtížné udržet i jediný CPU zaneprázdněný každodenním počítačem. Vícejádra však mají své využití za zvláštních okolností, jako jsou servery, hraní her atd. nebo řešení velkých problémů. Problém s více CPU je, že vyžaduje paměť, která musí odpovídat rychlosti zpracování s výkonem, spolu s bleskově rychlými datovými kanály a dalším příslušenstvím. Stručně řečeno, více jader CPU v každodenním zpracování poskytuje zlepšení výkonu, které nemůže převážit množství zdrojů potřebných k jeho použití. V důsledku toho dostáváme nevyužitý drahý stroj, který je možná určen pouze k předvedení.

Paralelní programování

Na rozdíl od multithreadingu, kde je každá úloha samostatnou logickou jednotkou větší úlohy, úlohy paralelního programování jsou nezávislé a na pořadí jejich provádění nezáleží. Úkoly jsou definovány podle funkce, kterou vykonávají, nebo podle dat použitých při zpracování; tomu se říká funkční paralelismus nebo datový paralelismus , resp. Ve funkčním paralelismu každý procesor pracuje na své části problému, zatímco v paralelismu dat pracuje procesor na své části dat. Paralelní programování je vhodné pro větší problémovou základnu, která se nevejde do jediné architektury CPU, nebo může být problém tak velký, že jej nelze vyřešit v rozumném odhadu času. Výsledkem je, že když jsou úlohy rozděleny mezi procesory, mohou získat výsledek relativně rychle.

Fork/Join Framework

Fork/Join Framework je definován v java.util.concurrent balík. Zahrnuje několik tříd a rozhraní, která podporují paralelní programování. Především zjednodušuje proces vytváření více vláken, jejich použití a automatizuje mechanismus alokace procesů mezi více procesorů. Pozoruhodný rozdíl mezi multithreadingem a paralelním programováním s tímto rámcem je velmi podobný tomu, co jsme zmínili dříve. Zde je procesní část optimalizována pro použití více procesorů na rozdíl od multithreadingu, kde je doba nečinnosti jednoho CPU optimalizována na základě sdíleného času. Další výhodou tohoto rámce je použití multithreadingu v prostředí paralelního provádění. Žádná škoda.

V tomto rámci jsou čtyři základní třídy:

  • ForkJoinTask: Toto je abstraktní třída, která definuje úkol. Úloha je obvykle vytvořena pomocí fork() metoda definovaná v této třídě. Tato úloha je téměř podobná normálnímu vláknu vytvořenému pomocí vlákna třídy, ale je lehčí než ona. Mechanismus, který používá, spočívá v tom, že umožňuje správu velkého počtu úloh s pomocí malého počtu skutečných vláken, která se připojují k ForkJoinPool . fork() metoda umožňuje asynchronní provedení úlohy vyvolání. join() metoda umožňuje čekat, až bude úloha, na kterou je volána, definitivně ukončena. Existuje další metoda, nazvaná invoke() , který kombinuje fork a připojit se operace do jednoho hovoru.
  • ForkJoinPool: Tato třída poskytuje společný fond pro správu provádění ForkJoinTask úkoly. V podstatě poskytuje vstupní bod pro příspěvky z jiných než ForkJoinTask klientů a také řízení a monitorování operací.
  • Rekurzivní akce: Toto je také abstraktní rozšíření ForkJoinTask třída. Obvykle tuto třídu rozšiřujeme, abychom vytvořili úlohu, která nevrací výsledek nebo má neplatnost návratový typ. Funkce compute() metoda definovaná v této třídě je přepsána tak, aby zahrnovala výpočetní kód úlohy.
  • Rekurzivní úloha: Toto je další abstraktní rozšíření ForkJoinTask třída. Tuto třídu rozšiřujeme, abychom vytvořili úlohu, která vrací výsledek. A podobně jako ResursiveAction obsahuje také chráněné abstraktní compute() metoda. Tato metoda je přepsána, aby zahrnovala výpočetní část úlohy.

Rámcová strategie Fork/Join

Tento rámec využívá rekurzivní rozděl a panuj strategie implementace paralelního zpracování. V podstatě rozděluje úkol na menší dílčí úkoly; pak je každý dílčí úkol dále rozdělen na dílčí dílčí úkoly. Tento proces je aplikován rekurzivně na každý úkol, dokud není dostatečně malý, aby mohl být zpracován postupně. Předpokládejme, že máme zvýšit hodnoty pole N čísla. Toto je úkol. Nyní můžeme pole rozdělit dvěma a vytvořit dva dílčí úkoly. Každý z nich znovu rozdělte na další dva dílčí úkoly a tak dále. Tímto způsobem můžeme aplikovat rozděl a panuj strategie rekurzivně, dokud se úkoly nevyčlení do jednotkového problému. Tento problém jednotky pak může být spuštěn paralelně více dostupnými jádrovými procesory. V neparalelním prostředí jsme museli procházet celým polem a provádět zpracování v sekvenci. To je zjevně neefektivní přístup s ohledem na paralelní zpracování. Skutečnou otázkou však je, zda lze každý problém rozdělit a zdolat ? Rozhodně NE! Existují však problémy, které často zahrnují určitý druh pole, shromažďování, seskupování dat, které tomuto přístupu obzvláště vyhovuje. Mimochodem, existují problémy, které nemusí využívat sběr dat, přesto lze optimalizovat pro použití strategie pro paralelní programování. Jaký typ výpočetních problémů je vhodný pro paralelní zpracování nebo diskusi o paralelním algoritmu je mimo rozsah tohoto článku. Podívejme se na rychlý příklad aplikace rámce Fork/Join.

Rychlý příklad

Toto je velmi jednoduchý příklad, který vám poskytne představu, jak implementovat paralelismus v Javě pomocí rámce Fork/Join.

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

Závěr

Toto je stručný popis paralelního programování a jeho podpory v Javě. Je dobře známo, že mít N jádra neudělají všechno N krát rychleji. Tuto funkci efektivně využívá pouze část aplikací Java. Paralelní programovací kód je obtížný rámec. Efektivní paralelní programy navíc musí brát v úvahu problémy, jako je vyvažování zátěže, komunikace mezi paralelními úlohami a podobně. Existují některé algoritmy, které lépe vyhovují paralelnímu provádění, ale mnohé ne. Java API každopádně o jeho podporu nemá nouzi. Vždy si můžeme pohrát s API, abychom zjistili, co nejlépe vyhovuje. Hodně štěstí při kódování 🙂


  1. Přístup k ladění indexu – 1. část

  2. Jak zjistit, zda je číslo plovoucí nebo celé číslo

  3. Správa indexů SQL Server Použití Správce indexů pro SQL Server

  4. Azure Virtual Machines pro použití SQL Server