VienkārŔa hashtable GPU

VienkārŔa hashtable GPU
Es ievietoju GitHub jaunu projektu A Simple GPU Hash Table.

Å Ä« ir vienkārÅ”a hash tabula GPU, kas spēj apstrādāt simtiem miljonu ievietojumu sekundē. Manā klēpjdatorā ar NVIDIA GTX 1060 kods ievieto 64 miljonus nejauÅ”i Ä£enerētu atslēgas-vērtÄ«bas pāru aptuveni 210 ms laikā un izdzēŔ 32 miljonus pāru aptuveni 64 ms laikā.

Tas nozÄ«mē, ka ātrums manā klēpjdatorā ir aptuveni 300 milj. ievietojumu/sekunžu un 500 milj. dzēŔanu/sekunžu.

Tabula ir uzrakstÄ«ta CUDA, lai gan to paÅ”u metodi var piemērot HLSL vai GLSL. Izpildei ir vairāki ierobežojumi, kas nodroÅ”ina augstu veiktspēju uz grafiskās kartes:

  • Tiek apstrādātas tikai 32 bitu atslēgas un tādas paÅ”as vērtÄ«bas.
  • Hash tabulai ir fiksēts lielums.
  • Un Å”im izmēram jābÅ«t vienādam ar diviem pakāpē.

Atslēgām un vērtÄ«bām ir jāpiedāvā vienkārÅ”s robežzÄ«mes marÄ·ieris (dotajā kodā tas ir 0xffffffff).

Hash tabula bez bloķēŔanas

Hash tabulā tiek izmantota atvērtā adresācija ar lineāro zondēŔanu, tas ir, tas ir vienkārÅ”i atslēgas-vērtÄ«bas pāru masÄ«vs, kas tiek glabāts atmiņā un tam ir lieliska keÅ”atmiņas veiktspēja. To nevar teikt par saistīŔanu (chaining), kas nozÄ«mē, ka jāmeklē rādÄ«tājs saistÄ«tajā sarakstā. Hash tabula ir vienkārÅ”s masÄ«vs, kas glabā elementus. KeyValue:

struct KeyValue
{
    uint32_t key;
    uint32_t value;
};

Tabulas izmēram jābÅ«t divciparu skaitlim, nevis pirmskaitlim, jo, lai izmantotu pow2/AND masku, ir nepiecieÅ”ama tikai viena ātra instrukcija, turpretÄ« modulārais operators strādā daudz lēnāk. Tas ir svarÄ«gi lineārās zondēŔanas gadÄ«jumā, jo, veicot lineāro meklēŔanu tabulā, slotu indeksam katrā slotā jāpiedalās. Un tā rezultātā katrā slotā pievienojas izmaksas par operācijas moduli.

Tabula glabā tikai atslēgu un vērtÄ«bu katram elementam, nevis atslēgas hash. Tā kā tabula glabā tikai 32 bitu atslēgas, hash tiek aprēķināts ļoti ātri. Dotajā kodā tiek izmantots Murmur3 hash, kas veic tikai dažus bitu slēgumus, XOR un reizināŔanas operācijas.

Hashtabula izmanto metodi, lai aizsargātu pret bloķēŔanu, kas nav atkarÄ«ga no datu izvietoÅ”anas secÄ«bas atmiņā. Pat ja daži rakstīŔanas darbi pārkāpj secÄ«bu citu Ŕādu operāciju attiecÄ«bā, hashtabula joprojām saglabās pareizu stāvokli. Par to mēs runāsim vēlāk. Å Ä« metode lieliski darbojas videokartēs, kur tÅ«kstoÅ”iem pavedienu darbojas konkurējoÅ”i.

Atslēgas un vērtÄ«bas hashtabulā tiek inicializētas kā tukÅ”as.

Kodu var modificēt, lai tas varētu apstrādāt arÄ« 64 bitu atslēgas un vērtÄ«bas. Atslēgām ir nepiecieÅ”amas atomārās operācijas lasīŔanai, rakstīŔanai un salÄ«dzināŔanai ar apmaiņu (compare-and-swap). Bet vērtÄ«bām nepiecieÅ”amas atomārās operācijas lasīŔanai un rakstīŔanai. Par laimi, CUDA lasīŔanas un rakstīŔanas operācijas 32 un 64 bitu vērtÄ«bām ir atomāras, kamēr tās ir dabiski izkārtotas (skat. Å”eit), un mÅ«sdienu videokartes atbalsta 64 bitu atomārās salÄ«dzināŔanas ar apmaiņu operācijas. Protams, pārejot uz 64 biti, veiktspēja nedaudz samazinās.

Hashtabulas stāvoklis

Katram atslēgas-un-vērtības pārim hashtabulā var būt viens no četriem stāvokļiem:

  • Atslēga un vērtÄ«ba ir tukÅ”as. Šādā stāvoklÄ« hashtabula tiek inicializēta.
  • Atslēga ir rakstÄ«ta, bet vērtÄ«ba vēl nav. Ja Å”ajā brÄ«dÄ« cits izpildes pavedienu nolasÄ«s datus, tad tas atgriezÄ«s tukÅ”u vērtÄ«bu. Tas ir normāli, tāpat notiktu, ja cits izpildes pavedienu bÅ«tu izpildÄ«jies nedaudz agrāk, un mēs runājam par konkurējoÅ”u datu struktÅ«ru.
  • Gan atslēga, gan vērtÄ«ba ir rakstÄ«tas.
  • VērtÄ«ba ir pieejama citiem izpildes pavedieniem, bet atslēga vēl nav. Tas var notikt, jo programmatÅ«ras modelis CUDA paredz vāji sakārtotu atmiņas modeli. Tas ir normāli, jebkurā gadÄ«jumā atslēga joprojām ir tukÅ”a pat tad, ja vērtÄ«ba tāda jau nav.

SvarÄ«gs nianses ir tas, ka tiklÄ«dz atslēga ir rakstÄ«ta slotā, tā vairs neatkāpjas — pat ja atslēga tiks dzēsta, mēs par to runāsim vēlāk.

Hashtabulas kods darbojas pat ar vāji sakārtotiem atmiņas modeļiem, kuros nav zināma lasīŔanas un rakstīŔanas secÄ«ba. Kad mēs apspriedÄ«sim ievadīŔanu, meklēŔanu un dzēŔanu hashtabulā, atcerieties, ka katrs atslēgas-un-vērtÄ«bas pāris atrodas vienā no iepriekÅ” minētajiem četriem stāvokļiem.

IevadīŔana hashtabulā

CUDA funkcija, kas ievieto atslēgas-vērtÄ«bu pārus hash tabulā, izskatās Ŕādi:

void gpu_hashtable_insert(KeyValue* hashtable, uint32_t key, uint32_t value)
{
    uint32_t slot = hash(key);

    while (true)
    {
        uint32_t prev = atomicCAS(&hashtable[slot].key, kEmpty, key);
        if (prev == kEmpty || prev == key)
        {
            hashtable[slot].value = value;
            break;
        }
        slot = (slot + 1) & (kHashTableCapacity-1);
    }
}

Lai ievietotu atslēgu, kods iterē cauri hash tabulas masÄ«vam, sākot no ievietotās atslēgas hasha. Katram masÄ«va slotam tiek veikta atomāra salÄ«dzinājuma ar apmaiņu operācija, kurā atslēga Å”ajā slotā tiek salÄ«dzināta ar tukÅ”u. Ja atrodam atŔķirÄ«bu, atslēga slotā tiek atjaunināta uz ievietojamo atslēgu, un pēc tam tiek atgriezta sākotnējā atslēga no slotā. Ja Ŕī oriÄ£inālā atslēga bija tukÅ”a vai atbilst ievietotajai atslēgai, tas nozÄ«mē, ka kods ir atradis piemērotu slotu ievietoÅ”anai un ievieto slotā ievietoto vērtÄ«bu.

Ja vienā kodu izsaukumā gpu_hashtable_insert() ir vairāki elementi ar to paÅ”u atslēgu, tad jebkura no to vērtÄ«bām var tikt ierakstÄ«ta atslēgas slotā. Tas tiek uzskatÄ«ts par normālu: viena no atslēgas-vērtÄ«bas ierakstīŔanas operācijām izsaukuma laikā bÅ«s veiksmÄ«ga, bet, tā kā tas viss notiek paralēli vairākos izpildes pavedienos, mēs nevaram paredzēt, kura rakstīŔanas operācija atmiņā bÅ«s pēdējā.

MeklēŔana hash tabulā

Kods atslēgu meklēŔanai:

uint32_t gpu_hashtable_lookup(KeyValue* hashtable, uint32_t key)
{
        uint32_t slot = hash(key);

        while (true)
        {
            if (hashtable[slot].key == key)
            {
                return hashtable[slot].value;
            }
            if (hashtable[slot].key == kEmpty)
            {
                return kEmpty;
            }
            slot = (slot + 1) & (kHashTableCapacity - 1);
        }
}

Lai atrastu atslēgas vērtÄ«bu, kas glabājas tabulā, mēs iterējam cauri masÄ«vam, sākot no meklētās atslēgas hasha. Katram slotam mēs pārbaudām, vai atslēga ir tā, kuru meklējam, un, ja jā, atgriežam tās vērtÄ«bu. Mēs arÄ« pārbaudām, vai atslēga ir tukÅ”a, un, ja jā, mēs pārtraucam meklēŔanu.

Ja mēs nevaram atrast atslēgu, tad kods atgriež tukÅ”u vērtÄ«bu.

Visas Ŕīs meklēŔanas operācijas var tikt veiktas konkurējoÅ”i, ievietoÅ”anas un dzēŔanas laikā. Katram pārim tabulā bÅ«s četri iepriekÅ” aprakstÄ«tie stāvokļi konkrētam pavedienam.

DzēŔana hash tabulā

Kods atslēgu dzēŔanai:

void gpu_hashtable_delete(KeyValue* hashtable, uint32_t key, uint32_t value)
{
    uint32_t slot = hash(key);

    while (true)
    {
        if (hashtable[slot].key == key)
        {
            hashtable[slot].value = kEmpty;
            return;
        }
        if (hashtable[slot].key == kEmpty)
        {
            return;
        }
        slot = (slot + 1) & (kHashTableCapacity - 1);
    }
}

Atslēgas dzēŔana notiek neparasti: mēs atstājam atslēgu tabulā un atzÄ«mējam tās vērtÄ«bu (ne paÅ”u atslēgu) kā tukÅ”u. Å is kods ir ļoti lÄ«dzÄ«gs lookup(), izņemot to, ka, atrodot atbilstÄ«bu atslēgai, tā vērtÄ«ba kļūst par tukÅ”u.

Kā minēts iepriekÅ”, tiklÄ«dz atslēga ir ierakstÄ«ta slotā, tā vairs nepārvietojas. Pat tad, ja elements tiek dzēsts no tabulas, atslēga paliek savā vietā, vienkārÅ”i tās vērtÄ«ba kļūst tukÅ”a. Tas nozÄ«mē, ka mums nav nepiecieÅ”ams izmantot atomāro rakstīŔanas operāciju slotā, jo nav svarÄ«gi, vai paÅ”reizējā vērtÄ«ba ir tukÅ”a vai nē — tā tomēr kļūs tukÅ”a.

Hesh-tabulas izmēra maiņa

MainÄ«t hesh-tabulas izmēru var, izveidojot lielāku tabulu un ievietojot tajā nepilnÄ«gās elementus no vecās tabulas. Es Å”o funkcionalitāti neesmu Ä«stenojis, jo vēlējos saglabāt koda paraugu vienkārÅ”u. Turklāt CUDA programmās atmiņas pieŔķirÅ”ana bieži tiek veikta hosta kodā, nevis CUDA kodē.

Rakstā Lock-Free Wait-Free Hash Table apraksta, kā mainÄ«t Ŕādu datu struktÅ«ru, kas ir aizsargāta no bloķēŔanās.

Konkurence

Ievietotajos iepriekŔējā koda fragmentos funkcijas gpu_hashtable_insert(), _lookup() un _delete() apstrādā pa vienai atslēgas-vērtÄ«bas pārim reizē. Bet zemāk gpu_hashtable_insert(), _lookup() un _delete() apstrādā pāru masÄ«vu paralēli, katru pāri atseviŔķā GPU izpildes plÅ«smā:

// CPU code to invoke the CUDA kernel on the GPU
uint32_t threadblocksize = 1024;
uint32_t gridsize = (numkvs + threadblocksize - 1) / threadblocksize;
gpu_hashtable_insert_kernel<<<gridsize, threadblocksize>>>(hashtable, kvs, numkvs);

// GPU code to process numkvs key/values in parallel
void gpu_hashtable_insert_kernel(KeyValue* hashtable, const KeyValue* kvs, unsigned int numkvs)
{
    unsigned int threadid = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadid < numkvs)
    {
        gpu_hashtable_insert(hashtable, kvs[threadid].key, kvs[threadid].value);
    }
}

Hesh-tabula, kas ir aizsargāta no bloķēŔanās, atbalsta konkurētspējÄ«gas ievietoÅ”anas, meklēŔanas un dzēŔanas operācijas. Tā kā atslēgas-vērtÄ«bas pāri vienmēr atrodas vienā no četriem stāvokļiem, un atslēgas nepārvietojas, tabula garantē precizitāti pat tad, ja tiek vienlaicÄ«gi lietotas dažādu veidu operācijas.

Tomēr, ja mēs paralēli apstrādājam ievietoÅ”anas un dzēŔanas partiju, un ja iebraukÅ”anas pāru masÄ«vā ir atkārtojumi, mēs nevarēsim paredzēt, kura pāra "uzvarēs" — tās tiks ierakstÄ«tas hesh-tabulā pēdējās. Pieņemsim, ka mēs izsaucām ievietoÅ”anas kodu ar iebraukÅ”anas masÄ«vu pāriem A/0 B/1 A/2 C/3 A/4. Kad kods pabeigs darbÄ«bu, pāri B/1 un C/3 neapÅ”aubāmi bÅ«s tabulā, bet tajā bÅ«s jebkura no pāriem A/0, A/2 vai A/4Tas var bÅ«t problēma vai arÄ« ne - viss atkarÄ«gs no pielietoÅ”anas. JÅ«s varat iepriekÅ” zināt, ka ievades masÄ«vā nav atkārtotu atslēgu, vai arÄ« jums var bÅ«t vienalga, kura vērtÄ«ba ir ierakstÄ«ta pēdējā.

Ja tas jums ir problēma, jums ir jānodala atkārtotās pāris no dažādām sistēmas CUDA izsaukumiem. CUDA jebkura operācija ar kodolu izsaukumu vienmēr beidzas pirms nākamā kodola izsaukuma (vismaz viena plÅ«smas ietvaros. AtŔķirÄ«gās plÅ«smās kodoli tiek izpildÄ«ti paralēli). Ja iepriekÅ” minētajā piemērā izsaucat vienu kodolu ar A/0 B/1 A/2 C/3, un citu ar A/4, tad atslēga A saņems vērtÄ«bu 4.

Tagad parunāsim par to, vai funkcijām lookup() un delete() jāizmanto vienkārŔs (plain) vai mainīgs (volatile) rādītājs uz pāru masīvu haŔa tabulā. CUDA dokumentācija apgalvo, ka:

Kompilatoram ir tiesÄ«bas optimizēt lasīŔanas un rakstīŔanas operācijas uz globālo vai kopÄ«go atmiņu… Å Ä«s optimizācijas var atspējot, izmantojot atslēgas vārdu volatile: ... jebkura atsauce uz Å”o mainÄ«go tiek kompilēta kā faktiskā lasīŔanas vai rakstīŔanas instrukcija atmiņā.

Korekcijas apsvērumi neprasa piemērot volatile. Ja izpildes plÅ«sma izmanto keÅ”otu vērtÄ«bu no iepriekŔējās lasīŔanas operācijas, tas nozÄ«mē, ka tā izmantos nedaudz novecojuÅ”u informāciju. Bet tomēr tas ir informācija no korekta stāvokļa haÅ”a tabulā noteiktā kodola izsaukuma brÄ«dÄ«. Ja jums ir nepiecieÅ”ams izmantot visjaunāko informāciju, varat izmantot rādÄ«tāju volatile, bet tad nedaudz samazināsies veiktspēja: pēc maniem testiem, izņemot 32 miljonus elementu, ātrums samazinājās no 500 miljoniem dzēŔanu/sek lÄ«dz 450 miljoniem dzēŔanu/sek.

Veiktspēja

IevietoÅ”anas testā 64 miljonu elementu un 32 miljonu no tiem dzēŔanas sacensÄ«ba starp std::unordered_map un haÅ”a tabulu GPU faktiski nepastāv:

VienkārŔa hashtable GPU
std::unordered_map tika pavadÄ«ti 70 691 ms elementu ievietoÅ”anai un dzēŔanai, pēc tam atbrÄ«voÅ”anai unordered_map (atbrÄ«voÅ”ana no miljoniem elementu aizņem daudz laika, jo iekÅ”pusē tiek veiktas daudzas atmiņas pieŔķirÅ”anās). GodÄ«gi sakot, pie unordered_map std::unordered_map std:unordered_map pilnÄ«gi citi ierobežojumi. Tas ir viens CPU izpildplÅ«sma, tas atbalsta atslēgas-vērtÄ«bas jebkura izmēra, labi darbojas ar augstiem izmantoÅ”anas koeficientiem un parāda stabilu veiktspēju pēc daudzkārtējām dzēŔanām.

Hash tabulas darbÄ«bas ilgums GPU un starpprogrammu mijiedarbÄ«bā bija 984 ms. Tas ietver laiku, kas tika pavadÄ«ts tabulas izvietoÅ”anai atmiņā un tās dzēŔanai (vienreizēju 1 GB atmiņas pieŔķirÅ”anu, kas CUDA ilgst zināmu laiku), elementu ievietoÅ”anai un dzēŔanai, kā arÄ« iterēŔanai pa tiem. Tāpat ņemti vērā visi kopēŔanas procesi uz un no videokartes atmiņas.

PaÅ”as hash tabulas darbÄ«ba aizņēma 271 ms. Tas ietver laiku, ko videokarte iztērēja elementu ievietoÅ”anai un dzēŔanai, un neietver laiku kopēŔanai uz atmiņu un iterēŔanai pa iegÅ«to tabulu. Ja GPU tabula dzÄ«vo ilgi, vai ja hash tabula ir pilnÄ«gi videokartes atmiņā (piemēram, hash tabulas izveidoÅ”anai, ko izmantos cits GPU kods, nevis centrālais procesors), tad testa rezultāti ir nozÄ«mÄ«gi.

Hash tabula videokartei demonstrē augstu veiktspēju, pateicoties lielai caurlaidspējai un aktīvai paralēlajai izpildei.

Trūkumi

Hash tabulas arhitektūrai ir dažas problēmas, par kurām jāatceras:

  • LÄ«nijas izpēte traucē klasterizācija, dēļ kuras atslēgas tabulā tiek novietotas tālu neideāli.
  • Atslēgas netiek dzēstas ar funkciju izdzēst un laika gaitā aizpilda tabulu.

Kā rezultātā hash tabulas veiktspēja var pakāpeniski samazināties, Ä«paÅ”i, ja tā pastāv ilgu laiku un tajā tiek veikti daudzi ievietojumi un dzēŔanas procesi. Viens no veidiem, kā mazināt Å”os trÅ«kumus, ir atkārtota kopÅ”ana uz jaunu tabulu ar pietiekami zemu izmantoÅ”anas koeficientu un dzēsto atslēgu filtrācija atkārtotā kopÅ”anā.

Lai ilustrētu iepriekÅ” minētās problēmas, izmantoÅ”u iepriekÅ” minēto kodu, lai izveidotu tabulu ar 128 miljoniem elementu, cikliski ievietoÅ”u 4 miljonus elementu, lÄ«dz aizpildīŔu 124 miljonus slotu (izmantoÅ”anas koeficients apmēram 0,96). Å eit ir rezultātu tabula, katra rinda ir CUDA kodola izsaukums ar 4 miljonu jauno elementu ievietoÅ”anu vienā hash tabulā:

IzmantoŔanas koeficients
IevietoŔanas ilgums 4 194 304 elementiem

0,00
11,608448 ms (361,314798 miljoni atslēgu/s)

0,03
11,751424 ms (356,918799 miljoni atslēgu/s)

0,06
11,942592 ms (351,205515 miljoni atslēgu/s)

0,09
12,081120 ms (347,178429 miljoni atslēgu/s)

0,12
12,242560 ms (342,600233 miljoni atslēgu/s)

0,16
12,396448 ms (338,347235 miljoni atslēgu/s)

0,19
12,533024 ms (334,660176 miljoni atslēgu/s)

0,22
12,703328 ms (330,173626 miljoni atslēgu/s)

0,25
12,884512 ms (325,530693 miljoni atslēgu/s)

0,28
13,033472 ms (321,810182 miljoni atslēgu/s)

0,31
13,239296 ms (316,807174 miljoni atslēgu/s)

0,34
13,392448 ms (313,184256 miljoni atslēgu/s)

0,37
13,624000 ms (307,861434 miljoni atslēgu/s)

0,41
13,875520 ms (302,280855 miljoni atslēgu/s)

0,44
14,126528 ms (296,909756 miljoni atslēgu/s)

0,47
14,399328 ms (291,284699 miljoni atslēgu/s)

0,50
14,690304 ms (285,515123 miljoni atslēgu/s)

0,53
15,039136 ms (278,892623 miljoni atslēgu/s)

0,56
15,478656 ms (270,973402 miljoni atslēgu/s)

0,59
15,985664 ms (262,379092 miljoni atslēgu/s)

0,62
16,668673 ms (251,627968 miljoni atslēgu/s)

0,66
17,587200 ms (238,486174 miljoni atslēgu/s)

0,69
18,690048 ms (224,413765 miljoni atslēgu/s)

0,72
20,278816 ms (206,831789 miljoni atslēgu/s)

0,75
22,545408 ms (186,038058 miljoni atslēgu/s)

0,78
26,053312 ms (160,989275 miljoni atslēgu/s)

0,81
31,895008 ms (131,503463 miljoni atslēgu/s)

0,84
42,103294 ms (99,619378 miljoni atslēgu/s)

0,87
61,849056 ms (67,815164 miljoni atslēgu/s)

0,90
105,695999 ms (39,682713 miljoni atslēgu/s)

0,94
240,204636 ms (17,461378 miljoni atslēgu/s)

Lielākam izmantoÅ”anas koeficientam samazinās veiktspēja. Tas lielākajā daļā gadÄ«jumu ir nevēlams. Ja lietotne ievieto elementus tabulā un pēc tam tos noraida (piemēram, skaitot vārdus grāmatas laikā), tas nav problēma. Bet ja lietotne izmanto ilgstoÅ”u hash tabulu (piemēram, grafiskajā redaktorā, lai uzglabātu neiztukÅ”otas attēlu daļas, kad lietotājs bieži ievieto un dzēŔ informāciju), tad Ŕāda uzvedÄ«ba var radÄ«t neērtÄ«bas.

Un es izmērÄ«ju hash tabulas zondes dziļumu pēc 64 miljoniem ievietoÅ”anas (izmantoÅ”anas koeficients 0,5). Vidējā dziļums bija 0,4774, tādēļ lielākā daļa atslēgu bija vai nu labākajos iespējamos slotos, vai vienā slotā no labākās pozÄ«cijas. Maksimālā zondes dziļums bija 60.

Pēc tam es izmērÄ«ju zondes dziļumu tabulā ar 124 miljoniem ievietoÅ”anas (izmantoÅ”anas koeficients 0,97). Vidējā dziļums jau bija 10,1757, bet maksimālā — 6474 (!!). Lineārās zondēŔanas veiktspēja strauji samazinās pie augstiem izmantoÅ”anas koeficientiem.

Vislabāk ir saglabāt Å”ai hash tabulai zemu izmantoÅ”anas koeficients. Tomēr tādējādi mēs paaugstinām veiktspēju, palielinot atmiņas patēriņu. Par laimi, 32 bitu atslēgu un vērtÄ«bu gadÄ«jumā, tas var bÅ«t pamatots. Ja iepriekÅ” minētajā piemērā tabulā ar 128 miljoniem elementu saglabāsim izmantoÅ”anas koeficientu 0,25, tad mēs varēsim tajā novietot ne vairāk kā 32 miljonus elementu, bet pārējie 96 miljoni vietu tiks zaudēti — pa 8 baitiem uz katru pāri, 768 Mb zaudētās atmiņas.

LÅ«dzu, ņemiet vērā, ka Å”eit runa ir par videokartes zaudēto atmiņu, kas ir vērtÄ«gāks resurss nekā sistēmas atmiņa. Lai gan lielākajai daļai mÅ«sdienu galddatoru videokartēm, kas atbalsta CUDA, ir vismaz 4 Gb atmiņas (rakstīŔanas brÄ«dÄ« NVIDIA 2080 Ti ir 11 Gb), tomēr zaudēt Ŕādus apjomus nebÅ«s prātÄ«gi.

Vēlāk es sÄ«kāk rakstīŔu par hash tabulu izveidi videokartēm, kurām nav problēmu ar probe dziļumu, kā arÄ« par veidiem, kā atkārtoti izmantot dzēsto vietu.

Probe dziļuma mērīŔana

Lai noteiktu atslēgas probe dziļumu, mēs varam iegūt atslēgas hash (tās ideālo indeksu tabulā) no tā faktiskā tabulas indeksa:

// get_key_index() -> index of key in hash table
uint32_t probelength = (get_key_index(key) - hash(key)) & (hashtablecapacity-1);

Dēļ divu bināro skaitļu burvÄ«bas papildināŔanas kodā un fakts, ka hash tabulas ietilpÄ«ba ir divi spēkā, Å”is pieejas darbosies pat tad, ja atslēgas indekss tiek pārvietots tabulas sākumā. Ņemsim atslēgu, kas hashējas 1, bet ir ievietota vietā 3. Tādējādi tabulai ar ietilpÄ«bu 4 mēs iegÅ«sim (3 — 1) & 3, kas ir ekvivalents 2.

Secinājums

Ja jums ir jautājumi vai komentāri, rakstiet man uz Twitter vai atveriet jaunu tēmu repozitorijā.

Šis kods ir rakstīts iedvesmojoties no brīniŔķīgām rakstiem:

Nākotnē es turpināŔu rakstÄ«t par hash tabulu realizācijām videokartēm un analizēŔu to veiktspēju. Manā plānā ir ķēdes saistīŔana, Robina Huda hashēŔana un kukaiÅ”a hashēŔana, izmantojot atomiskās operācijas datu struktÅ«rās, kuras ir piemērotas videokartēm.

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster