Github์ ์ฌ๋ ธ์ด์
์ด๋น ์์ต ๊ฐ์ ์ฝ์ ์ ์ฒ๋ฆฌํ ์ ์๋ ๊ฐ๋จํ GPU ํด์ ํ ์ด๋ธ์ ๋๋ค. ๋ด NVIDIA GTX 1060 ๋ ธํธ๋ถ์์ ์ฝ๋๋ ์ฝ 64ms ์์ ๋ฌด์์๋ก ์์ฑ๋ 210๋ง ๊ฐ์ ํค-๊ฐ ์์ ์ฝ์ ํ๊ณ ์ฝ 32ms ์์ 64๋ง ์์ ์ ๊ฑฐํฉ๋๋ค.
์ฆ, ๋ฉํ์ ์๋๋ ์ด๋น ์ฝ์ 300์ต ํ, ์ญ์ 500์ต ํ/์ด์ ๋๋ค.
ํ ์ด๋ธ์ CUDA๋ก ์์ฑ๋์์ง๋ง HLSL์ด๋ GLSL์๋ ๋์ผํ ๊ธฐ์ ์ ์ ์ฉํ ์ ์์ต๋๋ค. ๊ตฌํ์๋ ๋น๋์ค ์นด๋์ ๊ณ ์ฑ๋ฅ์ ๋ณด์ฅํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ์ ํ ์ฌํญ์ด ์์ต๋๋ค.
- 32๋นํธ ํค์ ๋์ผํ ๊ฐ๋ง ์ฒ๋ฆฌ๋ฉ๋๋ค.
- ํด์ ํ ์ด๋ธ์ ํฌ๊ธฐ๋ ๊ณ ์ ๋์ด ์์ต๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ์ด ํฌ๊ธฐ๋ XNUMX์ ๊ฑฐ๋ญ์ ๊ณฑ๊ณผ ๊ฐ์์ผ ํฉ๋๋ค.
ํค์ ๊ฐ์ ๊ฒฝ์ฐ ๊ฐ๋จํ ๊ตฌ๋ถ ๊ธฐํธ๋ฅผ ์์ฝํด์ผ ํฉ๋๋ค(์ ์ฝ๋์์๋ 0xffffffff์).
์ ๊ธ์ด ์๋ ํด์ ํ ์ด๋ธ
ํด์ ํ
์ด๋ธ์ ๋ค์๊ณผ ๊ฐ์ ๊ฐ๋ฐฉํ ์ฃผ์ ์ง์ ์ ์ฌ์ฉํฉ๋๋ค. KeyValue
:
struct KeyValue
{
uint32_t key;
uint32_t value;
};
ํ ์ด๋ธ์ ํฌ๊ธฐ๋ ์์๊ฐ ์๋ 2์ ๊ฑฐ๋ญ์ ๊ณฑ์ ๋๋ค. ์๋ํ๋ฉด ํ๋์ ๋น ๋ฅธ ๋ช ๋ น์ด๋ก powXNUMX/AND ๋ง์คํฌ๋ฅผ ์ ์ฉํ๋ ๋ฐ ์ถฉ๋ถํ์ง๋ง ๋ชจ๋๋ฌ์ค ์ฐ์ฐ์๋ ํจ์ฌ ๋๋ฆฌ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ด๋ ์ ํ ํ๋ก๋น์ ๊ฒฝ์ฐ ์ค์ํฉ๋๋ค. ์ ํ ํ ์ด๋ธ ์กฐํ์์๋ ์ฌ๋กฏ ์ธ๋ฑ์ค๊ฐ ๊ฐ ์ฌ๋กฏ์ ๋ํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์์ ๋น์ฉ์ ๊ฐ ์ฌ๋กฏ์ ๋ชจ๋๋ก ์ถ๊ฐ๋ฉ๋๋ค.
ํ ์ด๋ธ์๋ ํค์ ํด์๊ฐ ์๋ ๊ฐ ์์์ ํค์ ๊ฐ๋ง ์ ์ฅ๋ฉ๋๋ค. ํ ์ด๋ธ์๋ 32๋นํธ ํค๋ง ์ ์ฅ๋๋ฏ๋ก ํด์๊ฐ ๋งค์ฐ ๋น ๋ฅด๊ฒ ๊ณ์ฐ๋ฉ๋๋ค. ์์ ์ฝ๋๋ ๋ช ๋ฒ์ ๊ต๋, XOR ๋ฐ ๊ณฑ์ ๋ง ์ํํ๋ Murmur3 ํด์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํด์ ํ ์ด๋ธ์ ๋ฉ๋ชจ๋ฆฌ ์์์ ๋ฌด๊ดํ ์ ๊ธ ๋ณดํธ ๊ธฐ์ ์ ์ฌ์ฉํฉ๋๋ค. ์ผ๋ถ ์ฐ๊ธฐ ์์ ์ด ๋ค๋ฅธ ์์ ์ ์์๋ฅผ ๋ฐฉํดํ๋๋ผ๋ ํด์ ํ ์ด๋ธ์ ์ฌ์ ํ โโ์ฌ๋ฐ๋ฅธ ์ํ๋ฅผ ์ ์งํฉ๋๋ค. ์ด์ ๋ํด์๋ ์๋์์ ์ด์ผ๊ธฐํ๊ฒ ์ต๋๋ค. ์ด ๊ธฐ์ ์ ์์ฒ ๊ฐ์ ์ค๋ ๋๋ฅผ ๋์์ ์คํํ๋ ๋น๋์ค ์นด๋์์ ํ๋ฅญํ๊ฒ ์๋ํฉ๋๋ค.
ํด์ ํ ์ด๋ธ์ ํค์ ๊ฐ์ ๋น์ด ์๋๋ก ์ด๊ธฐํ๋ฉ๋๋ค.
64๋นํธ ํค์ ๊ฐ๋ ์ฒ๋ฆฌํ๋๋ก ์ฝ๋๋ฅผ ์์ ํ ์ ์์ต๋๋ค. ํค์๋ ์์์ฑ ์ฝ๊ธฐ, ์ฐ๊ธฐ, ๋น๊ต ๋ฐ โโ๊ตํ ์์
์ด ํ์ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์๋ ์์์ฑ ์ฝ๊ธฐ ๋ฐ ์ฐ๊ธฐ ์์
์ด ํ์ํฉ๋๋ค. ๋คํ์ค๋ฝ๊ฒ๋ CUDA์์๋ 32๋นํธ ๋ฐ 64๋นํธ ๊ฐ์ ๋ํ ์ฝ๊ธฐ-์ฐ๊ธฐ ์์
์ด ์์ฐ์ค๋ฝ๊ฒ ์ ๋ ฌ๋๋ ํ ์์์ ์
๋๋ค(์๋ ์ฐธ์กฐ).
ํด์ ํ ์ด๋ธ ์ํ
ํด์ ํ ์ด๋ธ์ ๊ฐ ํค-๊ฐ ์์ ๋ค์ ๋ค ๊ฐ์ง ์ํ ์ค ํ๋๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค.
- ํค์ ๊ฐ์ด ๋น์ด ์์ต๋๋ค. ์ด ์ํ์์ ํด์ ํ ์ด๋ธ์ด ์ด๊ธฐํ๋ฉ๋๋ค.
- ํค๋ ๊ธฐ๋ก๋์์ง๋ง ๊ฐ์ ์์ง ๊ธฐ๋ก๋์ง ์์์ต๋๋ค. ๋ค๋ฅธ ์ค๋ ๋๊ฐ ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์์ผ๋ฉด ๋น์ด ์๋ ์ค๋ ๋๋ฅผ ๋ฐํํฉ๋๋ค. ์ด๊ฒ์ ์ ์์ ์ธ ํ์์ ๋๋ค. ๋ค๋ฅธ ์คํ ์ค๋ ๋๊ฐ ์กฐ๊ธ ๋ ์ผ์ฐ ์๋ํ๋ค๋ฉด ๋์ผํ ์ผ์ด ๋ฐ์ํ์ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์๋ ๋์ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋ํด ์ด์ผ๊ธฐํ๊ณ ์์ต๋๋ค.
- ํค์ ๊ฐ์ด ๋ชจ๋ ๊ธฐ๋ก๋ฉ๋๋ค.
- ๊ฐ์ ๋ค๋ฅธ ์คํ ์ค๋ ๋์์ ์ฌ์ฉํ ์ ์์ง๋ง ํค๋ ์์ง ์์ต๋๋ค. ์ด๋ CUDA ํ๋ก๊ทธ๋๋ฐ ๋ชจ๋ธ์ ๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ธ ์์๊ฐ ๋์จํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ ์ ์์ ์ธ ํ์์ ๋๋ค. ์ด๋ค ๊ฒฝ์ฐ์๋ ๊ฐ์ด ๋ ์ด์ ๋น์ด ์์ง ์๋๋ผ๋ ํค๋ ์ฌ์ ํ ๋น์ด ์์ต๋๋ค.
์ค์ํ ์ฐจ์ด์ ์ ํค๊ฐ ์ฌ๋กฏ์ ๊ธฐ๋ก๋๋ฉด ๋ ์ด์ ์์ง์ด์ง ์๋๋ค๋ ๊ฒ์ ๋๋ค. ํค๊ฐ ์ญ์ ๋๋๋ผ๋ ์ด์ ๋ํด์๋ ์๋์์ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
ํด์ ํ ์ด๋ธ ์ฝ๋๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ ์ฐ๋ ์์๋ฅผ ์ ์ ์๋ ๋์จํ๊ฒ ์ ๋ ฌ๋ ๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ธ์์๋ ์๋ํฉ๋๋ค. ํด์ ํ ์ด๋ธ์ ์ฝ์ , ์กฐํ, ์ญ์ ๋ฅผ ์ดํด๋ณด๋ฉด์ ๊ฐ ํค-๊ฐ ์์ด ์์์ ์ค๋ช ํ ๋ค ๊ฐ์ง ์ํ ์ค ํ๋๋ผ๋ ์ ์ ๊ธฐ์ตํ์ธ์.
ํด์ ํ ์ด๋ธ์ ์ฝ์
ํด์ ํ ์ด๋ธ์ ํค-๊ฐ ์์ ์ฝ์ ํ๋ CUDA ํจ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
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);
}
}
ํค๋ฅผ ์ฝ์ ํ๊ธฐ ์ํด ์ฝ๋๋ ์ฝ์ ๋ ํค์ ํด์๋ถํฐ ์์ํ์ฌ ํด์ ํ ์ด๋ธ ๋ฐฐ์ด์ ๋ฐ๋ณตํฉ๋๋ค. ๋ฐฐ์ด์ ๊ฐ ์ฌ๋กฏ์ ํด๋น ์ฌ๋กฏ์ ํค๊ฐ ๋น์ด ์๋์ง ๋น๊ตํ๋ ์์ ๋น๊ต ๋ฐ โโ๊ตํ ์์ ์ ์ํํฉ๋๋ค. ๋ถ์ผ์น๊ฐ ๊ฐ์ง๋๋ฉด ์ฌ๋กฏ์ ํค๊ฐ ์ฝ์ ๋ ํค๋ก ์ ๋ฐ์ดํธ๋ ๋ค์ ์๋ ์ฌ๋กฏ ํค๊ฐ ๋ฐํ๋ฉ๋๋ค. ์ด ์๋ ํค๊ฐ ๋น์ด ์๊ฑฐ๋ ์ฝ์ ๋ ํค์ ์ผ์นํ๋ ๊ฒฝ์ฐ ์ฝ๋๋ ์ฝ์ ์ ์ ํฉํ ์ฌ๋กฏ์ ์ฐพ์ ์ฝ์ ๋ ๊ฐ์ ์ฌ๋กฏ์ ์ฝ์ ํฉ๋๋ค.
ํ๋์ ์ปค๋ ํธ์ถ์ ์๋ ๊ฒฝ์ฐ gpu_hashtable_insert()
๋์ผํ ํค๋ฅผ ๊ฐ์ง ์ฌ๋ฌ ์์๊ฐ ์๋ ๊ฒฝ์ฐ ํด๋น ๊ฐ ์ค ํ๋๋ฅผ ํค ์ฌ๋กฏ์ ์ธ ์ ์์ต๋๋ค. ์ด๋ ์ ์์ ์ธ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋ฉ๋๋ค. ํธ์ถ ์ค ํค-๊ฐ ์ฐ๊ธฐ ์ค ํ๋๊ฐ ์ฑ๊ณตํ์ง๋ง ์ด ๋ชจ๋ ๊ฒ์ด ์ฌ๋ฌ ์คํ ์ค๋ ๋ ๋ด์์ ๋ณ๋ ฌ๋ก ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ์ด๋ค ๋ฉ๋ชจ๋ฆฌ ์ฐ๊ธฐ๊ฐ ๋ง์ง๋ง์ด ๋ ์ง ์์ธกํ ์ ์์ต๋๋ค.
ํด์ ํ ์ด๋ธ ์กฐํ
ํค ๊ฒ์ ์ฝ๋:
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);
}
}
ํ ์ด๋ธ์ ์ ์ฅ๋ ํค ๊ฐ์ ์ฐพ์ผ๋ ค๋ฉด ์ฐพ๊ณ ์๋ ํค์ ํด์๋ถํฐ ์์ํ์ฌ ๋ฐฐ์ด์ ๋ฐ๋ณตํฉ๋๋ค. ๊ฐ ์ฌ๋กฏ์์ ํค๊ฐ ์ฐ๋ฆฌ๊ฐ ์ฐพ๊ณ ์๋ ํค์ธ์ง ํ์ธํ๊ณ , ๊ทธ๋ ๋ค๋ฉด ๊ทธ ๊ฐ์ ๋ฐํํฉ๋๋ค. ๋ํ ํค๊ฐ ๋น์ด ์๋์ง ํ์ธํ๊ณ , ๊ทธ๋ ๋ค๋ฉด ๊ฒ์์ ์ค๋จํฉ๋๋ค.
ํค๋ฅผ ์ฐพ์ ์ ์์ผ๋ฉด ์ฝ๋๋ ๋น ๊ฐ์ ๋ฐํํฉ๋๋ค.
์ด๋ฌํ ๋ชจ๋ ๊ฒ์ ์์ ์ ์ฝ์ ๊ณผ ์ญ์ ๋ฅผ ํตํด ๋์์ ์ํ๋ ์ ์์ต๋๋ค. ํ ์ด๋ธ์ ๊ฐ ์์ ํ๋ฆ์ ๋ํด ์์์ ์ค๋ช ํ ๋ค ๊ฐ์ง ์ํ ์ค ํ๋๋ฅผ ๊ฐ์ต๋๋ค.
ํด์ ํ ์ด๋ธ์์ ์ญ์
ํค ์ญ์ ์ฝ๋:
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);
}
}
ํค ์ญ์ ๋ ํน์ดํ ๋ฐฉ์์ผ๋ก ์ํ๋ฉ๋๋ค. ํค๋ฅผ ํ
์ด๋ธ์ ๊ทธ๋๋ก ๋๊ณ ํด๋น ๊ฐ(ํค ์์ฒด๊ฐ ์๋)์ ๋น์ด ์๋ ๊ฒ์ผ๋ก ํ์ํฉ๋๋ค. ์ด ์ฝ๋๋ ๋ค์๊ณผ ๋งค์ฐ ์ ์ฌํฉ๋๋ค. lookup()
๋จ, ํค์์ ์ผ์นํ๋ ํญ๋ชฉ์ด ๋ฐ๊ฒฌ๋๋ฉด ํด๋น ๊ฐ์ด ๋น์ด ์๊ฒ ๋ฉ๋๋ค.
์์์ ์ธ๊ธํ ๊ฒ์ฒ๋ผ ํค๊ฐ ์ฌ๋กฏ์ ๊ธฐ๋ก๋๋ฉด ๋ ์ด์ ์ด๋๋์ง ์์ต๋๋ค. ํ ์ด๋ธ์์ ์์๊ฐ ์ญ์ ๋๋๋ผ๋ ํค๋ ๊ทธ๋๋ก ์ ์ง๋๋ฉฐ ํด๋น ๊ฐ์ ๋จ์ํ ๋น์ด ์๊ฒ ๋ฉ๋๋ค. ์ฆ, ํ์ฌ ๊ฐ์ด ๋น์ด ์๋์ง ์ฌ๋ถ๋ ์ค์ํ์ง ์์ผ๋ฉฐ ์ฌ์ ํ ๋น์ด ์๊ธฐ ๋๋ฌธ์ ์ฌ๋กฏ ๊ฐ์ ์์์ฑ ์ฐ๊ธฐ ์์ ์ ์ฌ์ฉํ ํ์๊ฐ ์์ต๋๋ค.
ํด์ ํ ์ด๋ธ ํฌ๊ธฐ ์กฐ์
๋ ํฐ ํ ์ด๋ธ์ ๋ง๋ค๊ณ ์ด์ ํ ์ด๋ธ์ ๋น์ด ์์ง ์์ ์์๋ฅผ ์ฌ๊ธฐ์ ์ฝ์ ํ์ฌ ํด์ ํ ์ด๋ธ์ ํฌ๊ธฐ๋ฅผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ์ํ ์ฝ๋๋ฅผ ๋จ์ํ๊ฒ ์ ์งํ๊ณ ์ถ์๊ธฐ ๋๋ฌธ์ ์ด ๊ธฐ๋ฅ์ ๊ตฌํํ์ง ์์์ต๋๋ค. ๊ฒ๋ค๊ฐ CUDA ํ๋ก๊ทธ๋จ์์๋ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด CUDA ์ปค๋์ด ์๋ ํธ์คํธ ์ฝ๋์์ ์ํ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
๊ธฐ์ฌ
๊ฒฝ์๋ ฅ
์์ ํจ์ ์ฝ๋ ์กฐ๊ฐ์์ gpu_hashtable_insert()
, _lookup()
ะธ _delete()
ํ ๋ฒ์ ํ๋์ ํค-๊ฐ ์์ ์ฒ๋ฆฌํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ฎ์ gpu_hashtable_insert()
, _lookup()
ะธ _delete()
๊ฐ ์์ ๋ณ๋์ GPU ์คํ ์ค๋ ๋์์ ๋ณ๋ ฌ๋ก ์์ ๋ฐฐ์ด์ ์ฒ๋ฆฌํฉ๋๋ค.
// 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);
}
}
์ ๊ธ ๋ฐฉ์ง ํด์ ํ ์ด๋ธ์ ๋์ ์ฝ์ , ์กฐํ ๋ฐ ์ญ์ ๋ฅผ ์ง์ํฉ๋๋ค. ํค-๊ฐ ์์ ํญ์ ๋ค ๊ฐ์ง ์ํ ์ค ํ๋์ ์๊ณ ํค๊ฐ ์ด๋ํ์ง ์๊ธฐ ๋๋ฌธ์ ์๋ก ๋ค๋ฅธ ์ ํ์ ์์ ์ด ๋์์ ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ์๋ ํ ์ด๋ธ์ ์ ํ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
๊ทธ๋ฌ๋ ์ผ๊ด ์ฝ์
๋ฐ ์ญ์ ๋ฅผ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ๊ณ ์์ ์
๋ ฅ ๋ฐฐ์ด์ ์ค๋ณต ํค๊ฐ ํฌํจ๋์ด ์์ผ๋ฉด ์ด๋ค ์์ด "์น๋ฆฌ"ํ ์ง ์์ธกํ ์ ์์ผ๋ฉฐ ๋ง์ง๋ง์ ํด์ ํ
์ด๋ธ์ ๊ธฐ๋ก๋ฉ๋๋ค. ์์ ์
๋ ฅ ๋ฐฐ์ด์ ์ฌ์ฉํ์ฌ ์ฝ์
์ฝ๋๋ฅผ ํธ์ถํ๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. A/0 B/1 A/2 C/3 A/4
. ์ฝ๋๊ฐ ์๋ฃ๋๋ฉด ์์ ์ด๋ฃน๋๋ค. B/1
ะธ C/3
ํ
์ด๋ธ์ ์กด์ฌํ๋ ๊ฒ์ด ๋ณด์ฅ๋์ง๋ง ๋์์ ๋ชจ๋ ์์ด ํ
์ด๋ธ์ ๋ํ๋ฉ๋๋ค. A/0
, A/2
๋๋ A/4
. ์ด๋ ๋ฌธ์ ๊ฐ ๋ ์๋ ์๊ณ ์๋ ์๋ ์์ต๋๋ค. ๋ชจ๋ ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ฐ๋ผ ๋ค๋ฆ
๋๋ค. ์
๋ ฅ ๋ฐฐ์ด์ ์ค๋ณต๋ ํค๊ฐ ์๋ค๋ ๊ฒ์ ๋ฏธ๋ฆฌ ์ ์๋ ์๊ณ , ์ด๋ค ๊ฐ์ด ๋ง์ง๋ง์ ๊ธฐ๋ก๋์๋์ง ์ ๊ฒฝ์ฐ์ง ์์ ์๋ ์์ต๋๋ค.
์ด๊ฒ์ด ๋ฌธ์ ๊ฐ ๋๋ค๋ฉด ์ค๋ณต๋ ์์ ๋ค๋ฅธ CUDA ์์คํ
ํธ์ถ๋ก ๋ถ๋ฆฌํด์ผ ํฉ๋๋ค. CUDA์์ ์ปค๋์ ํธ์ถํ๋ ๋ชจ๋ ์์
์ ํญ์ ๋ค์ ์ปค๋ ํธ์ถ ์ ์ ์๋ฃ๋ฉ๋๋ค(์ ์ด๋ ํ๋์ ์ค๋ ๋ ๋ด์์. ๋ค๋ฅธ ์ค๋ ๋์์๋ ์ปค๋์ด ๋ณ๋ ฌ๋ก ์คํ๋ฉ๋๋ค). ์์ ์์์ ํ๋์ ์ปค๋์ ํธ์ถํ๋ฉด A/0 B/1 A/2 C/3
, ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ A/4
, ๊ทธ ๋ค์ ์ด์ A
๊ฐ์ ์ป์ ๊ฒ์ด๋ค 4
.
์ด์ ํจ์๊ฐ lookup()
ะธ delete()
ํด์ ํ
์ด๋ธ์ ์ ๋ฐฐ์ด์ ๋ํ ์ผ๋ฐ ๋๋ ํ๋ฐ์ฑ ํฌ์ธํฐ๋ฅผ ์ฌ์ฉํ์ญ์์ค.
์ปดํ์ผ๋ฌ๋ ์ ์ญ ๋๋ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ์ ๋ํ ์ฝ๊ธฐ ๋ฐ ์ฐ๊ธฐ๋ฅผ ์ต์ ํํ๋๋ก ์ ํํ ์ ์์ต๋๋ค. ์ด๋ฌํ ์ต์ ํ๋ ํค์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋นํ์ฑํํ ์ ์์ต๋๋ค.
volatile
: ... ์ด ๋ณ์์ ๋ํ ๋ชจ๋ ์ฐธ์กฐ๋ ์ค์ ๋ฉ๋ชจ๋ฆฌ ์ฝ๊ธฐ ๋๋ ์ฐ๊ธฐ ๋ช ๋ น์ผ๋ก ์ปดํ์ผ๋ฉ๋๋ค.
์ ํ์ฑ ๊ณ ๋ ค ์ฌํญ์๋ ์ ์ฉ์ด ํ์ํ์ง ์์ต๋๋ค. volatile
. ์คํ ์ค๋ ๋๊ฐ ์ด์ ์ฝ๊ธฐ ์์
์์ ์บ์๋ ๊ฐ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ฝ๊ฐ ์ค๋๋ ์ ๋ณด๋ฅผ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ์ปค๋ ํธ์ถ์ ํน์ ์๊ฐ์ ํด์ ํ
์ด๋ธ์ ์ฌ๋ฐ๋ฅธ ์ํ์์ ์ป์ ์ ๋ณด์
๋๋ค. ์ต์ ์ ๋ณด๋ฅผ ํ์ฉํด์ผ ํ ๊ฒฝ์ฐ ์์ธ์ ์ด์ฉํ๋ฉด ๋ฉ๋๋ค. volatile
ํ์ง๋ง ์ฑ๋ฅ์ ์ฝ๊ฐ ๊ฐ์ํฉ๋๋ค. ํ
์คํธ์ ๋ฐ๋ฅด๋ฉด 32๋ง ๊ฐ์ ์์๋ฅผ ์ญ์ ํ ๋ ์๋๊ฐ ์ด๋น 500์ต ์ญ์ ์์ ์ด๋น 450์ต XNUMX์ฒ๋ง ์ญ์ ๋ก ๊ฐ์ํ์ต๋๋ค.
ะัะพะธะทะฒะพะดะธัะตะปัะฝะพััั
64๋ง๊ฐ ์์๋ฅผ ์ฝ์
ํ๊ณ 32๋ง๊ฐ๋ฅผ ์ญ์ ํ๋ ํ
์คํธ์์ std::unordered_map
GPU์๋ ์ฌ์ค์ ํด์ ํ
์ด๋ธ์ด ์์ต๋๋ค.
std::unordered_map
์์๋ฅผ ์ฝ์
ํ๊ณ ์ ๊ฑฐํ ํ ํด์ ํ๋ ๋ฐ 70ms๊ฐ ์์๋์์ต๋๋ค. unordered_map
(์๋ฐฑ๋ง ๊ฐ์ ์์๋ฅผ ์ ๊ฑฐํ๋ ๋ฐ๋ ๋ง์ ์๊ฐ์ด ๊ฑธ๋ฆฝ๋๋ค. unordered_map
๋ค์ค ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ด ์ด๋ฃจ์ด์ง๋๋ค). ์์งํ ๋งํ์๋ฉด, std:unordered_map
์์ ํ ๋ค๋ฅธ ์ ํ ์ฌํญ. ๋จ์ผ CPU ์คํ ์ค๋ ๋๋ก, ๋ชจ๋ ํฌ๊ธฐ์ ํค-๊ฐ์ ์ง์ํ๊ณ , ๋์ ํ์ฉ๋ฅ ์์๋ ์ข์ ์ฑ๋ฅ์ ๋ฐํํ๋ฉฐ, ์ฌ๋ฌ ๋ฒ ์ญ์ ํ์๋ ์์ ์ ์ธ ์ฑ๋ฅ์ ๋ณด์ฌ์ค๋๋ค.
GPU ๋ฐ ํ๋ก๊ทธ๋จ ๊ฐ ํต์ ์ ์ํ ํด์ ํ ์ด๋ธ์ ์ง์ ์๊ฐ์ 984ms์์ต๋๋ค. ์ฌ๊ธฐ์๋ ํ ์ด๋ธ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ฐฐ์นํ๊ณ ์ญ์ ํ๋ ๋ฐ ์์๋๋ ์๊ฐ(ํ ๋ฒ์ 1GB์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ๋ ๋ฐ CUDA์์๋ ์ฝ๊ฐ์ ์๊ฐ์ด ์์๋จ), ์์๋ฅผ ์ฝ์ ๋ฐ ์ญ์ ํ๊ณ ๋ฐ๋ณตํ๋ ๋ฐ ์์๋๋ ์๊ฐ์ด ํฌํจ๋ฉ๋๋ค. ๋น๋์ค ์นด๋ ๋ฉ๋ชจ๋ฆฌ๋ก ๋ค์ด์ค๊ณ ๋๊ฐ๋ ๋ชจ๋ ๋ณต์ฌ๋ณธ๋ ๊ณ ๋ ค๋ฉ๋๋ค.
ํด์ ํ ์ด๋ธ ์์ฒด๋ฅผ ์๋ฃํ๋ ๋ฐ 271ms๊ฐ ๊ฑธ๋ ธ์ต๋๋ค. ์ฌ๊ธฐ์๋ ๋น๋์ค ์นด๋์์ ์์๋ฅผ ์ฝ์ ํ๊ณ ์ญ์ ํ๋ ๋ฐ ์์๋ ์๊ฐ์ด ํฌํจ๋๋ฉฐ, ๋ฉ๋ชจ๋ฆฌ์ ๋ณต์ฌํ๊ณ ๊ฒฐ๊ณผ ํ ์ด๋ธ์ ๋ฐ๋ณตํ๋ ๋ฐ ์์๋ ์๊ฐ์ ๊ณ ๋ ค๋์ง ์์ต๋๋ค. GPU ํ ์ด๋ธ์ด ์ค๋ซ๋์ ์ง์๋๊ฑฐ๋ ํด์ ํ ์ด๋ธ์ด ๋น๋์ค ์นด๋์ ๋ฉ๋ชจ๋ฆฌ์ ์์ ํ ํฌํจ๋์ด ์๋ ๊ฒฝ์ฐ(์: ์ค์ ํ๋ก์ธ์๊ฐ ์๋ ๋ค๋ฅธ GPU ์ฝ๋์์ ์ฌ์ฉํ ํด์ ํ ์ด๋ธ์ ์์ฑํ๋ ค๋ ๊ฒฝ์ฐ) ํ ์คํธ ๊ฒฐ๊ณผ๊ฐ ๊ด๋ จ์ด ์์ต๋๋ค.
๋น๋์ค ์นด๋์ ํด์ ํ ์ด๋ธ์ ๋์ ์ฒ๋ฆฌ๋๊ณผ ํ์ฑ ๋ณ๋ ฌํ๋ก ์ธํด ๋์ ์ฑ๋ฅ์ ๋ณด์ฌ์ค๋๋ค.
์ ํ
ํด์ ํ ์ด๋ธ ์ํคํ ์ฒ์๋ ์์์ผ ํ ๋ช ๊ฐ์ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
- ์ ํ ํ๋ก๋น์ ํด๋ฌ์คํฐ๋ง์ผ๋ก ์ธํด ๋ฐฉํด๋ฅผ ๋ฐ์ผ๋ฉฐ, ์ด๋ก ์ธํด ํ ์ด๋ธ์ ํค๊ฐ ์๋ฒฝํ์ง ์๊ฒ ๋ฐฐ์น๋ฉ๋๋ค.
- ๊ธฐ๋ฅ์ ์ฌ์ฉํด๋ ํค๊ฐ ์ ๊ฑฐ๋์ง ์์ต๋๋ค.
delete
์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ํ ์ด๋ธ์ด ์ด์์ ํด์ง๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ํด์ ํ ์ด๋ธ์ ์ฑ๋ฅ์ ์ ์ฐจ ์ ํ๋ ์ ์์ต๋๋ค. ํนํ ์ค๋ซ๋์ ์กด์ฌํ๊ณ ์ฝ์ ๋ฐ ์ญ์ ๊ฐ ๋ง์ ๊ฒฝ์ฐ์๋ ๋์ฑ ๊ทธ๋ ์ต๋๋ค. ์ด๋ฌํ ๋จ์ ์ ์ํํ๋ ํ ๊ฐ์ง ๋ฐฉ๋ฒ์ ํ์ฉ๋ฅ ์ด ์๋นํ ๋ฎ์ ์ ํ ์ด๋ธ๋ก ๋ค์ ํด์ํ๊ณ ๋ค์ ํด์ ์ค์ ์ ๊ฑฐ๋ ํค๋ฅผ ํํฐ๋งํ๋ ๊ฒ์ ๋๋ค.
์ค๋ช ๋ ๋ฌธ์ ๋ฅผ ์ค๋ช ํ๊ธฐ ์ํด ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ 128์ต 4์ฒ 124๋ฐฑ๋ง ๊ฐ์ ์์๊ฐ ์๋ ํ ์ด๋ธ์ ๋ง๋ค๊ณ 0,96์ต 4์ฒ XNUMX๋ฐฑ๋ง ๊ฐ์ ์ฌ๋กฏ์ ์ฑ์ธ ๋๊น์ง XNUMX๋ฐฑ๋ง ๊ฐ์ ์์๋ฅผ ๋ฐ๋ณตํฉ๋๋ค(์ฌ์ฉ๋ฅ ์ฝ XNUMX). ๋ค์์ ๊ฒฐ๊ณผ ํ ์ด๋ธ์ ๋๋ค. ๊ฐ ํ์ ํ๋์ ํด์ ํ ์ด๋ธ์ XNUMX๋ง ๊ฐ์ ์ ์์๋ฅผ ์ฝ์ ํ๋ CUDA ์ปค๋ ํธ์ถ์ ๋๋ค.
์ด์ฉ๋ฅ
์ฝ์
๊ธฐ๊ฐ 4๊ฐ ์์
0,00
11,608448ms(361,314798์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,03
11,751424ms(356,918799์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,06
11,942592ms(351,205515์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,09
12,081120ms(347,178429์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,12
12,242560ms(342,600233์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,16
12,396448ms(338,347235์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,19
12,533024ms(334,660176์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,22
12,703328ms(330,173626์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,25
12,884512ms(325,530693์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,28
13,033472ms(321,810182์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,31
13,239296ms(316,807174์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,34
13,392448ms(313,184256์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,37
13,624000ms(307,861434์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,41
13,875520ms(302,280855์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,44
14,126528ms(296,909756์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,47
14,399328ms(291,284699์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,50
14,690304ms(285,515123์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,53
15,039136ms(278,892623์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,56
15,478656ms(270,973402์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,59
15,985664ms(262,379092์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,62
16,668673ms(251,627968์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,66
17,587200ms(238,486174์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,69
18,690048ms(224,413765์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,72
20,278816ms(206,831789์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,75
22,545408ms(186,038058์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,78
26,053312ms(160,989275์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,81
31,895008ms(131,503463์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,84
42,103294ms(99,619378์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,87
61,849056ms(67,815164์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,90
105,695999ms(39,682713์ตXNUMX๋งXNUMX๋ง ํค/์ด)
0,94
240,204636ms(17,461378์ตXNUMX๋งXNUMX๋ง ํค/์ด)
ํ์ฉ๋๊ฐ ์ฆ๊ฐํ๋ฉด ์ฑ๋ฅ์ด ์ ํ๋ฉ๋๋ค. ์ด๋ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ฐ๋์งํ์ง ์์ต๋๋ค. ์์ฉ ํ๋ก๊ทธ๋จ์ด ํ ์ด๋ธ์ ์์๋ฅผ ์ฝ์ ํ ๋ค์ ํด๋น ์์๋ฅผ ์ญ์ ํ๋ ๊ฒฝ์ฐ(์: ์ฑ ์ ๋จ์ด ์๋ฅผ ์ ๋) ์ด๋ ๋ฌธ์ ๊ฐ ๋์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์์ฉ ํ๋ก๊ทธ๋จ์ด ์๋ช ์ด ๊ธด ํด์ ํ ์ด๋ธ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ(์๋ฅผ ๋ค์ด ์ฌ์ฉ์๊ฐ ์ ๋ณด๋ฅผ ์์ฃผ ์ฝ์ ํ๊ณ ์ญ์ ํ๋ ์ด๋ฏธ์ง์ ๋น์ด ์์ง ์์ ๋ถ๋ถ์ ์ ์ฅํ๊ธฐ ์ํด ๊ทธ๋ํฝ ํธ์ง๊ธฐ์์) ์ด ๋์์ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ 64๋ง ๊ฐ์ ์ฝ์ ํ ํด์ ํ ์ด๋ธ ํ์ ๊น์ด๋ฅผ ์ธก์ ํ์ต๋๋ค(ํ์ฉ๋ฅ 0,5). ํ๊ท ๊น์ด๋ 0,4774์ด๋ฏ๋ก ๋๋ถ๋ถ์ ํค๋ ๊ฐ๋ฅํ ๊ฐ์ฅ ์ข์ ์ฌ๋กฏ์ ์๊ฑฐ๋ ๊ฐ์ฅ ์ข์ ์์น์์ ํ ์ฌ๋กฏ ๋จ์ด์ ธ ์์ต๋๋ค. ์ต๋ ์๋ฆฌ ๊น์ด๋ 60์ด์์ต๋๋ค.
๊ทธ๋ฐ ๋ค์ 124์ต 0,97๋ง ๊ฐ์ ์ธ์ํธ(์ด์ฉ๋ฅ 10,1757)๊ฐ ์๋ ํ ์ด๋ธ์์ ํ๋ก๋น ๊น์ด๋ฅผ ์ธก์ ํ์ต๋๋ค. ํ๊ท ๊น์ด๋ ์ด๋ฏธ XNUMX์ด์๊ณ ์ต๋๊ฐ์ - 6474 (!!). ์ ํ ๊ฐ์ง ์ฑ๋ฅ์ ํ์ฉ๋๊ฐ ๋์์๋ก ํฌ๊ฒ ๋จ์ด์ง๋๋ค.
์ด ํด์ ํ ์ด๋ธ์ ํ์ฉ๋ฅ ์ ๋ฎ๊ฒ ์ ์งํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ํฌ์ํ๋ฉด์ ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค. ๋คํํ 32๋นํธ ํค์ ๊ฐ์ ๊ฒฝ์ฐ์๋ ์ด๊ฒ์ด ์ ๋นํ๋ ์ ์์ต๋๋ค. ์์ ์์์ 128์ต 0,25๋ง ๊ฐ์ ์์๊ฐ ์๋ ํ ์ด๋ธ์์ ํ์ฉ๋ฅ ์ 32๋ก ์ ์งํ๋ฉด 96๋ง ๊ฐ ์ดํ์ ์์๋ฅผ ๋ฐฐ์นํ ์ ์์ผ๋ฉฐ ๋๋จธ์ง 8๋ง ๊ฐ์ ์ฌ๋กฏ์ ์์ค๋ฉ๋๋ค(๊ฐ ์๋น 768๋ฐ์ดํธ). , XNUMXMB์ ๋ฉ๋ชจ๋ฆฌ ์์ค.
์ฐ๋ฆฌ๋ ์์คํ ๋ฉ๋ชจ๋ฆฌ๋ณด๋ค ๋ ๊ท์คํ ๋ฆฌ์์ค์ธ ๋น๋์ค ์นด๋ ๋ฉ๋ชจ๋ฆฌ ์์ค์ ๋ํด ์ด์ผ๊ธฐํ๊ณ ์์ต๋๋ค. CUDA๋ฅผ ์ง์ํ๋ ๋๋ถ๋ถ์ ์ต์ ๋ฐ์คํฌํฑ ๊ทธ๋ํฝ ์นด๋์๋ ์ต์ 4GB์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์์ง๋ง(์์ฑ ๋น์ NVIDIA 2080 Ti์ ๋ฉ๋ชจ๋ฆฌ๋ 11GB), ๊ทธ๋ฌํ ์์ ์๋ ๊ฒ์ ์ฌ์ ํ โโ๊ฐ์ฅ ํ๋ช ํ ๊ฒฐ์ ์ด ์๋๋๋ค.
๋์ค์ ํ๋ก๋น ๊น์ด์ ๋ฌธ์ ๊ฐ ์๋ ๋น๋์ค ์นด๋์ฉ ํด์ ํ ์ด๋ธ์ ๋ง๋๋ ๋ฐฉ๋ฒ๊ณผ ์ญ์ ๋ ์ฌ๋กฏ์ ์ฌ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์ธํ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
์๋ฆฌ ๊น์ด ์ธก์
ํค์ ๊ฒ์ ๊น์ด๋ฅผ ๊ฒฐ์ ํ๊ธฐ ์ํด ์ค์ ํ ์ด๋ธ ์ธ๋ฑ์ค์์ ํค์ ํด์(์ด์์ ์ธ ํ ์ด๋ธ ์ธ๋ฑ์ค)๋ฅผ ์ถ์ถํ ์ ์์ต๋๋ค.
// get_key_index() -> index of key in hash table
uint32_t probelength = (get_key_index(key) - hash(key)) & (hashtablecapacity-1);
1์ 3์ ๋ณด์ ์ด์ง์์ ๋ง๋ฒ๊ณผ ํด์ ํ
์ด๋ธ์ ์ฉ๋์ด 4์ XNUMX์ ๊ณฑ์ด๋ผ๋ ์ฌ์ค ๋๋ฌธ์ ์ด ์ ๊ทผ ๋ฐฉ์์ ํค ์ธ๋ฑ์ค๊ฐ ํ
์ด๋ธ์ ์์ ๋ถ๋ถ์ผ๋ก ์ด๋ํ๋ ๊ฒฝ์ฐ์๋ ์๋ํฉ๋๋ค. XNUMX๋ก ํด์๋์์ง๋ง ์ฌ๋กฏ XNUMX์ ์ฝ์
๋ ํค๋ฅผ ๊ฐ์ ธ์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ฉ๋์ด XNUMX์ธ ํ
์ด๋ธ์ ๋ํด ๋ค์์ ์ป์ต๋๋ค. (3 โ 1) & 3
, ์ด๋ 2์ ๋์ผํฉ๋๋ค.
๊ฒฐ๋ก
์ง๋ฌธ์ด๋ ์๊ฒฌ์ด ์์ผ์๋ฉด ์ ์๊ฒ ์ด๋ฉ์ผ์ ๋ณด๋ด์ฃผ์ธ์.
์ด ์ฝ๋๋ ํ๋ฅญํ ๊ธฐ์ฌ์์ ์๊ฐ์ ๋ฐ์ ์์ฑ๋์์ต๋๋ค.
์ธ๊ณ์์ ๊ฐ์ฅ ๊ฐ๋จํ ์ ๊ธ ์๋ ํด์ ํ ์ด๋ธ ์ ๊ธ์ด ์๊ณ ๋๊ธฐ๊ฐ ์๋ ํด์ ํ ์ด๋ธ
์์ผ๋ก๋ ๋น๋์ค ์นด๋์ ํด์ ํ
์ด๋ธ ๊ตฌํ์ ๋ํ ๊ธ์ ์ฐ๊ณ ์ฑ๋ฅ์ ๋ถ์ํ ์์ ์
๋๋ค. ๋ด ๊ณํ์๋ GPU ์นํ์ ์ธ ๋ฐ์ดํฐ ๊ตฌ์กฐ์์ ์์ ์ฐ์ฐ์ ์ฌ์ฉํ ์ฒด์ด๋, ๋ก๋นํ๋ ํด์ฑ, ๋ป๊พธ๊ธฐ ํด์ฑ์ด ํฌํจ๋ฉ๋๋ค.
์ถ์ฒ : habr.com