Gpu:n käyttö. GPU-laskenta

Kun puhutaan GPU:iden rinnakkaislaskennasta, meidän on muistettava, mitä aikaa elämme, tänään on aikaa, jolloin kaikki maailmassa kiihtyy niin paljon, että sinä ja minä menetämme ajantajun, huomaamatta kuinka se rientää ohi. Kaikki mitä teemme liittyy suureen tietojenkäsittelyn tarkkuuteen ja nopeuteen, tällaisissa olosuhteissa tarvitsemme varmasti työkaluja kaiken tiedon käsittelemiseksi ja muuttamiseksi tiedoiksi, lisäksi kun puhumme tällaisista tehtävistä, meidän on muistettava, että nämä tehtävät ovat välttämättömiä ei vain suurille organisaatioille tai megayrityksille, vaan myös tavallisille käyttäjille, jotka ratkaisevat korkeaan teknologiaan liittyvät elämänongelmansa kotona kotona. henkilökohtaiset tietokoneet! NVIDIA CUDA:n ilmestyminen ei ollut yllättävää, vaan pikemminkin perusteltua, sillä pian joudutaan käsittelemään PC:llä huomattavasti aiempaa enemmän aikaa vieviä tehtäviä. Aikaisemmin paljon aikaa vaatineet työt vievät nyt muutaman minuutin, ja vastaavasti tämä vaikuttaa koko maailman kokonaiskuvaan!

Mitä on GPU-laskenta?

GPU-laskenta on GPU:n käyttöä teknisten, tieteellisten ja jokapäiväisten tehtävien laskemiseen. GPU-laskentaan liittyy CPU:n ja GPU:n käyttö heterogeenisen näytteenoton välillä, eli: ohjelmien peräkkäinen osa siirtyy CPU:lle, kun taas aikaa vievät laskentatehtävät jätetään GPU:lle. Tämän ansiosta tapahtuu tehtävien rinnakkaisua, mikä nopeuttaa tiedonkäsittelyä ja lyhentää työn suoritusaikaa, järjestelmästä tulee tuottavampi ja se pystyy samanaikaisesti käsittelemään aiempaa suuremman määrän tehtäviä. Tällaisen menestyksen saavuttamiseksi pelkkä laitteistotuki ei kuitenkaan riitä, vaan tässä tapauksessa tarvitaan myös ohjelmistotuki, jotta sovellus pystyy siirtämään eniten aikaa vievät laskelmat GPU:lle.

Mikä on CUDA

CUDA on teknologia, jolla ohjelmoidaan algoritmeja yksinkertaistetulla C-kielellä, jotka suoritetaan kahdeksannen sukupolven ja vanhempien GeForce-kiihdyttimien grafiikkaprosessoreilla sekä vastaavilla NVIDIA:n Quadro- ja Tesla-korteilla. CUDA mahdollistaa C-ohjelmien sisällyttämisen tekstiin erikoistoiminnot. Nämä toiminnot on kirjoitettu yksinkertaistetulla C-ohjelmointikielellä ja suoritetaan GPU:lla. CUDA SDK:n alkuperäinen versio esiteltiin 15. helmikuuta 2007. Jotta koodi voidaan kääntää onnistuneesti tällä kielellä, CUDA SDK sisältää oman C-kääntäjänsä komentorivi nvcc NVIDIAsta. Nvcc-kääntäjä perustuu avoimeen Open64-kääntäjään ja se on suunniteltu kääntämään isäntäkoodi (pää, ohjauskoodi) ja laitekoodi (laitteistokoodi) (tiedostot, joiden pääte on .cu) objektitiedostoiksi, jotka soveltuvat lopullisen ohjelman tai kirjaston kokoamiseen. mikä tahansa ohjelmointiympäristö, kuten Microsoft Visual Studio.

Tekniset ominaisuudet

  1. Normaali C-kieli GPU:iden rinnakkaissovelluskehitykseen.
  2. Valmiit numeeriset analyysikirjastot nopeaan Fourier-muunnokseen ja lineaarisen algebran perusohjelmistopakettiin.
  3. Erityinen CUDA-ohjain tietojenkäsittelyyn nopealla tiedonsiirrolla GPU:n ja CPU:n välillä.
  4. Kyky liittää CUDA-ohjain OpenGL- ja DirectX-näytönohjaimien kanssa.
  5. Leikkaussalin tuki Linux-järjestelmät 32/64-bittinen, Windows XP 32/64-bittinen ja MacOS.

Tekniikan edut

  1. CUDA-sovellusliittymä (CUDA API) perustuu standardi C-ohjelmointikieleen tietyin rajoituksin. Tämä yksinkertaistaa ja tasoittaa CUDA-arkkitehtuurin oppimisprosessia.
  2. Säikeiden välistä 16 kt:n jaettua muistia voidaan käyttää käyttäjän järjestämään välimuistiin, jonka kaistanleveys on suurempi kuin tavallisista tekstuurista haettaessa.
  3. Tehokkaammat tapahtumat suorittimen muistin ja videomuistin välillä.
  4. Täysi laitteistotuki kokonaisluku- ja bittikohtaisille operaatioille.

Esimerkki tekniikan sovelluksesta

cRark

Tämän ohjelman aikaa vievin osa on tinktuura. Ohjelmassa on konsolikäyttöliittymä, mutta itse ohjelman mukana tulevien ohjeiden ansiosta voit käyttää sitä. Seuraava on lyhyet ohjeet ohjelman asettamista varten. Testaamme ohjelman toimivuutta ja vertaamme sitä toiseen vastaavaan ohjelmaan, joka ei käytä NVIDIA CUDA:ta, tässä tapauksessa tunnettuun "Advanced Archive Password Recovery" -ohjelmaan.

Ladatusta cRark-arkistosta tarvitsemme vain kolme tiedostoa: crark.exe, crark-hp.exe ja password.def. Crerk.exe on konsoliapuohjelma RAR 3.0 -salasanojen avaamiseen ilman salattuja tiedostoja arkiston sisällä (eli arkiston avaamisen yhteydessä näemme nimet, mutta emme voi purkaa arkistoa ilman salasanaa).

Crerk-hp.exe on konsoliapuohjelma RAR 3.0 -salasanojen avaamiseen koko arkiston salauksella (eli arkiston avaamisen yhteydessä emme näe nimeä tai itse arkistoja emmekä voi purkaa arkistoa ilman salasanaa).

Password.def on mikä tahansa uudelleennimetty tekstitiedosto, jossa on hyvin vähän sisältöä (esimerkiksi: 1. rivi: ## 2. rivi: ?* , tässä tapauksessa salasana murretaan käyttämällä kaikkia merkkejä). Password.def on cRark-ohjelman johtaja. Tiedosto sisältää säännöt salasanan murtamiseen (tai merkkialueen, jota crark.exe käyttää työssään). Tarkemmat tiedot näiden merkkien valintamahdollisuuksista kirjoitetaan tekstitiedostoon, joka saadaan avattaessa cRark-ohjelman tekijän verkkosivustolta ladattu tiedosto: russian.def.

Valmistautuminen

Sanon heti, että ohjelma toimii vain, jos näytönohjain perustuu GPU:hun, joka tukee CUDA 1.1 -kiihtystasoa. Joten sarjaa G80-siruun perustuvia näytönohjaimia, kuten GeForce 8800 GTX, ei enää tarvita, koska niillä on laitteistotuki CUDA 1.0 -kiihdytykseen. Ohjelma valitsee vain salasanat RAR-arkistojen versioille 3.0+ käyttäen CUDAa. Kaikki on asennettava ohjelmisto liittyvät CUDAan, nimittäin:

Luomme minkä tahansa kansion mihin tahansa paikkaan (esimerkiksi C:-asemaan) ja kutsumme sitä millä tahansa nimellä, esimerkiksi "3.2". Sijoitamme tiedostot sinne: crark.exe, crark-hp.exe ja password.def sekä salasanalla suojattu/salattu RAR-arkisto.

Seuraavaksi sinun pitäisi käynnistää komentokonsoli Windowsin merkkijonot ja siirry luotuun kansioon. SISÄÄN Windows Vista ja 7, sinun tulee kutsua "Käynnistä"-valikko ja kirjoittaa "cmd.exe" hakukenttään; Windows XP:ssä "Käynnistä"-valikosta sinun tulee ensin kutsua "Suorita"-valintaikkuna ja kirjoittaa "cmd.exe". sen sisällä. Kun olet avannut konsolin, anna komento, kuten: cd C:\folder\, cd C:\3.2 tässä tapauksessa.

Rekrytointi klo tekstieditori kaksi riviä (voit myös tallentaa tekstin .bat-tiedostona kansioon cRarkin avulla) arvataksesi salasanalla suojatun RAR-arkiston, jossa on salaamattomia tiedostoja:

kaiku pois;
cmd /K crark (arkiston nimi).rar

salasanalla suojatun ja salatun RAR-arkiston salasanan arvaaminen:

kaiku pois;
cmd /K crark-hp (arkiston nimi).rar

Kopioi 2 riviä tekstitiedostosta konsoliin ja paina Enter (tai suorita .bat-tiedosto).

tuloksia

Salauksen purkuprosessi on esitetty kuvassa:

Arvausnopeus cRarkilla CUDA:lla oli 1625 salasanaa sekunnissa. Minuutissa 36 sekunnissa valittiin 3-merkkinen salasana: "q)$." Vertailun vuoksi: Advanced Archive Password Recovery -sovelluksen hakunopeus kaksiytimisessä Athlon 3000+ -prosessorissani on enintään 50 salasanaa sekunnissa ja haun olisi pitänyt kestää 5 tuntia. Toisin sanoen RAR-arkiston bruteforce-valinta cRarkissa GeForce 9800 GTX+ -näytönohjaimella on 30 kertaa nopeampi kuin suorittimella.

Niille, joilla on Intel-prosessori, hyvä emolevy korkealla taajuudella järjestelmäväylä(FSB 1600 MHz), suorittimen nopeus ja hakunopeus ovat korkeammat. Ja jos sinulla on neliytiminen prosessori ja pari GeForce 280 GTX -tason näytönohjainta, raakojen salasanojen nopeus nopeutuu huomattavasti. Yhteenvetona esimerkistä on sanottava, että tämä ongelma ratkaistiin CUDA-tekniikalla vain 2 minuutissa 5 tunnin sijaan, mikä osoittaa tämän tekniikan suuren potentiaalin!

johtopäätöksiä

Tutkittuamme CUDA:n rinnakkaislaskennan teknologiaa tänään, näimme selvästi kaiken tehon ja valtavan potentiaalin tämän tekniikan kehittämiseen käyttämällä esimerkkiä salasanan palautusohjelmasta. RAR-arkistot. On sanottava tämän tekniikan näkymistä, tätä tekniikkaa löytää varmasti paikkansa jokaisen sitä käyttää päättävän ihmisen elämässä, olipa kyseessä sitten tieteelliset tehtävät tai videonkäsittelyyn liittyvät tehtävät tai jopa taloudelliset tehtävät, jotka vaativat nopeita, tarkkoja laskelmia, kaikki tämä johtaa väistämättä työvoiman lisääntymiseen tuottavuutta, jota ei voida jättää huomiotta. Tänään ilmaus "koti supertietokone" alkaa jo tulla sanakirjaan; On täysin selvää, että jokaisessa kodissa on jo CUDA-niminen työkalu tällaisen esineen toteuttamiseksi. G80-siruun perustuvien korttien julkaisun jälkeen (2006) on julkaistu valtava määrä NVIDIA-pohjaisia ​​kiihdyttimiä, jotka tukevat CUDA-tekniikkaa, joka voi toteuttaa unelmia supertietokoneista jokaisessa kodissa. Edistämällä CUDA-teknologiaa NVIDIA kohottaa auktoriteettiaan asiakkaiden silmissä tarjoamalla lisäominaisuuksia laitteet, jotka monet ovat jo ostaneet. Voimme vain uskoa, että CUDA kehittyy pian hyvin nopeasti ja antaa käyttäjille mahdollisuuden hyödyntää kaikkia GPU:iden rinnakkaislaskennan ominaisuuksia.

Kehittäjän tulee oppia käyttämään laitteen grafiikkasuoritusyksikköä (GPU) tehokkaasti, jotta sovellus ei hidastu tai tee turhaa työtä.

Määritä GPU-renderöintiasetukset

Jos sovelluksesi on hidas, se tarkoittaa, että joidenkin tai kaikkien näytön päivityskehysten päivittäminen kestää yli 16 millisekuntia. Jos haluat nähdä visuaalisesti kehyspäivitykset näytöllä, voit ottaa käyttöön erityisen vaihtoehdon laitteessa (Profile GPU Rendering).

Näet nopeasti, kuinka kauan kehysten hahmontamiseen kuluu. Haluan muistuttaa, että sinun on säilytettävä se 16 millisekunnin sisällä.

Vaihtoehto on käytettävissä laitteissa, joiden käyttöjärjestelmä on Android 4.1. Kehittäjätilan on oltava aktivoituna laitteessa. Laitteissa, joiden versio on 4.2 tai uudempi, tila on oletuksena piilotettu. Aktivoidaksesi mene osoitteeseen Asetukset | Puhelimesta ja napsauta riviä seitsemän kertaa Rakennusnumero.

Siirry aktivoinnin jälkeen kohtaan Kehittäjäasetukset ja löytää pointti Määritä GPU-renderöintiasetukset(Profiilin GPU-renderöinti), jonka pitäisi olla käytössä. Valitse ponnahdusikkunassa vaihtoehto Näytöllä sarakkeiden muodossa(Näytössä palkkeina). Tässä tapauksessa kaavio näytetään käynnissä olevan sovelluksen päällä.

Voit testata sovelluksesi lisäksi myös muita. Käynnistä mikä tahansa sovellus ja aloita työskentely sen kanssa. Kun työskentelet, näet päivitetyn kaavion näytön alareunassa. Vaaka-akseli edustaa kulunutta aikaa. Pystyakselilla näkyy kunkin kehyksen aika millisekunteina. Kun olet vuorovaikutuksessa sovelluksen kanssa, pystysuorat raidat on piirretty näytölle, näkyvät vasemmalta oikealle ja näyttävät kehysten suorituskyvyn ajan kuluessa. Jokainen tällainen sarake edustaa yhtä kehystä näytön piirtämiseen. Mitä korkeampi sarakkeen korkeus, sitä kauemmin piirtäminen vie aikaa. Ohut vihreä viiva on ohjeellinen ja vastaa 16 millisekuntia kehystä kohden. Siksi sinun on pyrittävä varmistamaan, että kaavio ei eksy tämän viivan yli, kun tutkit sovellustasi.

Katsotaanpa suurempaa versiota kaaviosta.

Vihreä viiva vastaa 16 millisekuntia. Pysyäksesi 60 kuvan sisällä sekunnissa, jokainen kaaviopalkki on piirrettävä tämän viivan alle. Jossain vaiheessa sarake on liian suuri ja on paljon korkeampi kuin vihreä viiva. Tämä tarkoittaa, että ohjelma hidastuu. Jokaisessa sarakkeessa on syaania, violettia (Lollipop ja enemmän), punaista ja oranssia.

Sininen väri vastaa luomiseen ja päivittämiseen käytetystä ajasta Näytä.

Violetti osa edustaa aikaa, joka kuluu säikeen renderöintiresurssien siirtämiseen.

Punainen väri edustaa piirtämisaikaa.

Oranssi väri osoittaa, kuinka kauan suorittimella kesti odottaa, että GPU valmistui. Tämä on ongelmien lähde suurilla arvoilla.

GPU:n kuormituksen vähentämiseksi on olemassa erityisiä tekniikoita.

Debug GPU overdraw -ilmaisin

Toinen asetus kertoo, kuinka usein sama osa näytöstä piirretään uudelleen (eli ylimääräistä työtä tehdään). Mennään taas Kehittäjäasetukset ja löytää pointti Debug GPU overdraw -ilmaisin(Debug GPU Overdraw), jonka pitäisi olla käytössä. Valitse ponnahdusikkunassa vaihtoehto Näytä peittoalueet(Näytä ylikytketyt alueet). Älä pelkää! Jotkut näytön elementit vaihtavat väriä.

Palaa mihin tahansa sovellukseen ja katso sen toimintaa. Väri osoittaa ongelma-alueet sovelluksessasi.

Jos sovelluksen väri ei ole muuttunut, kaikki on kunnossa. Yhtä väriä ei kerrota toisen päälle.

Sininen väri osoittaa, että yksi kerros piirretään alla olevan kerroksen päälle. Hieno.

Vihreä väri - piirretty uudelleen kahdesti. Kannattaa miettiä optimointia.

Pinkki väri - piirretty uudelleen kolme kertaa. Kaikki on erittäin huonosti.

Punainen väri - piirretty uudelleen monta kertaa. Jotain meni pieleen.

Voit tarkistaa hakemuksesi itse löytääksesi ongelmakohdat. Luo aktiviteetti ja aseta siihen komponentti TextView. Anna juurielementille ja tekstitunnisteelle taustaa attribuutissa android: tausta. Saat seuraavat asiat: ensin maalasit alimman kerroksen yhdellä värillä. Sitten se piirretään sen päälle uusi kerros alkaen TextView. Muuten itse asiassa TextView tekstiä myös piirretään.

Joissakin kohdissa päällekkäisiä värejä ei voida välttää. Mutta kuvittele, että asetat luettelon taustan samalla tavalla Listanäkymä, joka kattaa koko toiminta-alueen. Järjestelmä suorittaa kaksinkertaisen tehtävän, vaikka käyttäjä ei koskaan näe toiminnan alinta kerrosta. Ja jos lisäksi luot oman merkinnän jokaiselle listan elementille omalla taustallaan, saat yleensä ylikuormituksen.

Pieni neuvo. Paikka menetelmän jälkeen setContentView() kutsumalla menetelmää, joka poistaa näytön maalaamisesta teemavärillä. Tämä auttaa poistamaan yhden ylimääräisen värin:

GetWindow().setBackgroundDrawable(null);

Nykyään uutisia GPU:iden käytöstä yleisessä tietojenkäsittelyssä kuullaan joka kulmasta. Sanoista, kuten CUDA, Stream ja OpenCL, on tullut melkein eniten siteerattuja sanoja IT-Internetissä vain kahdessa vuodessa. Kaikki eivät kuitenkaan tiedä, mitä nämä sanat tarkoittavat ja mitä niiden takana oleva teknologia tarkoittaa. Ja Linux-käyttäjille, jotka ovat tottuneet "lennossa olemiseen", kaikki tämä näyttää pimeältä metsältä.

GPGPU:n syntymä

Olemme kaikki tottuneet ajattelemaan, että tietokoneen ainoa komponentti, joka pystyy suorittamaan mitä tahansa koodia, joka sen käsketään tekemään, on keskusprosessori. Pitkän aikaa lähes kaikki massamarkkinoiden PC:t oli varustettu yhdellä prosessorilla, joka käsitteli kaikki mahdolliset laskelmat, mukaan lukien koodi käyttöjärjestelmä, kaikki ohjelmistomme ja virukset.

Myöhemmin ilmestyi moniytimiset prosessorit ja moniprosessorijärjestelmät, joissa oli useita tällaisia ​​komponentteja. Tämän ansiosta koneet pystyivät suorittamaan useita tehtäviä samanaikaisesti, ja järjestelmän yleinen (teoreettinen) suorituskyky kasvoi täsmälleen yhtä paljon kuin koneeseen asennettujen ytimien määrä. Kävi kuitenkin ilmi, että moniytimisprosessorien valmistaminen ja suunnittelu oli liian vaikeaa ja kallista.

Jokaisessa ytimessä oli oltava monimutkaisen ja monimutkaisen x86-arkkitehtuurin täysimittainen prosessori, jolla oli oma (melko suuri) välimuisti, käskyputkisto, SSE-lohkot, monet optimointia suorittavat lohkot jne. ja niin edelleen. Siksi ytimien määrän lisäämisprosessi hidastui merkittävästi ja valkoiset yliopistotakit, joille kaksi tai neljä ydintä ei selvästikään riittänyt, löysivät tavan käyttää tieteellisiin laskelmiinsa muuta laskentatehoa, jota näytönohjaimella oli runsaasti. (Tämän seurauksena jopa BrookGPU-työkalu ilmestyi, emuloimalla ylimääräistä prosessoria DirectX- ja OpenGL-toimintokutsujen avulla).

Grafiikkaprosessorit, joilla ei ollut monia keskusprosessorin haittoja, osoittautuivat erinomaiseksi ja erittäin nopeaksi laskentakoneeksi, ja hyvin pian GPU-valmistajat itse alkoivat tarkastella lähemmin tieteellisten mielien kehitystä (ja nVidia palkkasi eniten tutkijoista työskentelemään heille). Tämän seurauksena ilmestyi nVidia CUDA -tekniikka, joka määrittelee rajapinnan, jonka avulla monimutkaisten algoritmien laskeminen oli mahdollista siirtää GPU:n harteille ilman kainalosauvoja. Myöhemmin sitä seurasi ATi (AMD) omalla versiollaan teknologiasta nimeltä Close to Metal (nykyään Stream), ja hyvin pian ilmestyi Applen standardiversio, nimeltään OpenCL.

Onko GPU kaikki kaikessa?

Kaikista eduista huolimatta GPGPU-tekniikalla on useita ongelmia. Ensimmäinen niistä on erittäin kapea soveltamisala. GPU:t ovat menneet paljon keskusprosessorin edellä laskentatehon ja ytimien kokonaismäärän lisäämisessä (näytönohjaimet kuljettavat yli sadasta ytimestä koostuvan laskentayksikön), mutta niin suuri tiheys saavutetaan maksimoimalla suunnittelun yksinkertaistaminen. itse sirusta.

Pohjimmiltaan GPU:n päätehtävä rajoittuu matemaattisiin laskelmiin, joissa käytetään yksinkertaisia ​​algoritmeja, jotka vastaanottavat syötteenä ei kovin suuria määriä ennustettavaa dataa. Tästä syystä GPU-ytimillä on hyvin yksinkertainen rakenne, niukat välimuistin koot ja vaatimaton ohjesarja, mikä johtaa lopulta niiden alhaisiin tuotantokustannuksiin ja mahdollisuuteen sijoittaa sirulle erittäin tiheästi. GPU:t ovat kuin kiinalainen tehdas, jossa on tuhansia työntekijöitä. He tekevät joitain yksinkertaisia ​​asioita melko hyvin (ja mikä tärkeintä, nopeasti ja halvalla), mutta jos uskot lentokoneen kokoamisen heidän tehtäväkseen, tuloksena on korkeintaan riippuliito.

Siksi GPU:iden ensimmäinen rajoitus on niiden keskittyminen nopeisiin matemaattisiin laskelmiin, mikä rajoittaa GPU:iden käyttöalueen avustamaan multimediasovellusten toimintaa sekä kaikkia ohjelmia, jotka osallistuvat monimutkaiseen tietojenkäsittelyyn (esimerkiksi arkistaattorit tai salausjärjestelmät). , sekä ohjelmistot, jotka liittyvät fluoresenssimikroskopiaan, molekyylidynamiikkaan, sähköstatiikkaan ja muihin Linux-käyttäjiä vähän kiinnostaviin asioihin).

Toinen GPGPU:n ongelma on, että kaikkia algoritmeja ei voida mukauttaa suoritettavaksi GPU:ssa. Yksittäiset ytimet GPU Ne ovat melko hitaita, ja niiden voima ilmenee vain yhdessä toimiessa. Tämä tarkoittaa, että algoritmi on yhtä tehokas kuin ohjelmoija voi rinnastaa sen tehokkaasti. Useimmiten vain hyvä matemaatikko pystyy käsittelemään sellaista työtä, jonka ohjelmistokehittäjiä on hyvin vähän.

Ja kolmanneksi: GPU:t toimivat itse näytönohjaimelle asennetun muistin kanssa, joten joka kerta kun grafiikkasuoritusta käytetään, tapahtuu kaksi ylimääräistä kopiointitoimintoa: syöttötiedot RAM-muisti itse sovellus ja tulos GRAMista takaisin sovellusmuistiin. Kuten voit kuvitella, tämä voi mitätöidä kaikki hyödyt sovelluksen ajon aikana (kuten FlacCL-työkalun tapauksessa, jota tarkastelemme myöhemmin).

Mutta siinä ei vielä kaikki. Huolimatta yleisesti hyväksytyn standardin olemassaolosta OpenCL:n muodossa, monet ohjelmoijat haluavat edelleen käyttää toimittajakohtaisia ​​GPGPU-tekniikan toteutuksia. Erityisen suosituksi osoittautui CUDA, joka, vaikka se tarjoaakin joustavamman ohjelmointirajapinnan (muuten, OpenCL in nVidia ajurit toteutettu CUDA:n päälle), mutta sitoo sovelluksen tiukasti yhden valmistajan näytönohjainkortteihin.

GPU:n kiihdytetty KGPU- tai Linux-ydin

Utahin yliopiston tutkijat ovat kehittäneet KGPU-järjestelmän, joka mahdollistaa joidenkin Linux-ytimen toimintojen suorittamisen GPU:ssa CUDA-kehyksen avulla. Tämän tehtävän suorittamiseen käytetään muokattua Linux-ydintä ja erityistä demonia, joka toimii käyttäjätilassa, kuuntelee ytimen pyyntöjä ja välittää ne näytönohjainohjaimelle CUDA-kirjaston avulla. Mielenkiintoista on, että huolimatta tällaisen arkkitehtuurin aiheuttamasta merkittävästä ylikuormituksesta, KGPU:n kirjoittajat onnistuivat luomaan AES-algoritmin toteutuksen, joka lisää salausnopeutta. tiedostojärjestelmä eCryptfs 6 kertaa.

Mitä siellä nyt on?

GPGPU:sta ei ole nuoruutensa ja yllä kuvattujen ongelmien vuoksi koskaan tullut todella laajalle levinnyttä tekniikkaa, mutta sen ominaisuuksia hyödyntäviä hyödyllisiä ohjelmistoja on olemassa (tosin pieniä määriä). Ensimmäisten joukossa ilmestyivät eri hash-krakkerit, joiden algoritmit on erittäin helppo rinnastaa.

Syntyi myös multimediasovelluksia, kuten FlacCL-enkooderi, joka mahdollistaa transkoodauksen ääniraita V FLAC-muoto. Jotkut aiemmat sovellukset ovat myös saaneet GPGPU-tuen, joista merkittävin on ImageMagick, joka voi nyt siirtää osan työstään GPU:lle OpenCL:n avulla. On myös projekteja tiedon arkistointilaitteiden ja muiden tiedonpakkausjärjestelmien siirtämiseksi CUDA/OpenCL:ään (ATi Unixoidista ei pidetä). Tarkastelemme mielenkiintoisimpia näistä projekteista artikkelin seuraavissa osissa, mutta yritetään nyt selvittää, mitä tarvitsemme, jotta se kaikki käynnistyisi ja toimisi vakaasti.

GPU:t ovat jo pitkään ylittäneet x86-prosessorit suorituskyvyltään

· Toiseksi, järjestelmään on asennettava näytönohjaimen uusimmat omat ajurit, jotka tukevat sekä kortille ominaisia ​​GPGPU-tekniikoita että avointa OpenCL:ää.

· Ja kolmanneksi, koska jakelukehittäjät eivät ole vielä alkaneet jakaa sovelluspaketteja GPGPU-tuella, meidän on rakennettava sovelluksia itse, ja tätä varten tarvitsemme valmistajien virallisia SDK:ita: CUDA Toolkit tai ATI Stream SDK. Ne sisältävät otsikkotiedostot ja kirjastot, joita tarvitaan sovellusten rakentamiseen.

Asenna CUDA Toolkit

Seuraa yllä olevaa linkkiä ja lataa CUDA Toolkit for Linux (voit valita useista versioista, Fedora-, RHEL-, Ubuntu- ja SUSE-jakeluille on versioita sekä x86- että x86_64-arkkitehtuureille). Lisäksi sinun on ladattava siellä myös kehittäjille tarkoitettuja ajuripaketteja (Developer Drivers for Linux, ne ovat luettelossa ensimmäisinä).

Käynnistä SDK-asennusohjelma:

$ sudo sh cudatoolkit_4.0.17_linux_64_ubuntu10.10.run

Kun asennus on valmis, jatkamme ohjainten asentamiseen. Voit tehdä tämän sammuttamalla X-palvelimen:

# sudo /etc/init.d/gdm stop

Avaa konsoli ja suorita ohjaimen asennusohjelma:

$ sudo sh devdriver_4.0_linux_64_270.41.19.run

Kun asennus on valmis, käynnistä X:

Jotta sovellukset voisivat toimia CUDA/OpenCL:n kanssa, asetamme LD_LIBRARY_PATH-muuttujan polun CUDA-kirjastojen hakemistoon:

$ vienti LD_LIBRARY_PATH=/usr/local/cuda/lib64

Tai jos asensit 32-bittisen version:

$ vienti LD_LIBRARY_PATH=/usr/local/cuda/lib32

Sinun on myös määritettävä polku CUDA-otsikkotiedostoihin, jotta kääntäjä löytää ne sovelluksen rakennusvaiheessa:

$ vienti C_INCLUDE_PATH=/usr/local/cuda/include

Siinä kaikki, nyt voit aloittaa CUDA/OpenCL-ohjelmiston rakentamisen.

Asenna ATI Stream SDK

Stream SDK ei vaadi asennusta, joten verkkosivulta ladattu AMD-arkisto voidaan yksinkertaisesti purkaa mihin tahansa hakemistoon ( paras valinta on /opt) ja kirjoita polku siihen samaan LD_LIBRARY_PATH-muuttujaan:

$ wget http://goo.gl/CNCNo

$ sudo tar -xzf ~/AMD-APP-SDK-v2.4-lnx64.tgz -C /opt

$ vienti LD_LIBRARY_PATH=/opt/AMD-APP-SDK-v2.4-lnx64/lib/x86_64/

$ vienti C_INCLUDE_PATH=/opt/AMD-APP-SDK-v2.4-lnx64/include/

Kuten CUDA Toolkitissä, x86_64 on korvattava x86:lla 32-bittisissä järjestelmissä. Siirry nyt juurihakemistoon ja pura icd-registration.tgz-arkisto (tämä on eräänlainen ilmainen lisenssiavain):

$ sudo tar -xzf /opt/AMD-APP-SDK-v2.4-lnx64/icd-registration.tgz - KANSSA /

Tarkistamme paketin oikean asennuksen/toiminnan clinfo-työkalulla:

$ /opt/AMD-APP-SDK-v2.4-lnx64/bin/x86_64/clinfo

ImageMagick ja OpenCL

OpenCL-tuki on ollut saatavilla ImageMagickissa jo jonkin aikaa, mutta se ei ole oletuksena käytössä missään jakelussa. Siksi meidän on käännettävä pikaviestit itse lähteestä. Tässä ei ole mitään monimutkaista, kaikki tarvitsemasi on jo SDK:ssa, joten kokoonpano ei vaadi lisäkirjastojen asentamista nVidiasta tai AMD:stä. Joten lataa / pura arkisto lähteiden kanssa:

$ wget http://goo.gl/F6VYV

$ tar -xjf ImageMagick-6.7.0-0.tar.bz2

$ cd ImageMagick-6.7.0-0

$ sudo apt-get install build-essential

Käynnistämme konfiguraattorin ja nappaamme sen tulosteen OpenCL-tukea varten:

$ LDFLAGS=-L$LD_LIBRARY_PATH ./konfiguraatio | grep -e cl.h -e OpenCL

Komennon oikean lähdön pitäisi näyttää tältä:

CL/cl.h:n käytettävyyden tarkistaminen... kyllä

CL/cl.h:n läsnäolon tarkistaminen... kyllä

CL/cl.h tarkistus... kyllä

OpenCL/cl.h:n käytettävyyden tarkistaminen... ei

tarkastetaan OpenCL/cl.h läsnäolo... ei

tarkistaa OpenCL/cl.h... ei

tarkistetaan OpenCL-kirjastoa... -lOpenCL

Sana "kyllä" on merkittävä joko kolmelle ensimmäiselle riville tai toiselle (tai molemmille vaihtoehdoille kerralla). Jos näin ei ole, C_INCLUDE_PATH-muuttujaa ei todennäköisesti alustettu oikein. Jos viimeinen rivi on merkitty sanalla "ei", ongelma on muuttujassa LD_LIBRARY_PATH. Jos kaikki on kunnossa, aloita rakennus-/asennusprosessi:

$ sudo tee asennus puhtaana

Tarkistamme, että ImageMagick on todella käännetty OpenCL-tuella:

$ /usr/local/bin/convert -version | grep-ominaisuudet

Ominaisuudet: OpenMP OpenCL

Mittaa nyt saatu nopeusvahvistus. ImageMagick-kehittäjät suosittelevat convolve-suodattimen käyttöä tähän:

$ aika /usr/bin/convert image.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" image2.jpg

$ aika /usr/local/bin/convert image.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" image2.jpg

Joidenkin muiden toimintojen, kuten koon muuttamisen, pitäisi nyt myös toimia paljon nopeammin, mutta sinun ei pitäisi odottaa ImageMagickin aloittavan grafiikan käsittelyn huimaa vauhtia. Toistaiseksi hyvin pieni osa paketista on optimoitu OpenCL:llä.

FlacCL (Flacuda)

FlacCL on FLAC-muotoisten äänitiedostojen enkooderi, joka käyttää työssään OpenCL:n ominaisuuksia. Se sisältyy Windowsin CUETools-pakettiin, mutta monon ansiosta sitä voidaan käyttää myös Linuxissa. Jos haluat hankkia arkiston kooderin kanssa, suorita seuraava komento:

$ mkdir flaccl && cd flaccl

$ wget www.cuetools.net/install/flaccl03.rar

$ sudo apt-get install unrar mono

$ unrar x fl accl03.rar

Jotta ohjelma voi löytää OpenCL-kirjaston, teemme symbolisen linkin:

$ ln -s $LD_LIBRARY_PATH/libOpenCL.so libopencl.so

Suoritetaan nyt kooderi:

$ mono CUETools.FLACCL.cmd.exe music.wav

Jos näytöllä näkyy virheilmoitus "Virhe: Pyydetty käännöskoko on suurempi kuin vaadittu työryhmän koko 32", järjestelmämme näytönohjain on liian heikko ja mukana olevien ytimien määrä tulisi vähentää määritettyyn määrään. käyttämällä '---lippua ryhmäkoko XX', jossa XX on tarvittava määrä ytimiä.

Sanon heti, että OpenCL:n pitkän alustusajan vuoksi havaittavia voittoja voidaan saada vain riittävän pitkillä raiteilla. Lyhyt äänitiedostoja FlacCL käsittelee lähes samalla nopeudella kuin perinteinen versio.

oclHashcat tai nopea raaka voima

Kuten jo totesin, erilaisten krakkauslaitteiden ja raa'an voiman salasanajärjestelmien kehittäjät olivat ensimmäisten joukossa lisänneet GPGPU-tuen tuotteisiinsa. Heille uudesta tekniikasta tuli todellinen pyhä malja, joka mahdollisti luonnollisesti helposti rinnastettavan koodin siirtämisen nopeiden GPU-prosessorien harteille. Siksi ei ole yllättävää, että tällaisia ​​ohjelmia on nyt kymmeniä erilaisia ​​toteutuksia. Mutta tässä artikkelissa puhun vain yhdestä niistä - oclHashcatista.

oclHashcat on hakkeri, joka voi arvata salasanoja tiivisteensä perusteella äärimmäisen hyvin suuri nopeus, samalla kun hyödynnät GPU:n tehoa OpenCL:n avulla. Jos uskot projektin verkkosivuilla julkaistuja mittauksia, MD5-salasanojen valintanopeus nVidia GTX580:lla on jopa 15 800 miljoonaa yhdistelmää sekunnissa, minkä ansiosta oclHashcat pystyy löytämään keskimääräisen monimutkaisen kahdeksan merkin salasanan vain 9 minuutissa.

Ohjelma tukee OpenCL- ja CUDA-, MD5-, md5($pass.$salt), md5(md5($pass)), vBulletin-algoritmeja< v3.8.5, SHA1, sha1($pass.$salt), хэши MySQL, MD4, NTLM, Domain Cached Credentials, SHA256, поддерживает распределенный подбор паролей с задействованием мощности нескольких машин.

7z $ x oclHashcat-0.25.7z

$cd oclHashcat-0,25

Ja suorita ohjelma (käytämme tiivisteluetteloa ja esimerkkisanakirjaa):

$ ./oclHashcat64.bin esimerkki.hash ?l?l?l?l esimerkki.dict

oclHashcat avaa käyttäjäsopimuksen tekstin, joka sinun on hyväksyttävä kirjoittamalla "KYLLÄ". Tämän jälkeen alkaa hakuprosessi, jonka eteneminen selviää painamalla . Voit keskeyttää prosessin napsauttamalla

Jatkaa - . Voit myös käyttää suoraa luettelointia (esimerkiksi aaaaaaaa - zzzzzzzz):

$ ./oclHashcat64.bin hash.txt ?l?l?l?l ?l?l?l?l

Ja sanakirjan ja suorahakumenetelmän erilaiset muunnelmat sekä niiden yhdistelmät (voit lukea tästä tiedostosta docs/examples.txt). Minun tapauksessani hakunopeus koko sanakirjasta oli 11 minuuttia, kun taas suora haku (aaaaaaaa:sta zzzzzzzz) kesti noin 40 minuuttia. GPU:n (RV710-siru) keskinopeus oli 88,3 miljoonaa sekunnissa.

johtopäätöksiä

Huolimatta monista erilaisista rajoituksista ja ohjelmistokehityksen monimutkaisuudesta, GPGPU on korkean suorituskyvyn pöytätietokoneiden tulevaisuus. Mutta tärkeintä on, että voit käyttää tämän tekniikan ominaisuuksia juuri nyt, ja tämä ei koske vain Windows-koneita, vaan myös Linuxia.


GPU Computingin käyttäminen C++ AMP:n kanssa

Toistaiseksi keskustellessamme rolemme ottaneet huomioon vain prosessoriytimet. Olemme saaneet joitain taitoja ohjelmien rinnakkaissuorituksessa useiden prosessorien välillä, jaettujen resurssien käytön synkronoinnissa ja nopeiden synkronointiprimitiivien käyttämisessä ilman lukkoja.

On kuitenkin toinen tapa rinnastaa ohjelmia - grafiikkasuoritusyksiköt (GPU), jossa on enemmän ytimiä kuin jopa korkean suorituskyvyn prosessoreissa. GPU-ytimet soveltuvat erinomaisesti rinnakkaisten tietojenkäsittelyalgoritmien toteuttamiseen, ja niiden suuri määrä maksaa enemmän kuin ohjelmien ajettaessa niillä vaivaa. Tässä artikkelissa tutustumme yhteen tavoista suorittaa ohjelmia GPU:ssa käyttämällä C++-kielilaajennuksia nimeltä C++AMP.

C++ AMP-laajennukset perustuvat C++-kieleen, minkä vuoksi tämä artikkeli näyttää esimerkkejä C++:sta. Kuitenkin, kun vuorovaikutusmekanismia käytetään maltillisesti. NET, voit käyttää C++ AMP -algoritmeja .NET-ohjelmissasi. Mutta puhumme tästä artikkelin lopussa.

Johdatus C++ AMP:hen

Pohjimmiltaan GPU on prosessori kuten mikä tahansa muu, mutta jolla on erityiset ohjeet, iso määrä ytimet ja sen muistin käyttöprotokolla. Nykyaikaisten GPU:iden ja perinteisten prosessorien välillä on kuitenkin suuria eroja, ja niiden ymmärtäminen on avainasemassa luotaessa ohjelmia, jotka käyttävät tehokkaasti GPU:n prosessointitehoa.

    Nykyaikaisilla GPU:illa on hyvin pieni ohjesarja. Tämä tarkoittaa joitain rajoituksia: toimintojen kutsumiskyvyn puute, rajoitettu joukko tuettuja tietotyyppejä, kirjastotoimintojen puute ja muita. Jotkut toiminnot, kuten ehdolliset haarat, voivat maksaa huomattavasti enemmän kuin vastaavat toiminnot, jotka suoritetaan perinteisillä prosessoreilla. Ilmeisesti suurten koodimäärien siirtäminen suorittimesta GPU:lle tällaisissa olosuhteissa vaatii huomattavaa vaivaa.

    Keskimääräisen GPU:n ytimien määrä on huomattavasti suurempi kuin keskimääräisessä perinteisessä prosessorissa. Jotkut tehtävät ovat kuitenkin liian pieniä tai niitä ei voida jakaa tarpeeksi suuriin osiin hyötyäkseen GPU:sta.

    Synkronointituki samaa tehtävää suorittavien GPU-ytimien välillä on erittäin heikkoa, ja se puuttuu kokonaan toimivien GPU-ytimien välillä erilaisia ​​tehtäviä. Tämä seikka edellyttää grafiikkaprosessorin synkronointia tavanomaisen prosessorin kanssa.

Heti herää kysymys: mitkä tehtävät soveltuvat ratkaistavaksi GPU:lla? Muista, että kaikki algoritmit eivät sovellu suoritettavaksi GPU:ssa. Esimerkiksi GPU:illa ei ole pääsyä I/O-laitteisiin, joten et voi parantaa noutavan ohjelman suorituskykyä RSS-syötteet Internetistä näytönohjaimen avulla. Monet laskentaalgoritmit voidaan kuitenkin siirtää GPU:lle ja niitä voidaan rinnastaa massiivisesti. Alla on muutamia esimerkkejä tällaisista algoritmeista (tämä luettelo ei suinkaan ole täydellinen):

    kuvien terävyyden lisääminen ja vähentäminen ja muut muunnokset;

    nopea Fourier-muunnos;

    matriisin transponointi ja kertominen;

    numeroiden lajittelu;

    suora hash-inversio.

Erinomainen lähde lisäesimerkeille on Microsoft Native Concurrency -blogi, joka tarjoaa koodinpätkiä ja selityksiä erilaisista C++ AMP:ssä toteutetuista algoritmeista.

C++ AMP on Visual Studio 2012:een sisältyvä kehys, joka antaa C++-kehittäjille helpon tavan suorittaa laskutoimituksia grafiikkasuorittimella, joka vaatii vain DirectX 11 -ohjaimen. Microsoft on julkaissut C++ AMP:n avoimena määrityksenä, jonka kuka tahansa kääntäjävalmistaja voi ottaa käyttöön.

C++ AMP -kehyksen avulla voit ajaa koodia sisään grafiikkakiihdyttimiä, jotka ovat tietokonelaitteita. DirectX 11 -ohjainta käyttämällä C++ AMP -kehys havaitsee dynaamisesti kaikki kiihdytit. C++ AMP sisältää myös ohjelmistokiihdyttimen emulaattorin ja perinteisen prosessoripohjaisen emulaattorin WARPin, joka toimii varajärjestelmänä järjestelmissä, joissa ei ole GPU:ta tai GPU:ta, mutta josta puuttuu DirectX 11 -ohjain, ja joka käyttää useita ytimiä ja SIMD-ohjeita.

Aloitetaan nyt algoritmin tutkiminen, joka voidaan helposti rinnastaa suorittamaan GPU:lla. Alla oleva toteutus ottaa kaksi samanpituista vektoria ja laskee pisteittäisen tuloksen. On vaikea kuvitella mitään yksinkertaisempaa:

Void VectorAddExpPointwise(float* ensin, float* toinen, float* tulos, int pituus) ( for (int i = 0; i< length; ++i) { result[i] = first[i] + exp(second[i]); } }

Jos haluat rinnastaa tämän algoritmin tavallisessa prosessorissa, sinun on jaettava iterointialue useisiin alialueisiin ja suoritettava yksi suoritussäie kullekin niistä. Olemme käyttäneet paljon aikaa aikaisemmissa artikkeleissa juuri tällä tavalla rinnastaa ensimmäinen alkulukuhakuesimerkkimme – olemme nähneet, kuinka se voidaan tehdä luomalla säikeitä manuaalisesti, siirtämällä töitä säievarastoon ja käyttämällä Parallel.For-tiedostoa. ja PLINQ automaattisesti rinnakkain. Muista myös, että kun rinnakkaisimme samanlaisia ​​algoritmeja perinteisellä prosessorilla, huolehdimme erityisesti siitä, ettemme jakaneet ongelmaa liian pieniin tehtäviin.

GPU:n osalta näitä varoituksia ei tarvita. Grafiikkasuorittimissa on useita ytimiä, jotka suorittavat säikeet erittäin nopeasti, ja kontekstin vaihtamisen kustannukset ovat huomattavasti alhaisemmat kuin perinteiset prosessorit. Alla on katkelma, joka yrittää käyttää toimintoa rinnakkain_jokaiselle C++ AMP -kehyksestä:

#sisältää #sisältää nimitilan samanaikaisuuden käyttäminen; void VectorAddExpPointwise(float* ensin, float* toinen, float* tulos, int pituus) ( array_view avFirst(pituus, ensimmäinen); array_view avSecond(pituus, sekunti); array_view avTulos(pituus, tulos); avResult.discard_data(); parallel_for_each(avResult.extent, [=](indeksi<1>i) rajoittaa(amp) (avResult[i] = avFirst[i] + fast_math::exp(avSecond[i]); )); avResult.synchronize(); )

Tarkastellaan nyt jokaista koodin osaa erikseen. Huomattakoon heti, että pääsilmukan yleinen muoto on säilytetty, mutta alunperin käytetty for loop on korvattu kutsulla parallel_for_each-funktioon. Itse asiassa periaate silmukan muuntamisesta funktio- tai menetelmäkutsuksi ei ole meille uusi - tällainen tekniikka on aiemmin esitelty käyttämällä TPL-kirjaston Parallel.For()- ja Parallel.ForEach()-menetelmiä.

Seuraavaksi syötetiedot (parametrit ensimmäinen, toinen ja tulos) kääritään ilmentymiin array_view. Ary_view-luokkaa käytetään GPU:lle (kiihdytin) siirretyn tiedon käärimiseen. Sen malliparametri määrittää tietotyypin ja sen ulottuvuuden. Jotta voidaan suorittaa ohjeita grafiikkasuorittimessa, joka käyttää alun perin perinteisellä CPU:lla käsiteltyä dataa, jonkun tai jonkun on huolehdittava tietojen kopioimisesta GPU:lle, koska useimmat nykyaikaiset näytönohjaimet ovat erillisiä laitteita, joissa on oma muisti. array_view-instanssit ratkaisevat tämän ongelman - ne tarjoavat tietojen kopioinnin pyynnöstä ja vain silloin, kun sitä todella tarvitaan.

Kun GPU suorittaa tehtävän, tiedot kopioidaan takaisin. Instantoimalla array_view const-argumentilla varmistamme, että ensimmäinen ja toinen kopioidaan GPU-muistiin, mutta niitä ei kopioida takaisin. Samoin soittamalla discard_data(), jätämme tulosten kopioinnin pois tavallisen prosessorin muistista kiihdytinmuistiin, mutta nämä tiedot kopioidaan päinvastaiseen suuntaan.

Paralleal_for_each-funktio ottaa laajuusobjektin, joka määrittää käsiteltävien tietojen muodon ja funktion, jota sovelletaan laajuusobjektin jokaiseen elementtiin. Yllä olevassa esimerkissä käytimme lambda-funktiota, jonka tuki löytyi ISO C++2011 (C++11) -standardista. Rajoitusavainsana (amp) käskee kääntäjää tarkistamaan, voidaanko funktion runko suorittaa GPU:ssa, ja poistaa käytöstä useimmat C++-syntaksit, joita ei voida kääntää GPU-käskyiksi.

Lambda-funktion parametri, indeksi<1>objekti edustaa yksiulotteista indeksiä. Sen on vastattava käytettävää laajuusobjektia - jos määrittäisimme laajuusobjektin kaksiulotteiseksi (esimerkiksi määrittämällä lähdetietojen muoto kaksiulotteiseksi matriisiksi), indeksin tulisi myös olla kaksiulotteinen. -ulotteinen. Alla on esimerkki tällaisesta tilanteesta.

Lopuksi menetelmäkutsu synkronoida() VectorAddExpPointwise-menetelmän lopussa se varmistaa, että GPU:n tuottaman array_view avResultin laskentatulokset kopioidaan takaisin tulostaulukkoon.

Tämä päättää ensimmäisen johdannon C++ AMP:n maailmaan, ja nyt olemme valmiita tarkempaan tutkimukseen sekä mielenkiintoisempaan esimerkkeihin, jotka osoittavat rinnakkaislaskennan käytön GPU:ssa. Vektorilisäys ei ole hyvä algoritmi, eikä se ole paras ehdokas GPU-käytön osoittamiseen, koska dataa on kopioitava paljon. Seuraava alajakso näyttää kaksi mielenkiintoista esimerkkiä.

Matriisin kertolasku

Ensimmäinen "todellinen" esimerkki, jota tarkastelemme, on matriisikerto. Toteutukseen otamme yksinkertaisen kuutiomatriisin kertolaskualgoritmin, emme Strassen-algoritmia, jonka suoritusaika on lähellä kuutiota ~O(n 2.807). Kun on annettu kaksi matriisia, m x w matriisi A ja w x n matriisi B, seuraava ohjelma kertoo ne ja palauttaa tuloksen, m x n matriisi C:

Void MatrixMultiply(int* A, int m, int w, int* B, int n, int* C) ( for (int i = 0; i< m; ++i) { for (int j = 0; j < n; ++j) { int sum = 0; for (int k = 0; k < w; ++k) { sum += A * B; } C = sum; } } }

On olemassa useita tapoja rinnakkaista tämä toteutus, ja jos haluat rinnastaa tämän koodin toimimaan tavallisella prosessorilla, oikea valinta olisi rinnakkaista ulkosilmukka. Grafiikkasuorittimessa on kuitenkin melko suuri määrä ytimiä, emmekä rinnastamalla vain ulompaa silmukkaa, emme pysty luomaan tarpeeksi työpaikkoja, jotta kaikki ytimet kuormittuvat työllä. Siksi on järkevää rinnastaa kaksi ulompaa silmukkaa jättämällä sisempi silmukka koskemattomaksi:

Void MatrixMultiply (int* A, int m, int w, int* B, int n, int* C) ( array_view avA(m, w, A); array_view avB(w, n, B); array_view avC(m, n, C); avC.discard_data(); parallel_for_each(avC.extent, [=](indeksi<2>idx) rajoittaa(amp) ( int summa = 0; for (int k = 0; k< w; ++k) { sum + = avA(idx*w, k) * avB(k*w, idx); } avC = sum; }); }

Tämä toteutus muistuttaa edelleen läheisesti matriisin kertolaskua ja edellä annettua vektorien yhteenlaskuesimerkkiä, lukuun ottamatta indeksiä, joka on nyt kaksiulotteinen ja joka on käytettävissä operaattorin avulla sisäisessä silmukassa. Kuinka paljon nopeampi tämä versio kuin tavallisella prosessorilla toimiva peräkkäinen vaihtoehto? Kun kerrotaan kaksi matriisia (kokonaislukua), joiden koko on 1024 x 1024, peräkkäinen versio tavallisella prosessorilla kestää keskimäärin 7350 millisekuntia, kun taas GPU-versio - pidä kiinni - kestää 50 millisekuntia, 147 kertaa nopeammin!

Hiukkasten liikkeen simulointi

Yllä esitetyt esimerkit GPU:n ongelmien ratkaisemisesta sisältävät hyvin yksinkertaisen sisäisen silmukan toteutuksen. On selvää, että näin ei aina tapahdu. Yllä oleva Native Concurrency -blogi näyttää esimerkin hiukkasten välisten gravitaatiovuorovaikutusten mallintamisesta. Simulaatio sisältää äärettömän määrän vaiheita; jokaisessa vaiheessa kiihtyvyysvektorin elementtien uudet arvot lasketaan kullekin hiukkaselle ja sitten määritetään niiden uudet koordinaatit. Täällä hiukkasvektori rinnastetaan - riittävän suurella määrällä hiukkasia (useasta tuhannesta ja enemmän) voit luoda riittävän suuren määrän tehtäviä ladataksesi kaikki GPU-ytimet työllä.

Algoritmin perustana on kahden hiukkasen välisen vuorovaikutuksen tuloksen määrittäminen alla esitetyllä tavalla, joka voidaan helposti siirtää GPU:lle:

// tässä float4 ovat vektoreita, joissa on neljä elementtiä // jotka edustavat toimintoihin osallistuvia partikkeleita void bodybody_interaction (float4& kiihtyvyys, const float4 p1, const float4 p2) rest(amp) ( float4 dist = p2 – p1; // ei w tässä käytetty float absDist = dist.x*dist.x + dist.y*dist.y + dist.z*dist.z; float invDist = 1.0f / sqrt(absDist); float invDistCube = invDist*invDist*invDist; kiihtyvyys + = dist*PARTICLE_MASS*invDistCube;)

Lähtötietona kussakin mallintamisvaiheessa on taulukko, jossa on hiukkasten koordinaatit ja nopeudet, ja laskelmien tuloksena syntyy uusi taulukko, jossa on hiukkasten koordinaatit ja nopeudet:

Rakennehiukkanen ( float4 -sijainti, nopeus; // konstruktorin, kopiokonstruktorin ja // operaattorin toteutukset = raja(amp) jätetty pois tilan säästämiseksi ); void simulation_step(array & edellinen, array & seuraava, sisäelimet) ( laajuus<1>ext(bodys); parallel_for_each (ext, [&](indeksi<1>idx) rajoittaa(amp) ( hiukkanen p = edellinen; float4-kiihtyvyys(0, 0, 0, 0); for (int body = 0; body< bodies; ++body) { bodybody_interaction (acceleration, p.position, previous.position); } p.velocity + = acceleration*DELTA_TIME; p.position + = p.velocity*DELTA_TIME; next = p; }); }

Sopivan graafisen käyttöliittymän avulla mallintaminen voi olla erittäin mielenkiintoista. C++ AMP -tiimin koko esimerkki löytyy Native Concurrency -blogista. Intel Core i7 -prosessorilla ja Geforce GT 740M -näytönohjaimella varustetussa järjestelmässäni 10 000 hiukkasen simulointi toimii ~2,5 fps (askel sekunnissa) peräkkäisellä versiolla, joka on käynnissä tavallisella prosessorilla, ja 160 fps käytettäessä optimoitua versiota. GPU:ssa - valtava suorituskyvyn kasvu.

Ennen kuin päätämme tämän osan, C++ AMP -kehyksessä on vielä yksi tärkeä ominaisuus, joka voi edelleen parantaa GPU:ssa ajettavan koodin suorituskykyä. GPU-tuki ohjelmoitava välimuisti(usein kutsuttu jaettu muisti). Tähän välimuistiin tallennetut arvot jakavat kaikki suoritussäikeet yhdessä ruudussa. Muistin laatoituksen ansiosta C++ AMP -kehykseen perustuvat ohjelmat voivat lukea tietoja näytönohjaimen muistista mosaiikin jaettuun muistiin ja sitten käyttää sitä useista suoritussäikeistä ilman, että tietoja tarvitsee hakea uudelleen näytönohjaimen muistista. Mosaiikki jaetun muistin käyttö on noin 10 kertaa nopeampaa kuin näytönohjaimen muisti. Toisin sanoen sinulla on syytä jatkaa lukemista.

Rinnakkaissilmukan ruutuversion tarjoamiseksi parallel_for_each-metodi välitetään domain tiled_extent, joka jakaa moniulotteisen laajuuden objektin moniulotteisiin ruutuihin, ja tiled_index lambda-parametrin, joka määrittää ruudun sisällä olevan säikeen yleisen ja paikallisen tunnuksen. Esimerkiksi 16x16 matriisi voidaan jakaa 2x2 ruutuun (kuten alla olevassa kuvassa) ja siirtää sitten parallel_for_each -funktioon:

Laajuus<2>matriisi(16,16); laatoitettu_laajuus<2,2>tiledMatrix = matrix.tile<2,2>(); parallel_for_each(tiedMatrix, [=](laatoitettu_indeksi<2,2>idx) rajoittaa(amp) ( // ... ));

Jokainen neljästä samaan mosaiikkiin kuuluvasta suoritussäikeestä voi jakaa lohkoon tallennetun tiedon.

Suorittaessasi operaatioita matriiseilla, GPU-ytimessä tavallisen indeksin sijaan<2>, kuten yllä olevissa esimerkeissä, voit käyttää idx.global. Paikallisen ruutumuistin ja paikallisten indeksien oikea käyttö voi parantaa suorituskykyä merkittävästi. Jos haluat ilmoittaa kaikkien suoritussäikeiden jakaman ruutumuistin yhdessä ruudussa, paikalliset muuttujat voidaan ilmoittaa tile_static-määritteen avulla.

Käytännössä käytetään usein tekniikkaa jaetun muistin ilmoittamiseksi ja sen yksittäisten lohkojen alustamiseksi eri suoritussäikeissä:

Parallel_for_each(tiedMatrix, [=](laatoitettu_indeksi<2,2>idx) limit(amp) ( // 32 tavua jaetaan kaikille säikeille lohkossa tile_static int local; // määritä arvo tämän suoritussäikeen elementille local = 42; ));

Ilmeisesti kaikki hyödyt jaetun muistin käytöstä voidaan saavuttaa vain, jos pääsy tähän muistiin on synkronoitu; eli säikeet eivät saa käyttää muistia ennen kuin jokin niistä on alustanut sen. Mosaiikin säikeiden synkronointi suoritetaan objektien avulla tile_barrier(muistuttaa TPL-kirjaston Barrier-luokkaa) - ne voivat jatkaa suorittamista vasta kutsuttuaan tile_barrier.Wait()-metodia, joka palauttaa ohjauksen vasta kun kaikki säikeet ovat kutsuneet tiili_barrier.Wait. Esimerkiksi:

Parallel_for_each(laattaMatriisi, (laatoitettu_indeksi<2,2>idx) limit(amp) ( // 32 tavua jaetaan kaikille lohkon tile_static int local säikeille; // määritä arvo tämän suoritussäikeen elementille local = 42; // idx.barrier on tile_barrierin esiintymä idx.barrier.wait(); // Nyt tämä säie voi käyttää "paikallista" taulukkoa // käyttämällä muiden suoritussäikeiden indeksejä! ));

Nyt on aika kääntää oppimasi konkreettinen esimerkki. Palataan matriisikertomisen toteutukseen, joka suoritetaan ilman laatoitusmuistin järjestämistä, ja lisätään siihen kuvattu optimointi. Oletetaan, että matriisin koko on 256:n kerrannainen – tämä mahdollistaa 16 x 16 lohkon kanssa työskentelyn. Matriisien luonne mahdollistaa kertolasku lohkolta, ja voimme hyödyntää tätä ominaisuutta (itse asiassa jakamista matriisit lohkoiksi on tyypillinen matriisin kertolaskualgoritmin optimointi, joka tarjoaa tehokkaamman suorittimen välimuistin käytön).

Tämän tekniikan ydin on seuraava. Löytääksesi C i,j (alkio rivillä i ja sarakkeessa j tulosmatriisissa), sinun on laskettava pistetulo A i,* (ensimmäisen matriisin i. rivi) ja B *,j (j -th sarake toisessa matriisissa ). Tämä vastaa kuitenkin rivin ja sarakkeen osittaisten pistetulojen laskemista ja tulosten summaamista. Voimme käyttää tätä tosiasiaa muuntaaksemme matriisin kertolaskualgoritmin laatoitusversioksi:

Void MatrixMultiply(int* A, int m, int w, int* B, int n, int* C) ( array_view avA(m, w, A); array_view avB(w, n, B); array_view avC(m, n, C); avC.discard_data(); parallel_for_each(avC.extent.tile<16,16>(), [=](tiedottu_indeksi<16,16>idx) rajoittaa(amp) ( int summa = 0; int localRow = idx.local, localCol = idx.local; for (int k = 0; k

Kuvatun optimoinnin ydin on, että jokainen mosaiikin säie (16 x 16 lohkolle luodaan 256 säiettä) alustaa elementtinsä 16 x 16 paikalliskopiona alkuperäisten matriisien A ja B fragmenteista. Jokainen mosaiikin säie vaatii vain yksi rivi ja yksi sarake näistä lohkoista, mutta kaikki säikeet yhdessä pääsevät jokaiseen riviin ja sarakkeeseen 16 kertaa. Tämä lähestymistapa vähentää merkittävästi päämuistin käyttöjen määrää.

Alkion (i,j) laskemiseksi tulosmatriisissa algoritmi vaatii ensimmäisen matriisin täydellisen i:nnen rivin ja toisen matriisin j:nnen sarakkeen. Kun säikeet ovat kaaviossa 16x16 laatoitettuja ja k=0, ensimmäisen ja toisen matriisin varjostetut alueet luetaan jaettuun muistiin. Tulosmatriisin suoritussäikeen laskentaelementti (i,j) laskee ensimmäisen k elementin osittaispistetulon alkuperäisten matriisien i:nnestä rivistä ja j:nnestä sarakkeesta.

Tässä esimerkissä kaakeloidun organisaation käyttö parantaa suorituskykyä valtavasti. Matriisin kertolaskujen laatoitettu versio on paljon nopeampi kuin yksinkertainen versio, ja se kestää noin 17 millisekuntia (samoilla 1024 x 1024 syöttömatriiseilla), mikä on 430 kertaa nopeampi kuin perinteisellä prosessorilla toimiva versio!

Ennen kuin lopetamme keskustelumme C++ AMP -kehyksestä, haluaisimme mainita kehittäjien käytettävissä olevat työkalut (Visual Studiossa). Visual Studio 2012 tarjoaa grafiikkasuoritusyksikön (GPU) virheenkorjaajan, jonka avulla voit asettaa keskeytyspisteitä, tutkia puhelupinoa sekä lukea ja muuttaa paikallisten muuttujien arvoja (jotkut kiihdytit tukevat GPU-virheenkorjausta suoraan; toiset Visual Studio käyttää ohjelmistosimulaattoria) ja profiloija, jonka avulla voit arvioida hyödyt, joita sovellus saa toimintojen rinnastamisesta GPU:n avulla. Lisätietoja Visual Studion virheenkorjausominaisuuksista on Walkthrough-artikkelissa. C++ AMP -sovelluksen virheenkorjaus" MSDN:ssä.

GPU-laskentavaihtoehdot .NET:ssä

Toistaiseksi tässä artikkelissa on esitetty vain esimerkkejä C++:ssa, mutta on olemassa useita tapoja hyödyntää GPU:n tehoa hallituissa sovelluksissa. Yksi tapa on käyttää interop-työkaluja, joiden avulla voit siirtää GPU-ytimien kanssa työskentelyn matalan tason C++-komponentteihin. Tämä ratkaisu sopii erinomaisesti niille, jotka haluavat käyttää C++ AMP -kehystä tai joilla on mahdollisuus käyttää valmiita C++ AMP -komponentteja hallituissa sovelluksissa.

Toinen tapa on käyttää kirjastoa, joka toimii suoraan GPU:n kanssa hallitusta koodista. Tällaisia ​​kirjastoja on tällä hetkellä useita. Esimerkiksi GPU.NET ja CUDAfy.NET (molemmat kaupalliset tarjoukset). Alla on esimerkki GPU.NET GitHub -tietovarastosta, joka osoittaa kahden vektorin pistetulon toteutuksen:

Julkinen staattinen void MultiplyAddGpu(double a, double b, double c) ( int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; int TotalThreads = BlockDimension.X * GridDimension.X; for (int ElementIdx = ThreadIdx E;

Olen sitä mieltä, että on paljon helpompaa ja tehokkaampaa oppia kielilaajennus (perustuu C++ AMP:hen) kuin yrittää organisoida vuorovaikutusta kirjastotasolla tai tehdä merkittäviä muutoksia IL-kieleen.

Joten, kun olemme tarkastelleet .NETin rinnakkaisohjelmoinnin mahdollisuuksia ja GPU:n käyttöä, kukaan ei epäile, etteikö rinnakkaislaskennan järjestäminen olisi tärkeä tapa lisätä tuottavuutta. Monilla palvelimilla ja työasemilla ympäri maailmaa suorittimien ja grafiikkasuorittimien korvaamaton prosessointiteho jää käyttämättä, koska sovellukset eivät yksinkertaisesti käytä sitä.

Task Parallel Library antaa meille ainutlaatuisen mahdollisuuden sisällyttää kaikki käytettävissä olevat prosessoriytimet, vaikka tämä edellyttääkin mielenkiintoisten synkronointiongelmien ratkaisemista, tehtävien liiallista pirstoutumista ja työn epätasaista jakautumista suoritussäikeiden välillä.

C++ AMP -kehystä ja muita monikäyttöisiä GPU-rinnakkaislaskentakirjastoja voidaan käyttää menestyksekkäästi satojen GPU-ytimien laskelmien rinnakkaisuun. Lopuksi on olemassa aiemmin tutkimaton mahdollisuus saada tuottavuushyötyjä käyttämällä pilvihajautettuja laskentatekniikoita, joista on viime aikoina tullut yksi tietotekniikan kehityksen pääsuunnista.

Yksi viimeaikaisen Windows 10 -päivityksen piilotetuimmista ominaisuuksista on mahdollisuus tarkistaa, mitkä sovellukset käyttävät grafiikkasuoritusyksikköäsi (GPU). Jos olet joskus avannut Task Managerin, olet luultavasti katsonut suorittimen käyttöäsi nähdäksesi, mitkä sovellukset käyttävät eniten suoritinta. Uusimmat päivitykset lisäsivät samanlaisen ominaisuuden, mutta GPU-grafiikkaprosessoreille. Tämä auttaa sinua ymmärtämään, kuinka intensiivisiä ohjelmistosi ja pelisi ovat GPU:ssasi ilman, että sinun tarvitsee ladata kolmannen osapuolen ohjelmistoja. On toinenkin mielenkiintoinen ominaisuus, joka auttaa purkamaan CPU:ta GPU:hun. Suosittelen lukemaan kuinka valita.

Miksi minulla ei ole GPU:ta Task Managerissa?

Valitettavasti kaikki näytönohjaimet eivät pysty tarjoamaan Windows-järjestelmälle GPU:n lukemiseen tarvittavia tilastoja. Voit olla varma, että voit nopeasti tarkistaa tämän tekniikan DirectX-diagnostiikkatyökalulla.

  1. Klikkaus " alkaa" ja kirjoita hakuun dxdiag suorittaaksesi DirectX-diagnostiikkatyökalun.
  2. Siirry "välilehteen" näyttö", oikealla sarakkeessa " Kuljettajat"sinulla täytyy olla WDDM malli yli 2.0 versio GPU-kaavioiden käyttämiseen tehtävähallinnassa.

Ota GPU-kaavio käyttöön tehtävähallinnassa

Jos haluat nähdä kunkin sovelluksen GPU-käytön, sinun on avattava tehtävähallinta.

  • Paina painikkeiden yhdistelmää Ctrl + Shift + Esc avataksesi tehtävähallinnan.
  • Napsauta hiiren kakkospainikkeella tehtävähallinnassa "tyhjä"-ruutua Nimi" ja tarkista avattavasta valikosta GPU Voit myös huomata GPU-ydin nähdäksesi mitkä ohjelmat käyttävät sitä.
  • Nyt tehtävähallinnassa GPU-kaavio ja GPU-ydin näkyvät oikealla.


Katso GPU:n kokonaissuorituskyky

Voit seurata GPU:n yleistä käyttöä seurataksesi sitä raskaassa kuormituksessa ja analysoidaksesi sitä. Tässä tapauksessa näet kaiken tarvitsemasi välilehdessä Esitys" valitsemalla näytönohjain.


Jokainen GPU-elementti on jaettu yksittäisiksi kaavioiksi, jotta saat entistä enemmän tietoa siitä, miten GPU:ta käytetään. Jos haluat muuttaa näytettäviä kaavioita, voit napsauttaa pientä nuolta kunkin tehtävän nimen vieressä. Tämä näyttö näyttää myös ohjainversiosi ja päivämäärän, mikä on hyvä vaihtoehto DXDiagin tai Laitehallinnan käyttämiselle.