SwissTable

SwissTable์€ ํ•ด์‹œํ…Œ์ด๋ธ”์˜ ์ตœ์ ํ™”๋œ ๊ตฌํ˜„ ํ˜•ํƒœ ์ค‘ ํ•˜๋‚˜๋‹ค.

2017๋…„์— ๊ตฌ๊ธ€์—์„œ ์ฒ˜์Œ ๊ณต๊ฐœํ–ˆ์œผ๋ฉฐ, ์ฒ˜์Œ ๊ณต๊ฐœ๋œ ๊ตฌํ˜„ ๋ฒ„์ „์€ C++์ด์—ˆ๋‹ค.

์ด์ „์— ์กด์žฌํ–ˆ๋˜ ํ•ด์‹œํ…Œ์ด๋ธ”์— ๋น„ํ•ด์„œ ๋Œ€์ฒด๋กœ ์šฐ์›”ํ–ˆ๊ณ , ๋”ฑํžˆ ๋‹จ์ ๋„ ์—†์–ด์„œ ์ผ๋ถ€ ์–ธ์–ด๋“ค์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ด๊ฑธ๋กœ ๋Œ€์ฒด๋˜์—ˆ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ Rust์˜ std Hashmap(since 1.35), Go์˜ ๊ธฐ๋ณธ map(since 1.24)์ด swisstable ๊ตฌํ˜„์ฒด๋‹ค.
๊ฑฐ์˜ ์ตœ์ดˆ๋กœ ์ „๋ฉด ๋„์ž…ํ•œ๊ฒŒ Rust ์ชฝ ํ™˜๊ฒฝ์ธ ๊ฒƒ ๊ฐ™๋”๋ผ.




๊ณ ์ „์ ์ธ ํ•ด์‹œํ…Œ์ด๋ธ”

๋Œ€๋ถ€๋ถ„์˜ ์–ธ์–ด์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํ•ด์‹œํ…Œ์ด๋ธ” ๊ตฌํ˜„์ฒด๊ฐ€ ๋‹ค์Œ ๋ฐฉ์‹์„ ๋”ฐ๋ฅธ๋‹ค.

ํ•ด์‹œํ…Œ์ด๋ธ”์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ทธ๋ƒฅ ๋ฐฐ์—ด์ด๋‹ค.
Tom์ด๋‹ˆ, John์ด๋‹ˆ ํ•˜๋Š” ์ž„์˜์˜ ํ‚ค๊ฐ’์ด ๋“ค์–ด์˜ค๋ฉด ๊ทธ๊ฑธ ํ•ด์‹œํ•จ์ˆ˜ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ ๋ฐฐ์—ด ์ธ๋ฑ์Šค์— ๋งคํ•‘์‹œ์ผœ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋ณดํ†ต ์ € ๋ฐฐ์—ด์„ backing array์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๊ทผ๋ฐ ๋ฌธ์ œ๋Š” ํ•ด์‹œ๊ฐ’์ด ํ•ญ์ƒ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋‹ค๋ฅธ ๊ฐ’์ด ๋™์ผํ•˜๊ฒŒ 3 ์ธ๋ฑ์Šค๋กœ ๋งคํ•‘๋˜๋ฉด ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ ๊ฐ™์€๊ฐ€?

๋ฐฉ๋ฒ• 1: Chaining

์ƒ๋‹น์ˆ˜์˜ ํ•ด์‹œํ…Œ์ด๋ธ” ๊ตฌํ˜„์—์„œ๋Š” ์•„์˜ˆ ๊ทธ๋ƒฅ ๋ฐฐ์—ด ์š”์†Œ ๋‚ด๋ถ€์— Linked List๋ฅผ ๋„ฃ๊ณ , ํ•œ ํ‚ค๋งˆ๋‹ค ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด๋‘”๋‹ค.

๊ทธ๋ž˜์„œ ํ•ด์‹œ ์ถฉ๋Œ์ด ๊ทน์‹ฌํ•˜๊ฒŒ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ํ•œ ์š”์†Œ์˜ ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๊ฐ€ ๋น„๋Œ€ํ•ด์ ธ์„œ ์‚ฝ์ž…/์‚ญ์ œ/์กฐํšŒ ์‹œ๊ฐ„ ๋“ฑ์ด O(N)์— ๊ฐ€๊น๊ฒŒ ๋‚˜์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ๋ฌผ๋ก  ํ•ด์‹œํ•จ์ˆ˜ ์„ฑ๋Šฅ์— ๋”ฐ๋ผ์„œ ๊ฐˆ๋ฆฌ๋Š” ๋ถ€๋ถ„์ด๊ธด ํ•˜๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ฐ’์ด ๋‹จ์ˆœํžˆ ์‚ฝ์ž…๋˜๊ฑฐ๋‚˜ ์ถฉ๋Œ๋ ๋•Œ๋งˆ๋‹ค ๋™์ ํ• ๋‹น์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๋‹จ์ ๋„ ์žˆ๊ณ , Linked List ํŠน์œ ์˜ ์บ์‹œ ๋น„์นœํ™”์„ฑ๋„ ๊ณ ์งˆ์ ์ธ ํ•œ๊ณ„ ์ค‘ ํ•˜๋‚˜๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.


๋ฐฉ๋ฒ• 2. probing
์ด๊ฑฐ ๋ง๊ณ  probing(์กฐ์‚ฌ)๋ผ๊ณ  ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์—ˆ๋‹ค. open addressing์ด๋ผ๊ณ ๋„ ๋ถ€๋ฅธ๋‹ค.
์‚ฌ์šฉ ๋นˆ๋„๋Š” chaining๋ณด๋‹ค ๋‚ฎ๊ธด ํ–ˆ๋Š”๋ฐ, ๊ทธ๋ž˜๋„ ๊ฝค ์“ฐ๊ธด ์ผ๋‹ค.

์‚ฌ์‹ค ์›๋ฆฌ๋Š” ๋ณ„๊ฑฐ ์—†๋‹ค.
๊ทธ๋ƒฅ ๋นˆ ๊ณต๊ฐ„ ์žˆ์œผ๋ฉด ์•„๋ฌด๋ฐ๋‚˜ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ์•„๋ฌด๋ฐ๋‚˜๋ฅผ ์–ด๋–ป๊ฒŒ ์ •ํ• ์ง€๋„ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅด๋‹ค.
๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์€ ๊ทธ๋ƒฅ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•œ ๋‹ค์Œ ์ธ๋ฑ์Šค์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋Ÿฐ ์‹์œผ๋กœ ๋ง์ด๋‹ค.

์ด๋Ÿฌ๋ฉด ์ถ”๊ฐ€ ํ• ๋‹น์ด Chaining์— ๋น„ํ•ด์„œ๋Š” ์ ์„ ์ˆ˜ ์žˆ์–ด์„œ ๋‚˜๋ฆ„ ๋‚˜์˜์ง€ ์•Š์€๋ฐ, ์ถฉ๋Œ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋นˆ Key๊ฐ€ ์ ์–ด์ง„๋‹ค๋ฉด ์„ฑ๋Šฅ์ด ๊ธ‰๊ฒฉํ•˜๊ฒŒ ๋–จ์–ด์ง„๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

https://hyoeun-log.tistory.com/entry/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-11%EC%9E%A5-%ED%95%B4%EC%8B%9C-%ED%85%8C%EC%9D%B4%EB%B8%94
์‚ฌ์‹ค ์ด๋Ÿฐ ๋ถˆ์•ˆ์ •์„ฑ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์œผ๋กœ ์ž˜ ์“ฐ์ด์ง€๋Š” ์•Š๋Š”๋ฐ, Python์˜ dict ๊ตฌํ˜„ ์ •๋„๊ฐ€ ์ข€ ํŠน์ดํ•˜๊ฒŒ probing ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋˜์—ˆ๋‹ค.
๋ฌผ๋ก  ์ด๊ฑธ ๊ทธ๋ƒฅ ์“ฐ๋ฉด ๊ฝ‰์ฐผ์„๋•Œ ์„ฑ๋Šฅ์ด ์•„์ž‘๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์šฉ๋Ÿ‰, ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ load factor์„ ๋” ๋ฏผ๊ฐํ•˜๊ฒŒ ์žก์•„๋†จ๋‹ค.




Swisstable

swisstable์€ ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ์ด์ „์— ๋ฐ˜์ฏค ๋ฒ„๋ ค์กŒ๋˜ open addressing Hashtable์˜ ๋ณ€์ข…์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.
๊ทธ ๊ธฐ๋ณธ ์›๋ฆฌ์™€ ๊ตฌ์กฐ๋ฅผ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•ด๋ณด๊ฒ ๋‹ค.



Hash ๊ฐ’ ๊ตฌ์กฐ

swisstable์˜ ํ•ด์‹œ๊ฐ’์€ ๋ฐ˜๋“œ์‹œ 8๋ฐ”์ดํŠธ ์ •์ˆ˜์—ฌ์•ผ ํ•œ๋‹ค.
์™œ๋‚˜๋ฉด ๋น„ํŠธ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์„œ ๋ณ„๊ฐœ์˜ 2๊ฐ€์ง€ ๊ฐ’์„ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

https://abseil.io/about/design/swisstables
๊ทธ๋ž˜์„œ ์ƒ์œ„ 57๋น„ํŠธ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์›๋ž˜ ์•Œ๋˜ ํ…Œ์ด๋ธ” ๋ถ„๋ฐฐ์šฉ ํ‚ค๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ์ €๊ฒŒ ๊ฐ™์œผ๋ฉด ๊ฐ™์€ ํ‚ค๊ณ , ํ‚ค๊ฐ€ ๊ฐ™์œผ๋ฉด ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ•˜์œ„ 7๋น„ํŠธ๋Š” ์ถฉ๋Œ์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฒฝ์šฐ, probing์„ ์œ„ํ•œ ํŒ๋‹จ ๊ธฐ์ค€์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.



Probing

๋ฐฉ๊ธˆ ์–ธ๊ธ‰ํ•œ 7๋น„ํŠธ H2 ๊ฐ’์„ ์ถฉ๋Œ ๋ฐœ์ƒ ์‹œ probing์„ ์œ„ํ•œ ๋ถ€๊ฐ€ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
H1 ํ‚ค๊ฐ’์ด ๊ฐ™๋”๋ผ๋„ ์‹ค์ œ ๊ฐ’์ด ๋‹ค๋ฅด๋ฉด H2 ๊ฐ’์ด ๋‹ค๋ฅผ ํ™•๋ฅ ์ด ๋งค์šฐ ๋†’์œผ๋‹ˆ, ๊ทธ๊ฑธ ๊ธฐ์ค€์œผ๋กœ ์ธ๋ฑ์Šค๋ฅผ ๋‹ค์‹œ ๋ถ„๋ฐฐํ•ด์„œ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋ฌผ๋ก  ์ด๊ฒƒ๋„ ๊ฒฐ๊ตญ ํ•ด์‹œ๋‹ˆ๊นŒ, ์‹ค์ œ ์›๋ณธ ๊ฐ’์ด ๋‹ค๋ฅด๋”๋ผ๋„ H2 ๊ฐ’์—์„œ๋„ ํ•ด์‹œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ์ˆ˜๋Š” ์žˆ๋‹ค.
๊ทธ๋ž˜์„œ ๊ตฌํ˜„์‹œ์— ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋Š” ํ•ด์•ผ๊ฒ ์ง€๋งŒ ํ™•๋ฅ ์ด 1/128 (2^7)๋กœ ๋‚ฎ์•„์„œ ๊ฑฑ์ •ํ•  ์ผ์€ ๋ณ„๋กœ ์—†๊ธด ํ•˜๋‹ค.



SIMD

H2๋กœ ๋ถ„์‚ฐ์„ ํ•˜๋“  ์–ด์จŒ๋“ , Probing์€ ์–ด์จŒ๊ฑฐ๋‚˜ H2 ๊ฐ’ ๊ธฐ๋ฐ˜์œผ๋กœ ๋นˆ์ž๋ฆฌ๊ฐ€ ๋‚˜์˜ฌ๋•Œ๊นŒ์ง€ ์ฐพ์•„์•ผ ํ•œ๋‹ค.
๊ทธ๋Ÿฌ๋ฉด ๊ฐ™์€ ๊ทธ๋ฃน(H1 ์ถฉ๋Œ ๋Œ€์ƒ)์—์„œ H2 ๊ฐ’ ๊ธฐ๋ฐ˜์œผ๋กœ ์—ฌ๋Ÿฌ๋ฒˆ ๋’ค์ ธ์•ผํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ๊ทธ๋ ‡๊ฒŒ ํ•œ๋‹ค๋ฉด ์•„๋ฌด๋ž˜๋„ ์„ฑ๋Šฅ์ƒ ๋น„ํšจ์œจ์ ์ธ ๋ถ€๋ถ„์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ swisstable์„ SIMD๋ฅผ ์‘์šฉํ•ด์„œ ๋ณ‘๋ ฌ ์ตœ์ ํ™”๋ฅผ ๋‹ฌ์„ฑํ•œ๋‹ค.
์–ด์ฐจํ”ผ H2๋Š” 1๋ฐ”์ดํŠธ์งœ๋ฆฌ ์ž‘์€ ํ•ด์‹œ๊ฐ’์ด๋‹ˆ๊นŒ, SIMD์— 8๊ฐœ-16๊ฐœ์”ฉ ์šฐ๊ฒจ๋„ฃ๊ณ  ํ•œ๋ฒˆ์— ๋น„๊ตํ•ด์„œ ๋น ๋ฅด๊ฒŒ ์œ„์น˜๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.



์‹ค์„ฑ๋Šฅ

swisstable์ด ๊ธฐ์กด์˜ hashtable๊ณผ ๋น„๊ตํ•ด์„œ ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์ด๊ธฐ๋„ ํ•˜๋‹ค๋Š” ๊ฒƒ์€ ๋ณ„๋กœ ์ด๋ก ์˜ ์—ฌ์ง€๊ฐ€ ์—†๋‹ค.
๊ฒ€์ฆ๋„ ๊ฑฐ์˜ ๋๋‚ฌ๊ณ  ์‚ฌ์šฉ์— ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.

์ถ”๊ฐ€ ํ• ๋‹น์„ ๋‚จ๋ฐœํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์—์„œ๋Š” ๋ฐฐ์—ด๊ณผ ๊ฑฐ์˜ ๋™๋“ฑํ•œ ์ˆ˜์ค€์ด๋‹ค.

CPU ๋ฆฌ์†Œ์Šค ๊ธฐ์ค€์œผ๋กœ๋Š” ์‹ค์ œ ์†๋„๋„ 2๋ฐฐ ์ •๋„ ๋น ๋ฅด๋‹ค๊ณ  ํ•˜๋‚˜, ์ด๊ฑด ์กฐ๊ธˆ ๊ตฌํ˜„ ๋ฐฉ์‹๊ณผ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.
์–ธ์–ด ํ™˜๊ฒฝ์— ๋”ฐ๋ผ์„œ (ํŠนํžˆ Java์˜ ๊ฒฝ์šฐ) CPU Time ์ž์ฒด๊ฐ€ ๊ทน์ ์œผ๋กœ ์ค„์–ด๋“ค์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ์˜๊ฒฌ๋„ ์žˆ๋”๋ผ



์ฐธ์กฐ
https://go.dev/blog/swisstable
https://github.com/rust-lang/hashbrown
https://abseil.io/about/design/swisstables
https://stackoverflow.com/questions/9010222/why-can-a-python-dict-have-multiple-keys-with-the-same-hash
https://dev.to/huizhou92/swisstable-a-high-performance-hash-table-implementation-1knc
https://leventov.medium.com/smoothiemap-2-the-lowest-memory-hash-table-ever-6bebd06780a3