A számítástechnikában az előtag összege , kumulatív összege , inkluzív letapogatás , vagy csak egy x0, x1, x2, ... számsorozat letapogatása y0, y1, y2, ... számsorozat, amely egy előtag összege a beviteli sorozatból:
y0 = x0 _ _ y 1 = x 0 + x 1 y 2 \ u003d x 0 + x 1 + x 2 …Például a természetes számok előtag összegei háromszög alakú számok :
beírni a számokat | egy | 2 | 3 | négy | 5 | 6 | … |
---|---|---|---|---|---|---|---|
előtag összege | egy | 3 | 6 | tíz | tizenöt | 21 | … |
Az előtag összegei triviálisak a szekvenciális számítási modellekben az y i = y i − 1 + x i képlet alkalmazásával az egyes kimeneti értékek szekvenciális sorrendben történő kiszámításához. Annak ellenére azonban, hogy számításilag egyszerűek, az előtag összegei hasznos primitívek egyes algoritmusokban, mint például a counting sort , [1] [2] , és ezek képezik a funkcionális programozási nyelvek magasabb rendű letapogatási függvényének alapját . Az előtag összegeit párhuzamos algoritmusokban is alaposan tanulmányozták , mind megoldandó tesztproblémaként, mind hasznos primitívként más párhuzamos algoritmusok szubrutinjaként. [3] [4] [5]
Elméletileg az előtag összegéhez csak a ⊕ bináris asszociatív operátorra van szükség , ami számos algoritmusban hasznossá teszi, a jól elkülönített páronkénti pontbontások kiszámításától a karakterlánc-feldolgozásig. [6] [7]
Matematikailag az előtag összegek felvételének művelete végestől végtelen sorozatig általánosítható; ebben az értelemben az előtag összege sorozatrészösszegként ismert . Az előtag összegzése vagy részleges összegzése véges vagy végtelen sorozatok vektortereire lineáris leképezést képez ; inverz operátoraik véges különbségek.
Funkcionális programozási szempontból az előtag összege általánosítható bármely bináris műveletre (nem csak az összeadási műveletre ); az ebből az általánosításból származó magasabb rendű függvényt pásztázásnak nevezzük , és ez szorosan összefügg a konvolúciós művelettel . Mind a vizsgálati, mind az összehasonlítási műveletek egy adott bináris műveletet ugyanarra az értéksorozatra alkalmaznak, de abban különböznek, hogy a vizsgálat a bináris művelet teljes eredménysorát adja vissza, míg a fold csak a végeredményt adja vissza. Például faktorszámok sorozata előállítható természetes számok pásztázásával összeadás helyett szorzással:
beírni a számokat | egy | 2 | 3 | négy | 5 | 6 | … |
---|---|---|---|---|---|---|---|
előtag értékek | egy | 2 | 6 | 24 | 120 | 720 | … |
A szkennelés nyelvi és könyvtári megvalósítása lehet inkluzív vagy exkluzív . Az inkluzív letapogatás az y i kimenet kiszámításakor tartalmazza az x i bemenetet ( ), míg a kizárólagos vizsgálat nem ( ). Az utóbbi esetben az implementációk vagy meghatározatlanul hagyják az y 0 értéket, vagy elfogadnak egy speciális " x -1 " értéket, amellyel elindítják a keresést. Az exkluzív szkennelés általánosabb abban az értelemben, hogy az inkluzív vizsgálat mindig megvalósítható exkluzív vizsgálatként (az x i és az y i utólagos kombinálásával ), de a kizárólagos vizsgálat nem mindig valósítható meg inkluzív vizsgálatként, mint pl. a maximális előtag összegének esete .
Az alábbi táblázat példákat sorol fel a több programozási nyelv és könyvtár által biztosított átfogó és exkluzív szkennelési funkciókra:
Nyelvek/könyvtárak | Inclusive Scan | Exkluzív szkennelés |
---|---|---|
Haskell | scanl1 | scanl |
MPI | MPI_Scan | MPI_Exscan |
C++ | std::inclusive_scan | std::exclusive_scan |
Scala | scan | |
Rozsda | scan Archiválva : 2021. június 6. a Wayback Machine -nél |
Két kulcsfontosságú algoritmus létezik az előtag összegének párhuzamos kiszámítására. Az első módszer kisebb mélységet és nagyobb párhuzamosítási hajlandóságot jelent , de ez a módszer nem elég hatékony. A második lehetőség hatékonyabb, de kétszeres mélységet igényel, és kevesebb lehetőséget kínál a párhuzamosításra. Az alábbiakban mindkét algoritmust bemutatjuk.
Hillis és Steele a következő párhuzamos előtag-összeg algoritmust mutatja be: [8]
tennivalóért _ _ párhuzamosan tenni _ ha akkor másA jelölés az x tömb j -edik elemének értékét jelenti az i lépésben .
Adott n processzornak, hogy a belső hurok minden iterációját állandó időben teljesítse, az algoritmus O (log n ) idő alatt fut.
Egy hatékony párhuzamos előtag összegszámítási algoritmus a következő módon valósítható meg: [3] [9] [10]
Ha a bemeneti szekvencia n méretű, akkor a rekurzió O (log n ) mélységig folytatódik , amit ennek az algoritmusnak a párhuzamos végrehajtási ideje is korlátoz. Az algoritmusműveletek száma O ( n ) , és aszimptotikus lassulás nélkül megvalósíthatók egy absztrakt párhuzamos osztott memóriás (PRAM) számítógépen O ( n /log n ) processzorral , ha algoritmusváltozatokban minden processzorhoz több indexet rendelünk, amelyekhez több elem van, mint processzor. [3]
Az előző algoritmusok mindegyike O (log n ) -ban fut . Az előbbi azonban pontosan log 2 n lépést tesz meg, míg az utóbbi 2 log 2 n − 2 lépést. A bemutatott 16 bemenetes példák esetében az 1. algoritmus 12-párhuzamos (49 munkaegység osztva 4-gyel), míg a 2. algoritmus csak 4-párhuzamos (26 munkaegység osztva 6-tal). Azonban a 2. algoritmus munkahatékony, csak egy állandó tényezőt (2) hajt végre a szekvenciális algoritmus által igényelt munkamennyiségből, az 1. algoritmus pedig nem hatékony, aszimptotikusan több munkát végez (logaritmikus tényező), mint amennyi szekvenciálisan szükséges. Ezért az 1. algoritmus előnyösebb, ha nagyszámú párhuzamos folyamat lehetséges, ellenkező esetben a 2. algoritmus élvez elsőbbséget.
Az előtagösszegekre vonatkozó párhuzamos algoritmusok gyakran általánosíthatók más asszociatív bináris letapogatási műveletekre, [3] [4] , és hatékonyan kiszámolhatók modern párhuzamos hardvereken, például a GPU -n (Graphics Processing Unit). [11] Uzi Vishkin szabadalmaztatta a többparaméteres előtag összegének kiszámítására tervezett hardver funkcionális blokk létrehozásának ötletét . [12]
Számos párhuzamos megvalósítás kétlépcsős eljárást használ, amelyben a részleges előtag összegét az első szakaszban számítják ki minden egyes feldolgozóegységhez; ezeknek a részösszegeknek az előtag összegét azután kiszámítják és visszacsatolják a feldolgozó egységekhez a második lépéshez, a ma ismert előtagot használva magértékként. Aszimptotikusan ez a módszer körülbelül két olvasást és egy írást vesz igénybe minden elemhez.
Az előtag-összeg párhuzamos számítási algoritmusának megvalósításánál, más párhuzamos algoritmusokhoz hasonlóan, figyelembe kell venni a platform párhuzamosítási architektúráját . Számos olyan algoritmus létezik, amely a megosztott memóriaplatformokhoz igazodik , valamint olyan algoritmusok, amelyek jól illeszkednek az elosztott memóriaplatformokhoz , miközben az üzenetküldést használják a folyamatok közötti kommunikáció egyetlen formájaként.
Megosztott memória: kétszintű algoritmusA következő algoritmus egy megosztott memóriagép modellt feltételez ; minden PE feldolgozó elem (az angol feldolgozó elemekből) ugyanahhoz a memóriához fér hozzá. Ennek az algoritmusnak egy változata a Multicore Standard Template Library-ben (MCSTL) [13] [14] valósult meg, amely a C++ Standard Template Library párhuzamos megvalósítása , amely adaptált változatokat biztosít különböző algoritmusok párhuzamos számításaihoz.
Az adatelemek és a feldolgozóelemek előtag összegének egyidejű kiszámításához az adatokat blokkokra osztjuk, amelyek mindegyike tartalmaz elemeket (az egyszerűség kedvéért feltételezzük, hogy osztható -vel ). Felhívjuk figyelmét, hogy bár az algoritmus blokkokra osztja az adatokat , csak a feldolgozó elemek működnek párhuzamosan.
Az első ciklusban minden PE kiszámol egy helyi előtag összegét a blokkjához. Az utolsó blokkot nem kell kiszámítani, mivel ezek az előtag összegek csak a következő blokkok előtagösszegeinek eltolásaként kerülnek kiszámításra, és az utolsó blokk értelemszerűen nem megfelelő.
Az egyes blokkok utolsó pozíciójában tárolt eltolásokat a rendszer a saját előtag összegében gyűjti, és a következő pozíciókban tárolja. Ha kicsi, akkor a szekvenciális algoritmus elég gyorsan fut, nagyoknál ez a lépés párhuzamosan is végrehajtható.
Most térjünk át a második ciklusra. Ezúttal az első blokkot nem kell feldolgozni, mivel nem kell figyelembe vennie az előző blokk eltolását. Most azonban az utolsó blokk is bekerült, és az egyes blokkokhoz tartozó előtagösszegeket az előző ciklusban kiszámított előtagösszeg blokkok eltolásai alapján számítják ki.
function prefix_sum ( elemek ) { n := méret ( elemek ) p := feldolgozó elemek száma prefix_sum : = [ 0 .. .0 ] n méretű _ párhuzamos i = 0 - p - 1 { // i := az aktuális PE indexe j = i * n / ( p + 1 ) - ( i + 1 ) * n / ( p + 1 ) - 1 do { // A helyi blokkok előtag összege itt tárolódik store_prefix_sum_with_offset_in ( elements , 0 , prefix_sum ) } } x = 0 for i = 1 to p { x += prefix_sum [ i * n / ( p + 1 ) - 1 ] // Előtag összegének felépítése az első p blokkra prefix_sum [ i * n / ( p + 1 )] = x / / Eredmények mentése eltolásként való használatra a második ciklusban } párhuzamos i = 1 - p { // i := az aktuális PE indexe j = i * n / ( p + 1 ) - ( i + 1 ) * n / ( p + 1 ) - 1 do { eltolás : = prefix_sum [ i * n / ( p + 1 )] // Az előtag összegének kiszámítása az előző blokkok összegének eltolásaként store_prefix_sum_with_offset_in ( elemek , eltolás , előtag_összeg ) } } return prefix_sum } Elosztott memória: A hiperkocka algoritmusA hypercube prefix sum algoritmus [15] jól adaptálható elosztott memória platformokhoz , és üzenetcserét használ a feldolgozó elemek között. Feltételezzük, hogy az algoritmus a -dimenziós hiperkocka sarkainak számával egyenlő PE-t tartalmaz .
Az algoritmus során minden PE-t egy hipotetikus hiperkocka sarokként kezelünk, a közös előtag összegének ismeretében , valamint az összes elem előtag összegét egészen önmagáig (a PE-k közötti rendezett indexek szerint), mindegyik a sajátjában. hiperkocka.
A PE sarkokkal rendelkező -dimenziós hiperkockában az algoritmust egyszer meg kell ismételni , hogy a nulla dimenziós hiperkockák egydimenziós hiperkockává egyesüljenek. Feltételezve egy duplex kommunikációs modellt , amelyben a különböző hiperkockákban lévő két szomszédos PE mindkét irányban felcserélhető egy kommunikációs lépésben, ez azt jelenti, hogy a kommunikáció elindul.
i : = Saját processzorelem indexe ( PE ) m : = e PE helyi elemeinek előtag összege d : = a hiperkocka méreteinek száma _ _ _ _ x = m ; // Invariáns: PE előtag összege az aktuális beágyazott kockában σ = m ; // Invariáns: az aktuális alkocka összes elemének előtag összege for ( k = 0 ; k <= d - 1 ; k ++ ){ y = σ @ PE ( i xor 2 ^ k ) // Az ellentétes részkocka teljes előtag összegének lekérése a k dimenzióhoz képest σ = σ + y / / Összegzési előtag mindkét beágyazott kocka összege if ( i & 2 ^ k ){ x = x + y // Egy másik beágyazott kockából származó előtag összegének összegzése csak akkor, ha ez a PE magasabb index } } Nagy üzenetméretek: csővezetékes bináris faA Binary Tree Pipeline Algorithm [16] egy másik algoritmus elosztott memóriaplatformokhoz, amely különösen alkalmas nagy üzenetméretekhez.
A hiperkocka algoritmushoz hasonlóan ez is egy speciális kommunikációs struktúrát feltételez. A PE-k hipotetikusan egy bináris fában (pl. Fibonacci-fában) helyezkednek el, a PE-ben lévő indexüknek megfelelő infix számozással . Egy ilyen fában a kommunikáció mindig a szülő és a gyermek csomópontok között történik.
Az infix számozás biztosítja, hogy bármely adott PE j esetén a bal oldali részfa által elérhető összes csomópont indexe kisebb legyen, mint , és a jobb oldali részfa összes csomópontjának indexe nagyobb legyen, mint . A szülő indexe nagyobb, mint a PE j részfa bármelyik indexe, ha PE j a bal gyermek, és kisebb, mint a PE j részfa bármelyik indexe . Ez lehetővé teszi a következő érvelést:
Jegyezze meg a különbséget a részfa-helyi és az általános előtag összege között. A kettes, három és négyes pont körkörös függőséget hozhat létre, de nem. Az alsó szintű PE-k megkövetelhetik a felső szintű PE-k teljes előtag összegét a közös előtag összegének kiszámításához, de a felső szintű PE-k csak a részfa helyi előtagjának összegét igénylik a közös előtag összegének kiszámításához. A gyökércsomópontnak, mint a legmagasabb szintű csomópontnak csak a bal oldali részfájának helyi előtagjának összegére van szüksége a saját előtag összegének kiszámításához. A PE 0 -tól a gyökér PE-ig tartó úton minden PE-nek csak a bal oldali részfájának helyi előtagjának összegére van szüksége a saját előtag összegének kiszámításához, míg a PE p-1- től (utolsó PE) a PE gyökérig tartó úton minden csomópontnak szüksége van a teljes összegre. szülőjének előtag összege, hogy kiszámítsa saját teljes előtag összegét.
Ez egy kétfázisú algoritmushoz vezet:
Növekvő fázis
Egy részfa helyi előtag összegét terjeszti a szülőjére minden PE j esetén .
Lefelé irányuló fázis A PE j címzett részfájában nem szereplő összes alacsonyabb indexű PE
kizárólagos (kizárólagos PE j , valamint a bal oldali részfájában lévő PE) előtag összegének terjesztése a bal oldal alsóbb szintjén lévő PE-kre. a PE j gyermek részfája . A ⊕ [0…j] előtag összegének kiterjesztése a megfelelő gyermek részfára, PE j .
Érdemes megjegyezni, hogy az algoritmus minden PE-n végrehajtódik, és a PE-k megvárják, amíg az összes gyermek/szülő összes csomagja megérkezik.
k := csomagok száma egy üzenetben egy PE m @ { left , right , parent , this } : = // Üzenetek különböző PE - knek x = m @ ezt // Felfelé irányuló fázis - Helyi részfa előtag összegének kiszámítása j = 0 -tól k - 1 -ig : // Csővezeték: üzenetsorozatonként , ha hasLeftChild : blokkolja a fogadást m [ j ] @ left // Helyi m[j] lecserélése vett m[ j-re ] // Kumulatív, inkluzív helyi előtag összege alacsonyabb indexű PE- kből x [ j ] = m [ j ] ⨁ x [ j ] if hasRightChild : blokkolja a fogadást m [ j ] @ right // Nem egyesítjük az m[j]-et egy helyi előtag összegébe, mivel a megfelelő gyermekek magasabb indexű PE - k küldik x [ j ] ⨁ m [ j ] szülőnek else : küldés x [ j ] a szülőnek // Lefelé irányuló fázis j = 0 - tól k - 1 -ig : m [ j ] @ ez = 0 if hasParent : blokkolja a fogadást m [ j ] @ szülő // A bal oldali gyermeknél m[j] a szülő kizárólagos előtag összege, a jobb oldali gyermek esetében a bezáró előtag összege x [ j ] = m [ j ] ⨁ x [ j ] m [ j ] küldése balra // Az összes PE teljes előtag összege, amely kisebb, mint ez vagy a bal oldali részfában lévő bármely PE, x [ j ] küldése jobbra // Az összes PE teljes előtag összege kisebb vagy egyenlő, mint ez a PE SzállításA csővezetékezés akkor alkalmazható, ha a hosszüzenet részekre osztható, és a ⨁ operátor mindegyik ilyen részre külön alkalmazható. [16]
Ha az algoritmust csővezeték nélkül használjuk, akkor csak két réteg (a küldő PE és a fogadó PE) fut a fában egy adott időpontban, míg a többi PE várakozik. Ha szinteket tartalmazó feldolgozóelemekből álló bináris kiegyensúlyozott fát használunk, akkor az útvonal hossza tól -ig , ami megfelel a nem párhuzamos felfelé irányuló kommunikációs műveletek maximális számának. Hasonlóképpen, a lefelé irányuló linkek is ugyanarra az értékre korlátozódnak. Figyelembe véve a kommunikáció kezdési idejét és a bájt átviteli idejét, azt kapjuk, hogy a fázisok időkorlátosak a nem pipeline átvitelben. Ha részekre osztjuk , amelyek mindegyike tartalmaz elemeket, és külön küldi el őket, az első részt a helyi előtag összegének részeként kell átadni, és ez az idő az utolsó részre érvényes, ha .
Más részeken az összes PE párhuzamosan működhet, és minden harmadik interakciós művelet (fogadás a bal oldalon, fogadás jobb oldalon, küldés a szülőnek) csomagot küld a következő szintre, így az egyik fázis elvégezhető az interakciós műveleteknél és mindkettő fázisok együttesen megkövetelik , ami nagyon jó mutatója az üzenet hosszának .
Az algoritmus tovább optimalizálható full duplex vagy távközlési kommunikációs modell használatával , valamint az upstream és downstream fázisok átfedésével. [16]
Ha egy adatkészletet dinamikusan kell frissíteni, akkor az egy Fenwick-fában tárolható . Egy ilyen adatstruktúra nemcsak az előtag összegének logaritmikus időben történő bármely értékének megtalálását teszi lehetővé, hanem a tömb bármely elemének értékének megváltoztatását is. [17] . Mivel az előtag összege kifejezést 1982-ben még nem használták széles körben, megjelent a munka [18] , ahol bevezették a Partial Sum Tree (5.1) nevű adatstruktúrát, amely a Fenwick-fa nevét váltotta fel.
A többdimenziós tömbökön lévő tetszőleges téglalap alakú altömbök összegének kiszámításához az összegzett területek táblázatát egy előtagösszegekre épített adatstruktúra képviseli. Egy ilyen táblázat hasznos lehet képkonvolúciós problémák esetén . [19]
A számláló rendezés egy egész számok rendezési algoritmusa , amely a kulcsfrekvencia hisztogram előtag összegét használja a rendezett kimeneti tömbben lévő egyes kulcsok pozíciójának kiszámításához. Lineáris időben fut az elemek számánál kisebb egész kulcsok esetén, és gyakran használják a radix sort részeként , amely egy gyors algoritmus a kevésbé korlátozott nagyságrendű egész számok rendezésére. [egy]
A lista rangsorolása , az a feladat, hogy egy linkelt listát ugyanazt az elemsorozatot tartalmazó tömbbé alakítsunk , felfogható az egyesek sorozatainak előtagösszegeként, majd az egyes elemeket az előtag értékéből származó tömbbeli pozícióhoz illesztjük. összeg. Számos fontos fa probléma megoldható párhuzamos algoritmusokban a lista rangsorolása, az előtag összegei és az Euler-bejárások kombinálásával . [négy]
Az előtagösszegek párhuzamos számítását bináris összeadók , logikai áramkörök fejlesztésében is használják, amelyek két n bites bináris számot tudnak összeadni. Ebben az esetben az összeadás hordozó bitsorozatot pásztázási műveletként lehet ábrázolni bemeneti bitpárok sorozatán, többségi függvény használatával az adott átvitel és a két bit kombinálására. A kimeneti szám minden bitje megtalálható a két bemeneti bit exkluzív diszjunkciójaként , a megfelelő bittördeléssel. Ily módon lehetséges egy olyan összeadó összeállítása, amely O ( n ) kaput és O (log n ) időlépést használ . [3] [9] [10]
A párhuzamos véletlen hozzáférésű számítástechnikai gép modelljében az előtag összegek használhatók olyan párhuzamos algoritmusok modellezésére, amelyek lehetővé teszik több processzor számára, hogy egyidejűleg ugyanazt a memóriahelyet érjék el párhuzamos gépeken, amelyek tiltják az egyidejű hozzáférést. A rendezési hálózaton keresztül egyidejű memória-hozzáférési kérelmek sorba rendezhetők egy sorozatba, így az ugyanahhoz a cellához való hozzáférés a sorozaton belül szomszédos. A letapogatási műveletek ezután felhasználhatók annak meghatározására, hogy a kért cellákhoz való írási hozzáférések közül melyik sikerült, és a memóriaolvasási műveletek eredményeit több processzor között is szétoszthatjuk, amelyek ugyanazt az eredményt kérik. [húsz]
Guy Blallock doktori értekezésében [21] a párhuzamos előtag-műveletek az olyan gépek által biztosított adatpárhuzamossági modell formalizálásának részét képezik , mint a Connection Machine . A Connection Machine CM-1 és CM-2 egy hiperkocka hálózatot biztosított, amelyben a fent említett 1. algoritmus valósítható meg, míg a CM-5 egy hálózatot biztosított a 2. algoritmus megvalósításához. [22]
Gray-kódok , bináris értéksorozatok készítésekor azzal a tulajdonsággal, hogy az egymást követő sorozatok értékei egy bitpozícióban különböznek egymástól, az n szám az n pozícióban lévő Gray-kód értékévé konvertálható egyszerűen az XOR felvételével. n és n /2 (az a szám , amely az n -nek egy bitpozícióval jobbra való eltolásával keletkezik ). A fordított művelet, amely az x Grey-kódolt értékét bináris számmá dekódolja, bonyolultabb, de kifejezhető az x biteinek előtag összegeként , ahol az előtag összegén belüli minden összegműveletet modulo 2 hajtjuk végre. Az ilyen típusú előtagösszeg hatékonyan végrehajtható a modern számítógépeken elérhető bitenkénti logikai műveletek segítségével, ha egy kizárólagos "vagy"-t vagy x -et számítanak ki az x -et két hatványnyi bittel balra eltoló számok mindegyikével .
A párhuzamos előtag (a szorzást fő asszociációs műveletként használva) gyors párhuzamos polinom interpolációs algoritmusok készítésére is használható . Különösen az interpolációs polinom Newton - formájának különbségének osztási együtthatóinak kiszámítására használható. [23] Ez az előtag-alapú megközelítés általánosított osztott különbségek előállítására is használható (konfluens) Hermite-interpolációhoz , valamint párhuzamos algoritmusokhoz Vandermonde -rendszerekhez .