Megosztás a következőn keresztül:


Gyorsítás a PLINQ-ban

Ez a cikk olyan információkat tartalmaz, amelyek segítenek olyan PLINQ-lekérdezések írásában, amelyek a lehető leghatékonyabbak, miközben továbbra is megfelelő eredményeket adnak.

A PLINQ elsődleges célja, hogy felgyorsítsa a LINQ és az Objects lekérdezések végrehajtását a lekérdezésdelegáltak párhuzamos végrehajtásával többmagos számítógépeken. A PLINQ akkor működik a legjobban, ha a forrásgyűjtemény egyes elemeinek feldolgozása független, és nincs közös állapot az egyes meghatalmazottak között. Az ilyen műveletek gyakran előfordulnak a LINQ-ban az objektumokra és a PLINQ-ra, és gyakran "kellemesen párhuzamosnak" is nevezik őket, mert könnyen ütemezhetnek több szálon. Azonban nem minden lekérdezés tartalmaz teljesen kellemesen párhuzamos műveleteket. A lekérdezések legtöbb esetben olyan operátorokat foglalnak magukban, amelyek vagy nem párhuzamosak, vagy lelassítják a párhuzamos végrehajtást. És még a teljesen kellemesen párhuzamos lekérdezések esetén is a PLINQ-nak particionálnia kell az adatforrást, és ütemeznie kell a munkát a szálakon, és általában egyesítenie kell az eredményeket a lekérdezés befejezésekor. Mindezek a műveletek növelik a párhuzamosítás számítási költségeit; ezeket a párhuzamosítási költségeket többletterhelésnek nevezzük. A PLINQ-lekérdezések optimális teljesítményének elérése érdekében a cél az, hogy maximalizálja a kellemesen párhuzamos alkatrészeket, és minimalizálja a többletterhelést igénylő alkatrészeket.

A PLINQ-lekérdezés teljesítményét befolyásoló tényezők

Az alábbi szakaszok felsorolják a párhuzamos lekérdezési teljesítményt befolyásoló legfontosabb tényezőket. Ezek olyan általános állítások, amelyek önmagukban nem elegendőek a lekérdezési teljesítmény előrejelzéséhez minden esetben. Mint mindig, fontos az adott lekérdezések tényleges teljesítményének mérése reprezentatív konfigurációkkal és terhelésekkel rendelkező számítógépeken.

  1. A teljes munka számítási költsége.

    A gyorsításhoz a PLINQ-lekérdezésnek elegendő kellemes párhuzamos munkával kell rendelkeznie a többletterhelés eltolásához. A munka kifejezhető az egyes meghatalmazottak számítási költségeivel és a forrásgyűjtemény elemeinek számával megszorozva. Feltételezve, hogy egy művelet párhuzamosítható, minél számításilag drágább, annál nagyobb a gyorsítás lehetősége. Ha például egy függvény végrehajtása egy ezredmásodpercet vesz igénybe, az 1000 elemnél több egymást követő lekérdezés egy másodpercet vesz igénybe a művelet végrehajtásához, míg a négy maggal rendelkező számítógépek párhuzamos lekérdezései csak 250 ezredmásodpercet vesznek igénybe. Ez 750 ezredmásodperc gyorsítást eredményez. Ha a függvénynek egy másodpercre volt szüksége az egyes elemek végrehajtásához, akkor a gyorsítás 750 másodperc lenne. Ha a meghatalmazott nagyon költséges, akkor a PLINQ jelentős gyorsítást kínálhat, és csak néhány elem szerepel a forrásgyűjteményben. Ezzel szemben a kis méretű forrásgyűjtemények kis méretű delegáltakkal általában nem jó jelöltek a PLINQ-ra.

    Az alábbi példában a queryA valószínűleg jó jelölt a PLINQ-hoz, feltéve, hogy a Select függvény sok munkát igényel. A queryB valószínűleg nem jó jelölt, mert nincs elég munka a Select utasításban, és a párhuzamosítás többletterhelése a legtöbb vagy az összes gyorsítást ellensúlyozza.

    Dim queryA = From num In numberList.AsParallel()  
                 Select ExpensiveFunction(num); 'good for PLINQ  
    
    Dim queryB = From num In numberList.AsParallel()  
                 Where num Mod 2 > 0  
                 Select num; 'not as good for PLINQ  
    
    var queryA = from num in numberList.AsParallel()  
                 select ExpensiveFunction(num); //good for PLINQ  
    
    var queryB = from num in numberList.AsParallel()  
                 where num % 2 > 0  
                 select num; //not as good for PLINQ  
    
  2. A rendszer logikai magjainak száma (a párhuzamosság foka).

    Ez a pont az előző szakasz nyilvánvaló társadalma, a kellemesen párhuzamos lekérdezések gyorsabban futnak a több maggal rendelkező gépeken, mert a munka több egyidejű szál között osztható. A gyorsítás teljes mennyisége attól függ, hogy a lekérdezés teljes munkájának hány százaléka párhuzamosítható. Ne feltételezzük azonban, hogy minden lekérdezés kétszer olyan gyorsan fog futni egy nyolc magos számítógépen, mint egy négymagos számítógépen. Az optimális teljesítmény érdekében a lekérdezések finomhangolásakor fontos a különböző magszámú számítógépek tényleges eredményeinek mérése. Ez a pont az 1. ponthoz kapcsolódik: nagyobb adathalmazokra van szükség a nagyobb számítási erőforrások előnyeinek kihasználásához.

  3. A műveletek száma és típusa.

    A PLINQ biztosítja az AsOrdered operátort olyan helyzetekben, amikor szükség van az elemek sorrendjének fenntartására a forrásütemezésben. A megrendeléshez tartozik költség, de ez a költség általában szerény. A GroupBy és a Join műveletek szintén többletterhelést okoznak. A PLINQ akkor működik a legjobban, ha bármilyen sorrendben feldolgozhatja a forrásgyűjtemény elemeit, és amint elkészült, átadja őket a következő operátornak. További információ: Order Preservation in PLINQ.

  4. A lekérdezés végrehajtásának formája.

    Ha egy lekérdezés eredményeit a ToArray vagy a ToList meghívásával tárolja, akkor az összes párhuzamos szál eredményeit össze kell egyesíteni az egyetlen adatstruktúrával. Ez elkerülhetetlen számítási költséggel jár. Hasonlóképpen, ha az eredményeket egy foreach (For Each in Visual Basic) ciklus használatával iterálja, a feldolgozószálak eredményeit szerializálni kell az enumerátorszálra. Ha azonban csak az egyes szálak eredményei alapján szeretne valamilyen műveletet végrehajtani, a ForAll metódus használatával több szálon is elvégezheti ezt a műveletet.

  5. Az egyesítési beállítások típusa.

    A PLINQ konfigurálható úgy, hogy pufferelje a kimenetét, és a teljes eredménykészlet létrehozása után egyszerre vagy darabokban hozza létre, vagy más módon streamelje az egyes eredményeket a létrehozásuk során. Az előbbi a teljes végrehajtási idő csökkenését eredményezi, az utóbbi pedig csökkentett késést eredményez a hozamú elemek között. Bár az egyesítési beállítások nem mindig befolyásolják a lekérdezések általános teljesítményét, hatással lehetnek az észlelt teljesítményre, mert szabályozhatják, hogy a felhasználónak mennyi ideig kell várnia az eredmények megtekintésére. További információ: Egyesítési beállítások a PLINQ-ban.

  6. A particionálás típusa.

    Bizonyos esetekben az indexelhető forrásgyűjteményen keresztüli PLINQ-lekérdezések kiegyensúlyozatlan munkaterhelést eredményezhetnek. Ha ez történik, előfordulhat, hogy egy egyéni particionáló létrehozásával növelheti a lekérdezés teljesítményét. További információ: Egyéni particionálók a PLINQ-hoz és a TPL-hez.

Amikor a PLINQ szekvenciális módot választ

A PLINQ mindig legalább olyan gyorsan megkísérli végrehajtani a lekérdezést, amilyen gyorsan a lekérdezés egymás után futna. Bár a PLINQ nem vizsgálja, hogy a felhasználói meghatalmazottak számításilag mennyire drágák, vagy hogy mekkora a bemeneti forrás, bizonyos lekérdezési "alakzatokat" keres. Pontosabban olyan lekérdezési operátorokat vagy operátorkombinációkat keres, amelyek általában lassabban hajtanak végre lekérdezéseket párhuzamos módban. Ha ilyen alakzatokat talál, a PLINQ alapértelmezés szerint szekvenciális módba kerül vissza.

Egy adott lekérdezés teljesítményének mérése után azonban megállapíthatja, hogy az ténylegesen gyorsabban fut párhuzamos módban. Ilyen esetekben a ParallelExecutionMode.ForceParallelism jelölő használatával utasíthatja a WithExecutionMode PLINQ-t a lekérdezés párhuzamosítására. További információ : A végrehajtási mód megadása a PLINQ-ban.

Az alábbi lista azokat a lekérdezésalakzatokat ismerteti, amelyeket a PLINQ alapértelmezés szerint szekvenciális módban hajt végre:

  • A Select, indexelt Where, indexelt SelectMany vagy ElementAt záradékot tartalmazó lekérdezések egy olyan rendezési vagy szűrési operátor után, amely eltávolította vagy átrendezte az eredeti indexeket.

  • A Take, TakeWhile, Skip, SkipWhile operátort és a forrásütemezésben szereplő indexeket tartalmazó lekérdezések nem az eredeti sorrendben vannak.

  • Zip- vagy SequenceEquals-lekérdezéseket tartalmazó lekérdezések, kivéve, ha az adatforrások egyikének eredetileg rendezett indexe van, a másik adatforrás pedig indexelhető (például tömb vagy IList(T)).

  • A Concatot tartalmazó lekérdezések, kivéve, ha az indexelhető adatforrásokra van alkalmazva.

  • Fordított lekérdezéseket tartalmazó lekérdezések, kivéve, ha indexelhető adatforrásra vonatkoznak.

Lásd még