{"id":24375885,"url":"https://github.com/csvancea/gpu-hashtable","last_synced_at":"2026-04-24T23:31:54.166Z","repository":{"id":130856206,"uuid":"369838759","full_name":"csvancea/GPU-HashTable","owner":"csvancea","description":"GPU-backed linear-probing hash table implemented in CUDA. Supports batch operations such as insert and retrieval.","archived":false,"fork":false,"pushed_at":"2021-05-23T15:19:09.000Z","size":28,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-16T09:15:05.188Z","etag":null,"topics":["cuda","hashtable"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/csvancea.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-05-22T15:10:57.000Z","updated_at":"2023-05-21T12:55:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"623298e6-4cf2-42f8-86a1-cacfafc9e4bd","html_url":"https://github.com/csvancea/GPU-HashTable","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/csvancea/GPU-HashTable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csvancea%2FGPU-HashTable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csvancea%2FGPU-HashTable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csvancea%2FGPU-HashTable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csvancea%2FGPU-HashTable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/csvancea","download_url":"https://codeload.github.com/csvancea/GPU-HashTable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/csvancea%2FGPU-HashTable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32245044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T13:21:15.438Z","status":"ssl_error","status_checked_at":"2026-04-24T13:21:15.005Z","response_time":64,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cuda","hashtable"],"created_at":"2025-01-19T05:58:30.820Z","updated_at":"2026-04-24T23:31:54.160Z","avatar_url":"https://github.com/csvancea.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GPU HashTable - Cosmin-Răzvan VANCEA - 333CA\n\nPentru rezolvarea temei am ales să implementez un Hash Table cu linear-probing\nca metodă de rezolvare a coliziunilor.\n\n## Implementare\n\nTabela de dispersie este stocată ca un vector de structuri `kv` (cheie-valoare).\n\n### Operația de instanțiere\n\nLa instanțierea unei clase de tip `GpuHashTable`, aloc în memoria GPU tabela de\ndispersie de capacitate totală `size` cu toate elementele setate la `KEY_INVALID`,\nmarcând astfel că toate pozițiile sunt neocupate.\n\n### Operația de inserare/actualizare (`insertBatch`)\n\nFiecare pereche de tip cheie-valoare este scrisă de câte un thread GPU. Kernel-ul\ncalculează hash-ul cheii, apoi iterează prin tabela `kv` începând de la poziția\n`hash % size`, câutând fie:\n\n- un spațiu liber (și inserează acolo perechea)\n- un spațiu unde se află deja cheia (și se face actualizarea valorii)\n\nVerificarea și actualizarea unei anumite poziții din tabela `kv` se face atomic\nfolosind `atomicCAS`, și anume se compară valoarea cheii de la indexul curent cu\n`KEY_INVALID` și se scrie noua cheie doar dacă comparația returnează adevărat,\nastfel garantându-se că două thread-uri nu pot scrie în aceeași locație (doar\nunul va reuși să rescrie cheia de la poziția liberă).\n\nUn caz special este operația de actualizare: nu este nevoie de scriere atomică\ndeoarece un singur thread (cel asociat perechii cheie-valoare) va încerca să\nscrie la poziția în cauză.\n\nPentru calcularea corectă a load factor-ului, în kernel incrementez un contor\nde fiecare dată când se face o operație de INSERT exclusiv. Nu puteam face\nacest lucru direct din host deoarece perechile primite în `insertBatch` pot\nsă necesite o operație de tip insert sau update, acest tip putând fi determinat\ndoar în momentul în care se interoghează tabela (deci în kernel).\n\nÎn cazul în care host-ul determină că tabela nu are destule spații libere\npentru a face operația de insert, se execută `RESHAPE` (detalii mai jos).\n\n### Operația de căutare (`getBatch`)\n\nFiecare cheie este căutată de câte un thread GPU. Kernel-ul calculează hash-ul\ncheii, apoi iterează prin tabela `kv` începând de la poziția\n`hash % size`, câutând spațiul unde se află cheia. Dacă găsește cheia, salvează\nvaloarea asociată.\n\nÎn cazul în care nu se găsește cheia in `size` iterații (adică după ce am\nparcurs toată tabela), inseamnă că aceasta nu există și se returneaza valoarea\n`KEY_INVALID` (0) pentru cheia asociată.\n\n### Operația de redimensionare (`reshape`)\n\nSe alocă o tabelă nouă, iar pentru fiecare slot din tabela veche există un thread\nGPU care va insera perechea cheie-valoare de la acel slot (dacă există) în noua\ntabelă. (Re)inserarea este implementată ca mai sus.\n\nÎn primele încercări de rezolvare, tabela `kv` era redimensionată doar atunci\ncând nu mai erau destule spații libere pentru o operație de insert, iar\nredimensionarea se făcea dublând capacitatea actuală până când:\n\n```text\n    new_capacity \u003e= num_of_items_to_insert + num_of_items_already_in_table\n```\n\nÎn alte cuvinte, dublarea se oprea atunci când aveam destule spații libere\nîn `kv` pentru a realiza cu succes operația de insert. Deși ideea de rezolvare\nera corectă, iar testele treceau (dublând mereu dimensiunea se respecta cerința\nca load factor-ul să fie între 0.5 și 1.0), timpul de execuție era lent deoarece\npractic redimensionarea se făcea doar atunci când load factor-ul ajungea la o\nvaloare apropiată de 1.0.\n\nCum tabela de dispersie folosește în spate o logică de tip linear-probing,\ncu cât load factor-ul este mai mare, cu atât distanța dintre poziția ideală a\ncheii în tabelă (cea dată de funcția de hash) și poziția reală la care este\nplasată cheia (unde se găsește un spațiu liber) devine din ce în ce mai mare,\niar astfel operațiile de căutare și inserare devin mai costisitoare din punct\nde vedere temporal. (on the bright side: memoria folosită este aproximativ O(n))\n\nPentru rezolvarea acestei probleme, am ales să schimb logica de redimensionare\ncu una care îmi permite să forțez load factor-ul să fie mereu într-un anumit\ninterval stabilit. Am ales acest interval să fie [0.65, 0.80], favorizând\nastfel într-o mică măsura timpul în detrimentul spațiului.\n\n## Rezultate\n\n```text\n------- Test T1 START   ----------\n\nHASH_BATCH_INSERT   count: 1000000          speed: 83M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 1000000          speed: 85M/sec          loadfactor: 65%\n----------------------------------------------\nAVG_INSERT: 83 M/sec,   AVG_GET: 85 M/sec,      MIN_SPEED_REQ: 10 M/sec\n\n------- Test T1 END     ----------       [ OK RESULT: +20 pts ]\n\n\n\n------- Test T2 START   ----------\n\nHASH_BATCH_INSERT   count: 500000           speed: 78M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 500000           speed: 69M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 500000           speed: 89M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 500000           speed: 141M/sec         loadfactor: 65%\n----------------------------------------------\nAVG_INSERT: 74 M/sec,   AVG_GET: 115 M/sec,     MIN_SPEED_REQ: 20 M/sec\n\n------- Test T2 END     ----------       [ OK RESULT: +20 pts ]\n\n\n\n------- Test T3 START   ----------\n\nHASH_BATCH_INSERT   count: 125000           speed: 51M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 125000           speed: 56M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 125000           speed: 49M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 125000           speed: 43M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 125000           speed: 37M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 125000           speed: 57M/sec          loadfactor: 78%\nHASH_BATCH_INSERT   count: 125000           speed: 30M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 125000           speed: 58M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 65M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 95M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 96M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 95M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 95M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 96M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 90M/sec          loadfactor: 74%\nHASH_BATCH_GET      count: 125000           speed: 85M/sec          loadfactor: 74%\n----------------------------------------------\nAVG_INSERT: 48 M/sec,   AVG_GET: 90 M/sec,      MIN_SPEED_REQ: 40 M/sec\n\n------- Test T3 END     ----------       [ OK RESULT: +15 pts ]\n\n\n\n------- Test T4 START   ----------\n\nHASH_BATCH_INSERT   count: 2500000          speed: 89M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 2500000          speed: 77M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 2500000          speed: 65M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 2500000          speed: 56M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 2500000          speed: 99M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 2500000          speed: 152M/sec         loadfactor: 65%\nHASH_BATCH_GET      count: 2500000          speed: 152M/sec         loadfactor: 65%\nHASH_BATCH_GET      count: 2500000          speed: 145M/sec         loadfactor: 65%\n----------------------------------------------\nAVG_INSERT: 72 M/sec,   AVG_GET: 137 M/sec,     MIN_SPEED_REQ: 50 M/sec\n\n------- Test T4 END     ----------       [ OK RESULT: +15 pts ]\n\n\n\n------- Test T5 START   ----------\n\nHASH_BATCH_INSERT   count: 20000000         speed: 81M/sec          loadfactor: 65%\nHASH_BATCH_INSERT   count: 20000000         speed: 69M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 20000000         speed: 90M/sec          loadfactor: 65%\nHASH_BATCH_GET      count: 20000000         speed: 106M/sec         loadfactor: 65%\n----------------------------------------------\nAVG_INSERT: 75 M/sec,   AVG_GET: 98 M/sec,      MIN_SPEED_REQ: 50 M/sec\n\n------- Test T5 END     ----------       [ OK RESULT: +15 pts ]\n\nTOTAL gpu_hashtable  85/85\n```\n\nOperațiile `GET` se execută mai rapid, ceea ce era de așteptat deoarece:\n\n1. kernel-urile nu conțin operații atomice\n2. se fac mai puține alocări de memorie în VRAM (doar una pentru input/output)\n3. nu se redimensionează tabela\n4. se fac doar citiri\n\nLegat de operațiile `INSERT`, viteza de scriere scade cu cât sunt mai multe\nelemente inserate în vector, însă crește după redimensionare. Acest lucru se\npoate datora coliziunilor și procesului de rezolvare al acestora, după cum am\nmenționat și anterior în paragraful despre `RESHAPE`. (TL;DR: din cauza\ndistanței dintre poziția ideală și cea reală la care este plasată perechea\ncheie-valoare)\n\n## Bibliografie\n\n- [Laboratoare CUDA ASC](https://ocw.cs.pub.ro/courses/asc/laboratoare/08)\n- [Simple Lock Free Hash Table (ideea de a avea 1 thread GPU per element de inserat/căutat)](https://nosferalatu.com/SimpleGPUHashTable.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcsvancea%2Fgpu-hashtable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcsvancea%2Fgpu-hashtable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcsvancea%2Fgpu-hashtable/lists"}