ํฌ์ŠคํŠธ

CS โ€” pushback vs emplaceback

CS โ€” pushback vs emplaceback

๐Ÿ“• 04/30 โ€” vector push_back vs emplace_back + ํ•ด์‹œ ์ถฉ๋Œ ๋ณด๊ฐ•

๋ชจ์˜๋ฉด์ ‘ ๋‹ค์Œ ์ฃผ์ œ: โ€œvector ์˜ push_back ๊ณผ emplace_back ์˜ ์ฐจ์ด์ ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด ์ฃผ์„ธ์š”โ€ 14๋ฒˆ ๋ชจ์˜๋ฉด์ ‘ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ ๋ณด๊ฐ•: ํ•ด์‹œ ์ถฉ๋Œ(Hash Collision) ์ฒ˜๋ฆฌ โ€” ์ฒด์ด๋‹ vs ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ + vector capacity / rehash ์ •๋ฆฌ


ํ•™์Šต ์˜์—ญ ์ „ํ™˜์  โ€” ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉ๋ฒ• + 14๋ฒˆ ๋ณด๊ฐ• ํ•œ ๋ฌถ์Œ

1
2
3
4
5
6
13๋ฒˆ  vector vs list                    โ€” ์‹œํ€€์Šค ์ปจํ…Œ์ด๋„ˆ ๋ฉ”๋ชจ๋ฆฌยท์บ์‹œ
14๋ฒˆ  std::map                          โ€” ์—ฐ๊ด€ ์ปจํ…Œ์ด๋„ˆ (RB-Tree)
14๋ฒˆ ํ›„์†  std::map followup             โ€” ๋ชจ์˜๋ฉด์ ‘ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ 16๊ฐœ
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
15๋ฒˆ  push_back vs emplace_back โ˜…       โ€” vector ๊ด€์šฉ๊ตฌ + ํ•ด์‹œ ์ถฉ๋Œ + capacity
์ดํ›„  std::unordered_map deepdive       โ€” ํ•ด์‹œ ์ž๋ฃŒ๊ตฌ์กฐ ๋‹จ๋… ์ •๋ฆฌ

๋ณธ ๋ฌธ์„œ๋Š” ๋‘ ๊ฐ€์ง€ ๊ฐˆ๋ž˜๋ฅผ ํ•œ ํŒŒ์ผ์— ๋ฌถ๋Š”๋‹ค:

  1. ๋‹ค์Œ ์ฃผ์ œ ๋‹ต๋ณ€ โ€” push_back vs emplace_back (1ยท2๋ถ€)
  2. 14๋ฒˆ ๋ณด๊ฐ• โ€” ํ•ด์‹œ ์ถฉ๋Œ ์ฒ˜๋ฆฌ(์ฒด์ด๋‹/์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ) + vector capacity/rehash (3ยท4๋ถ€)

๋ชจ์˜๋ฉด์ ‘ ๋‹ต๋ณ€

vector::push_back ๊ณผ emplace_back ์€ ๋‘˜ ๋‹ค ๋์— ์›์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฉค๋ฒ„ ํ•จ์ˆ˜์ด์ง€๋งŒ, ๊ฐ์ฒด ์ƒ์„ฑ ์œ„์น˜์™€ ์ธ์ž ์ „๋‹ฌ ๋ฐฉ์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

push_back(value) ์€ ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ vector ์˜ ๋งˆ์ง€๋ง‰ ์Šฌ๋กฏ์— ๋ณต์‚ฌ ๋˜๋Š” ์ด๋™ํ•ด ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ push_back(T(args...)) ์ฒ˜๋Ÿผ ์ž„์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ๊ฒƒ์„ ๋‹ค์‹œ ์˜ฎ๊ธฐ๋Š” 2๋‹จ๊ณ„ ๋น„์šฉ์ด ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. C++11 ๋ถ€ํ„ฐ rvalue ์˜ค๋ฒ„๋กœ๋“œ๊ฐ€ ์ถ”๊ฐ€๋ผ ์ž„์‹œ ๊ฐ์ฒด๋Š” ์ด๋™๋˜์ง€๋งŒ, ์—ฌ์ „ํžˆ ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ ์ž์ฒด์˜ ๋น„์šฉ์€ ๋‚จ์Šต๋‹ˆ๋‹ค.

emplace_back(args...) ์€ vector ์˜ ๋งˆ์ง€๋ง‰ ์Šฌ๋กฏ ๋ฉ”๋ชจ๋ฆฌ ์œ„์—์„œ ์ง์ ‘ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ + perfect forwarding ์œผ๋กœ ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ์ž์— ์ „๋‹ฌํ•˜๋ฏ€๋กœ ์ž„์‹œ ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž ์ธ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด๋‚˜ ๋ณต์‚ฌ๊ฐ€ ๊ธˆ์ง€๋œ ํƒ€์ž…(unique_ptr ๋“ฑ)์ฒ˜๋Ÿผ in-place ์ƒ์„ฑ์ด ๋ณธ์งˆ์ ์œผ๋กœ ํ•„์š”ํ•œ ์ƒํ™ฉ์—์„œ ์˜๋ฏธ์žˆ๋Š” ์ฐจ์ด๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.

1
2
3
4
5
6
7
std::vector<std::string> v;
v.push_back("Alice");                  // const char* โ†’ string ์ž„์‹œ ์ƒ์„ฑ โ†’ ์ด๋™
v.emplace_back("Alice");               // ์Šฌ๋กฏ์—์„œ string("Alice") ์ง์ ‘ ์ƒ์„ฑ

std::vector<std::pair<int, std::string>> p;
p.push_back({1, "A"});                 // pair ์ž„์‹œ ์ƒ์„ฑ โ†’ ์ด๋™
p.emplace_back(1, "A");                // pair(1, "A") in-place ์ƒ์„ฑ

๋‹ค๋งŒ emplace_back ์ด ํ•ญ์ƒ ๋” ๋น ๋ฅด๊ฑฐ๋‚˜ ๋” ์•ˆ์ „ํ•œ ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. explicit ์ƒ์„ฑ์ž๋ฅผ ์˜๋„์น˜ ์•Š๊ฒŒ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๊ณ , ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ์ง€ ์•Š์•„ ์ฝ”๋“œ ์˜๋„๋ฅผ ํ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜๋„๊ฐ€ โ€œ์ด ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๋ผโ€ ๋ฉด push_back, โ€œ์—ฌ๊ธฐ์„œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋„ฃ์–ด๋ผโ€ ๋ฉด emplace_back ์ด ์˜๋ฏธ์ƒ ๋” ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค.

์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ๊ฒฐ์ •์ ์ธ ๊ฒฝ์šฐ๋Š” ์•ž์„œ ๋งํ•œ ์ƒ์„ฑ์ž ์ธ์ž๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด ๋˜๋Š” ๋ณต์‚ฌ๊ฐ€ ๊ธˆ์ง€๋œ ํƒ€์ž…์˜ in-place ์ƒ์„ฑ์ž…๋‹ˆ๋‹ค. POD ํƒ€์ž…์ด๋‚˜ ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋ฅผ ์˜ฎ๊ธธ ๋•Œ๋Š” ๋‘ ํ•จ์ˆ˜์˜ ๋น„์šฉ์ด ์‚ฌ์‹ค์ƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(๋Œ€๋ถ€๋ถ„ ์ปดํŒŒ์ผ๋Ÿฌ ์ตœ์ ํ™”๋กœ ๋™์ผ ์ฝ”๋“œ ์ƒ์„ฑ).


์‹ฌํ™” ์งˆ๋ฌธ โ€” reserve ยท capacity ยท ๊ฐ์ฒด ์ƒ์„ฑ ์ƒ์„ธ

push_back ์€ ์ธ์ž๋ฅผ 1๊ฐœ๋งŒ ๋ฐ›๋Š”๋‹ค๋Š” ๊ฒŒ ๋ฌด์Šจ ๋œป์ธ๊ฐ€?

push_back ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” push_back(const T&) ๋˜๋Š” push_back(T&&) ๋กœ ์ธ์ž 1๊ฐœ๋งŒ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์ƒ์„ฑ์ž ์ธ์ž๋ฅผ ๋„˜๊ธฐ๋ ค๋ฉด ๊ฐ์ฒด๋ฅผ ๋จผ์ € ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. push_back(1, 2) ๋Š” ์ปดํŒŒ์ผ ์—๋Ÿฌ์ด๊ณ , push_back(Point(1, 2)) ์ฒ˜๋Ÿผ ๊ฐ์ฒด 1๊ฐœ๋กœ ๊ฐ์‹ธ์„œ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. emplace_back(1, 2) ๋Š” ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์ธ์ž ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋ฐ›์•„ ์Šฌ๋กฏ์—์„œ ์ง์ ‘ Point(1, 2) ๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์ด ์ œ์•ฝ์ด ์—†์Šต๋‹ˆ๋‹ค.

reserve ๋Š” capacity ๋ฅผ ๋Š˜๋ฆฌ๋Š”๊ฐ€, size ๋ฅผ ๋Š˜๋ฆฌ๋Š”๊ฐ€?

reserve(n) ์€ capacity ๋งŒ ๋Š˜๋ฆฌ๊ณ  size ๋Š” ๊ทธ๋Œ€๋กœ ์ž…๋‹ˆ๋‹ค. ํž™์— n * sizeof(T) ๋ฐ”์ดํŠธ์งœ๋ฆฌ ์—ฐ์† ๋ธ”๋ก์„ ๋ฏธ๋ฆฌ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด๊ณ , ์›์†Œ๋Š” ์ดํ›„ push_back / emplace_back ์„ ํ•ด์•ผ ์ƒ๊น๋‹ˆ๋‹ค. size ๋ฅผ ์ง์ ‘ ๋Š˜๋ฆฌ๋ ค๋ฉด resize(n) ์„ ์จ์•ผ ํ•˜๋ฉฐ, ์ด ๊ฒฝ์šฐ ์›์†Œ n ๊ฐœ๊ฐ€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค.

reserve ์˜ ๋ฉ”๋ชจ๋ฆฌยท์บ์‹œ ๋™์ž‘

reserve ๋Š” ํž™์— ์—ฐ์† ๋ธ”๋ก์„ ํ•œ ๋ฒˆ์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. size ๋ฒ”์œ„ ์•ˆ๋งŒ ์ดˆ๊ธฐํ™”๋œ ์›์†Œ์ด๊ณ , ๋‚˜๋จธ์ง€๋Š” raw ๋ฉ”๋ชจ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์บ์‹œ ๊ด€์ ์—์„œ ์žฌํ• ๋‹น์ด ์ผ์–ด๋‚˜๋ฉด ์ƒˆ ์ฃผ์†Œ๋กœ ๋ธ”๋ก์ด ํ†ต์งธ๋กœ ์ด๋™ํ•˜๋ฉด์„œ ์บ์‹œ ๋ผ์ธ์ด ๋ฌดํšจํ™”๋ฉ๋‹ˆ๋‹ค. reserve ๋Š” ์ด ์žฌํ• ๋‹น ์ž์ฒด๋ฅผ ๋ง‰์•„ ๊ฐ™์€ ์ฃผ์†Œ๋ฅผ ๊ณ„์† ์‚ฌ์šฉํ•˜๊ฒŒ ํ•˜๊ณ , ์—ฐ์† ๋ฐฐ์—ด์ด๋ฏ€๋กœ CPU prefetch ๋„ ์ •ํ™•ํžˆ ์˜ˆ์ธก๋ฉ๋‹ˆ๋‹ค.

reserve ๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด UB ์ธ๊ฐ€?

์•„๋‹™๋‹ˆ๋‹ค. capacity ๋ฅผ ์ดˆ๊ณผํ•ด์„œ push_back ํ•˜๋ฉด ์ž๋™ ์žฌํ• ๋‹น์ด ์ผ์–ด๋‚˜๋ฏ€๋กœ UB ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. UB ๋Š” size ๋ฅผ ์ดˆ๊ณผํ•œ ์ธ๋ฑ์Šค๋กœ ์ง์ ‘ ์ ‘๊ทผํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. reserve(10) ํ›„ size ๊ฐ€ 0์ธ ์ƒํƒœ์—์„œ v[0] ์— ์ ‘๊ทผํ•˜๋ฉด ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์€ raw ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฑด๋“œ๋ฆฌ๋Š” ๊ฒƒ์ด๋ฏ€๋กœ UB ์ž…๋‹ˆ๋‹ค. ์ ‘๊ทผ ๊ฐ€๋Šฅ ๋ฒ”์œ„์˜ ๊ธฐ์ค€์€ capacity ๊ฐ€ ์•„๋‹ˆ๋ผ size ์ž…๋‹ˆ๋‹ค.

reserve ๋ฅผ ์“ฐ๋ฉด push_back ๊ณผ emplace_back ์˜ ์ฐจ์ด๊ฐ€ ์ƒ์‡„๋˜๋Š”๊ฐ€?

์•„๋‹™๋‹ˆ๋‹ค. ๋‘˜์€ ๋‹ค๋ฅธ ์ถ•์˜ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. reserve ๋Š” ์žฌํ• ๋‹น ํšŸ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด๊ณ , push_back vs emplace_back ์˜ ์ฐจ์ด๋Š” ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค. reserve ๋กœ ์žฌํ• ๋‹น์„ ์—†์• ๋„, push_back(Point(1,2)) ๋Š” ์ž„์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ์Šฌ๋กฏ์œผ๋กœ ์ด๋™ํ•˜๋Š” 2๋‹จ๊ณ„์ด๊ณ , emplace_back(1, 2) ๋Š” ์Šฌ๋กฏ์—์„œ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” 1๋‹จ๊ณ„์ธ ๊ฒƒ์€ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค. ์ฐจ์ด๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฑด POD / ์ž‘์€ ๊ฐ์ฒด์—์„œ ์ปดํŒŒ์ผ๋Ÿฌ ์ตœ์ ํ™”๋กœ ์ž„์‹œ ๊ฐ์ฒด๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ์ด๋ฉฐ โ€” reserve ๊ฐ€ ์•„๋‹ˆ๋ผ ์ตœ์ ํ™” ๋•๋ถ„์ž…๋‹ˆ๋‹ค.

reserve ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๊ธฐ์กด ์›์†Œ๊ฐ€ ์ด๋™ํ•˜๋Š”๊ฐ€?

reserve ํ˜ธ์ถœ ์‹œ์ ๊ณผ ์ดํ›„๋ฅผ ๋‚˜๋ˆ ์•ผ ํ•ฉ๋‹ˆ๋‹ค. reserve(n) ์„ ํ˜ธ์ถœํ•  ๋•Œ ํ˜„์žฌ capacity ๋ณด๋‹ค ํฌ๋ฉด ์ƒˆ ๋ธ”๋ก์„ ํ• ๋‹นํ•˜๊ณ  ๊ธฐ์กด ์›์†Œ๋ฅผ ์ด๋™ํ•œ ๋’ค ๊ธฐ์กด ๋ธ”๋ก์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค โ€” ์ด๋™์ด 1ํšŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ push_back ์€ capacity ๋ฅผ ์ดˆ๊ณผํ•˜๊ธฐ ์ „๊นŒ์ง€ ์Šฌ๋กฏ์— ์‚ฝ์ž…๋งŒ ํ•˜๋ฏ€๋กœ ์ด๋™์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ reserve ์˜ ํšจ๊ณผ๋Š” โ€œ์—ฌ๋Ÿฌ ๋ฒˆ ์ด๋™ํ•  ๊ฑธ ์ตœ๋Œ€ 1ํšŒ๋กœ ์ค„์ด๋Š” ๊ฒƒโ€ ์ž…๋‹ˆ๋‹ค.


ํ•ต์‹ฌ ๊ฐœ๋…

๋ถ„๋ฅ˜ํ‚ค์›Œ๋“œํ•œ ์ค„ ์ •์˜
vector ๊ด€์šฉ๊ตฌpush_back(value)์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ๊ฐ’์„ ๋ณต์‚ฌยท์ด๋™ํ•ด ์ถ”๊ฐ€
ย emplace_back(args...)์Šฌ๋กฏ ์œ„์—์„œ ์ƒ์„ฑ์ž ์ง์ ‘ ํ˜ธ์ถœ (in-place)
ย perfect forwarding๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ + std::forward ๋กœ ์ธ์ž ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ
ย rvalue ์˜ค๋ฒ„๋กœ๋“œC++11 ์˜ push_back(T&&) โ€” ์ž„์‹œ ๊ฐ์ฒด ์ด๋™
ย explicit ์ƒ์„ฑ์žemplace_back ์œผ๋กœ ์šฐํšŒ ๊ฐ€๋Šฅ โ€” ์ฃผ์˜
๋ฉ”๋ชจ๋ฆฌsize()ํ˜„์žฌ ์›์†Œ ์ˆ˜
ย capacity()์žฌํ• ๋‹น ์—†์ด ๋‹ด์„ ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ์›์†Œ ์ˆ˜
ย ์žฌํ• ๋‹น (Reallocation)size > capacity ์‹œ ์ƒˆ ๋ฉ”๋ชจ๋ฆฌ + ์ „์ฒด ๋ณต์‚ฌยท์ด๋™
ย amortized O(1)ํ‰๊ท ์€ O(1), ์ตœ์•…(์žฌํ• ๋‹น) ์€ O(n) โ€” ํ‰๊ท ํ™”ํ•˜๋ฉด O(1)
ย growth factor๋ณดํ†ต 1.5x ~ 2x (๊ตฌํ˜„๋งˆ๋‹ค ๋‹ค๋ฆ„)
ย reserve(n)์žฌํ• ๋‹น ๋ฏธ๋ฆฌ 1ํšŒ โ€” ์ดํ›„ n ๊ฐœ๊นŒ์ง€ ์žฌํ• ๋‹น 0
ย shrink_to_fit()capacity ๋ฅผ size ๋กœ ์ถ•์†Œ (๊ตฌํ˜„ ๊ถŒ๊ณ )
ํ•ด์‹œ ์ถฉ๋Œ์ถฉ๋Œ (Collision)๋‹ค๋ฅธ ํ‚ค๊ฐ€ ๊ฐ™์€ ๋ฒ„ํ‚ท ์ธ๋ฑ์Šค
ย ์ฒด์ด๋‹ (Separate Chaining)๊ฐ™์€ ๋ฒ„ํ‚ท์˜ ์›์†Œ๋ฅผ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฌถ์Œ
ย ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ (Open Addressing)์ถฉ๋Œ ์‹œ ๋‹ค์Œ ๋นˆ ์Šฌ๋กฏ์œผ๋กœ probing
ย ์„ ํ˜• ํƒ์‚ฌ (Linear Probing)์ถฉ๋Œ ์‹œ +1 ์นธ์”ฉ ์ด๋™
ย ์ด์ฐจ ํƒ์‚ฌ (Quadratic Probing)์ถฉ๋Œ ์‹œ +1ยฒ, +2ยฒ, +3ยฒ โ€ฆ ์ด๋™
ย ์ด์ค‘ ํ•ด์‹ฑ (Double Hashing)๋‘ ๋ฒˆ์งธ ํ•ด์‹œ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋งŒํผ ์ด๋™
ย ๋กœ๋นˆ ํ›„๋“œ ํ•ด์‹ฑ (Robin Hood)probing ๊ฑฐ๋ฆฌ ๊ท ๋“ฑํ™”
ย ํด๋Ÿฌ์Šคํ„ฐ๋งopen addressing ์˜ ์ถฉ๋Œ์ด ์ธ์ ‘ ์Šฌ๋กฏ์— ๋ชฐ๋ฆผ

๋ชฉ์ฐจ

  1. push_back vs emplace_back โ€” ํ•ต์‹ฌ ์ฐจ์ด
  2. ๋‚ด๋ถ€ ๋™์ž‘ โ€” ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ + perfect forwarding
  3. ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ๊ฒฐ์ •์ ์ธ ์ผ€์ด์Šค
  4. emplace_back ์˜ ํ•จ์ •
  5. vector capacity ์™€ ์žฌํ• ๋‹น (rehash ์™€ ๋น„๊ต)
  6. ํ•ด์‹œ ์ถฉ๋Œ ์ฒ˜๋ฆฌ โ€” ์ฒด์ด๋‹
  7. ํ•ด์‹œ ์ถฉ๋Œ ์ฒ˜๋ฆฌ โ€” ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ
  8. STL ยท Unreal ์˜ ์ถฉ๋Œ ํ•ด๊ฒฐ ์ •์ฑ…
  9. ๋ฉด์ ‘ ๋‹จ๊ณจ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ
  10. ํšŒ๊ท€ ๋‹ค๋ฆฌ

1. push_back vs emplace_back โ€” ํ•ต์‹ฌ ์ฐจ์ด

1-1. ํ•œ ์ค„ ๋น„๊ต

1
2
push_back(value)      โ€” "์™„์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ์ค„๊ฒŒ, ๊ฑฐ๊ธฐ์— ์˜ฎ๊ฒจ ๋„ฃ์–ด๋ผ"
emplace_back(args...) โ€” "์žฌ๋ฃŒ๋ฅผ ์ค„๊ฒŒ, ๊ฑฐ๊ธฐ์„œ ์ง์ ‘ ๋งŒ๋“ค์–ด๋ผ"

1-2. ๋™์ž‘ ๋‹จ๊ณ„ ๋น„๊ต

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point { int x, y; Point(int x, int y) : x(x), y(y) {} };
std::vector<Point> v;

// push_back โ€” 2๋‹จ๊ณ„
v.push_back(Point(1, 2));
//   1) Point(1, 2) ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ (์Šคํƒ ๋˜๋Š” ์ž„์‹œ ์˜์—ญ)
//   2) v ์˜ ์Šฌ๋กฏ์œผ๋กœ ์ด๋™ ์ƒ์„ฑ (move ctor)
// โ†’ ์ž„์‹œ ๊ฐ์ฒด 1๊ฐœ + ์ด๋™ 1๋ฒˆ

// emplace_back โ€” 1๋‹จ๊ณ„
v.emplace_back(1, 2);
//   1) v ์˜ ์Šฌ๋กฏ์—์„œ Point(1, 2) ์ง์ ‘ ์ƒ์„ฑ (placement new ํšจ๊ณผ)
// โ†’ ์ž„์‹œ ๊ฐ์ฒด 0๊ฐœ, ์ด๋™ 0๋ฒˆ โ˜…

1-3. ์‹œ๊ทธ๋‹ˆ์ฒ˜

1
2
3
4
5
6
7
// push_back โ€” 2๊ฐœ ์˜ค๋ฒ„๋กœ๋“œ
void push_back(const T& value);   // lvalue: ๋ณต์‚ฌ
void push_back(T&& value);        // rvalue: ์ด๋™ (C++11)

// emplace_back โ€” ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ 1๊ฐœ
template<class... Args>
T& emplace_back(Args&&... args);  // perfect forwarding (C++11)

1-4. ๋ฌด์—‡์„ ๋ฐ›๋Š”๊ฐ€

ย push_backemplace_back
๋ฐ›๋Š” ๊ฒƒ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด (T ๋˜๋Š” T&&)์ƒ์„ฑ์ž ์ธ์ž๋“ค (Args...)
ํ˜ธ์ถœ ์˜ˆv.push_back(Point(1,2))v.emplace_back(1, 2)
์ž„์‹œ ๊ฐ์ฒด1๊ฐœ ์ƒ์„ฑ + ์ด๋™0๊ฐœ โ˜…
๋น„์šฉ์ƒ์„ฑ + ์ด๋™ (๋˜๋Š” ๋ณต์‚ฌ)์ƒ์„ฑ 1๋ฒˆ

2. ๋‚ด๋ถ€ ๋™์ž‘ โ€” ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ + perfect forwarding

2-1. emplace_back ์˜ ๊ตฌํ˜„ ๊ณจ๊ฒฉ (๋‹จ์ˆœํ™”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template<class T, class Alloc>
template<class... Args>
T& vector<T,Alloc>::emplace_back(Args&&... args) {
    if (size_ == capacity_) {
        grow();   // ์žฌํ• ๋‹น
    }
    // ์Šฌ๋กฏ์—์„œ ์ง์ ‘ ์ƒ์„ฑ โ€” placement new ์™€ ๊ฐ™์€ ํšจ๊ณผ
    std::allocator_traits<Alloc>::construct(
        alloc_, data_ + size_,
        std::forward<Args>(args)...   // perfect forwarding
    );
    ++size_;
    return data_[size_ - 1];
}

ํ•ต์‹ฌ ๋‘ ๊ฐ€์ง€:

  • ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ (Args...) โ€” ์ž„์˜ ๊ฐœ์ˆ˜์˜ ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›์Œ
  • std::forward<Args>(args)... โ€” lvalue/rvalue ์ •๋ณด๋ฅผ ๋ณด์กดํ•ด ์ƒ์„ฑ์ž์— ์ „๋‹ฌ (perfect forwarding)

2-2. ์™œ T&& ๊ฐ€ ์•„๋‹ˆ๋ผ Args&&... ์ธ๊ฐ€

1
2
3
4
5
6
7
// ๋งŒ์•ฝ ์ด๋ ‡๊ฒŒ ํ–ˆ๋‹ค๋ฉด:
void emplace_back(T&& temp);   // โ† ์ด๋Ÿฌ๋ฉด push_back ๊ณผ ๋‹ค๋ฅผ ๊ฒŒ ์—†์Œ

// ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ์ด๋ผ์•ผ ๋น„๋กœ์†Œ:
v.emplace_back(1, 2, 3);       // ์ธ์ž 3๊ฐœ โ†’ T(1, 2, 3) ์ง์ ‘ ์ƒ์„ฑ
v.emplace_back();              // ์ธ์ž 0๊ฐœ โ†’ T() ์ง์ ‘ ์ƒ์„ฑ
v.emplace_back("Alice", 30);   // ์ธ์ž 2๊ฐœ โ†’ T("Alice", 30) ์ง์ ‘ ์ƒ์„ฑ

์—ฌ๋Ÿฌ ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•ด ์ž„์‹œ T ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์Šฌ๋กฏ์—์„œ ๋ฐ”๋กœ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒŒ emplace ์˜ ๋ณธ์งˆ.

2-3. perfect forwarding ์˜ ์˜๋ฏธ

1
2
3
4
5
std::string s = "Alice";

v.emplace_back(s);              // s ๋Š” lvalue โ†’ string(const string&) ๋ณต์‚ฌ
v.emplace_back(std::move(s));   // rvalue โ†’ string(string&&) ์ด๋™
v.emplace_back("Alice");        // rvalue (const char*) โ†’ string(const char*) ์ง์ ‘

std::forward<Args>(args)... ๋Š” ํ˜ธ์ถœ์ž๊ฐ€ lvalue ๋กœ ์คฌ๋Š”์ง€ rvalue ๋กœ ์คฌ๋Š”์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ์ž์— ์ „๋‹ฌํ•œ๋‹ค. ์ด ๋•๋ถ„์— emplace_back ํ•œ ํ•จ์ˆ˜๋กœ ๋ณต์‚ฌยท์ด๋™ยท์ง์ ‘ ์ƒ์„ฑ์„ ๋ชจ๋‘ ์ปค๋ฒ„ํ•œ๋‹ค.


3. ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ๊ฒฐ์ •์ ์ธ ์ผ€์ด์Šค

3-1. ๋ฌด๋ธŒ-์˜จ๋ฆฌ ํƒ€์ž… (unique_ptr)

1
2
3
4
5
6
7
8
9
std::vector<std::unique_ptr<Resource>> v;

// push_back โ€” std::unique_ptr ์ž„์‹œ ๋งŒ๋“ค๊ณ  ์ด๋™
v.push_back(std::make_unique<Resource>("data"));
// ๋˜๋Š”: v.push_back(std::unique_ptr<Resource>(new Resource("data")));

// emplace_back โ€” ์Šฌ๋กฏ์—์„œ unique_ptr ์ง์ ‘ ์ƒ์„ฑ
v.emplace_back(new Resource("data"));   // ์ฃผ์˜: raw new โ€” exception unsafe
v.emplace_back(std::make_unique<Resource>("data"));   // ์•ˆ์ „ + in-place

๋‘ ๋ฒˆ์งธ ์ค„์€ raw new ๋ฅผ ์‚ฌ์šฉํ•ด make_unique ์™€ emplace_back ์‚ฌ์ด์— ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์œ„ํ—˜์ด ์žˆ๋‹ค. C++14 ์ดํ›„๋กœ๋Š” std::make_unique + emplace_back ๋˜๋Š” push_back ๋‘˜ ์ค‘ ๋ฌด์—‡์„ ์จ๋„ ๋™๋“ฑํ•˜๊ฒŒ ์•ˆ์ „.

3-2. ํฐ ๊ฐ์ฒด์˜ ๋ณต์žกํ•œ ์ƒ์„ฑ์ž

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct BigObject {
    BigObject(int a, double b, const std::string& c, std::vector<int> d) { /*...*/ }
};

std::vector<BigObject> v;

// push_back โ€” BigObject ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ ํ›„ ์ด๋™
v.push_back(BigObject(1, 2.0, "x", {1,2,3}));
//          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//          ์ด ์ž„์‹œ ๊ฐ์ฒด๊ฐ€ ์Šคํƒ์— ์ž ๊น ๋งŒ๋“ค์–ด์กŒ๋‹ค๊ฐ€ ์Šฌ๋กฏ์œผ๋กœ ์ด๋™

// emplace_back โ€” ์Šฌ๋กฏ ์œ„์—์„œ ์ง์ ‘ ์ƒ์„ฑ
v.emplace_back(1, 2.0, "x", std::vector<int>{1,2,3});
//             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//             ์ด ์ธ์ž๋“ค์ด ๊ทธ๋Œ€๋กœ BigObject ์ƒ์„ฑ์ž๋กœ ์ „๋‹ฌ๋จ

์ด๋™ ์ƒ์„ฑ์ž๊ฐ€ trivial ์ด๋ผ๋„ ์ž„์‹œ ๊ฐ์ฒด ์ž์ฒด์˜ ์Šคํƒ ๊ณต๊ฐ„ + ์ •๋ฆฌ ๋น„์šฉ์ด ์žˆ๋‹ค. ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด์ผ์ˆ˜๋ก ์ฐจ์ด๊ฐ€ ์ปค์ง.

3-3. POD / ์ž‘์€ ๊ฐ์ฒด๋Š” ์ฐจ์ด ์—†์Œ

1
2
3
4
5
std::vector<int> v;
v.push_back(42);       // int ํ•œ ๋ฒˆ ๋ณต์‚ฌ โ€” 1 cycle
v.emplace_back(42);    // int ํ•œ ๋ฒˆ ์ƒ์„ฑ โ€” 1 cycle

// ์ปดํŒŒ์ผ๋Ÿฌ ์ตœ์ ํ™” ํ›„ ๋‘ ์ฝ”๋“œ๋Š” ๋ณดํ†ต ๋™์ผํ•œ ์–ด์…ˆ๋ธ”๋ฆฌ

int, float, ์ž‘์€ POD ๊ตฌ์กฐ์ฒด ๋“ฑ์€ ๋‘ ํ•จ์ˆ˜๊ฐ€ ์‚ฌ์‹ค์ƒ ๊ฐ™๋‹ค. ์˜๋ฏธ๋งŒ ์ฐจ์ด๋‚  ๋ฟ ์„ฑ๋Šฅ์€ ๊ฐ™์Œ.

3-4. ์ •๋ฆฌํ‘œ

์ผ€์ด์Šคpush_backemplace_back์ฐจ์ด
POD (int, float)1 ๋‹จ๊ณ„1 ๋‹จ๊ณ„์—†์Œ
์ž‘์€ ๊ฐ์ฒด (std::pair<int,int>)์ž„์‹œ + ์ด๋™์ง์ ‘ ์ƒ์„ฑ๋ฏธ๋ฏธ
ํฐ ๊ฐ์ฒด (๊ธด ๋ฌธ์ž์—ดยท์ปจํ…Œ์ด๋„ˆ ๋ฉค๋ฒ„)์ž„์‹œ + ์ด๋™์ง์ ‘ ์ƒ์„ฑ์žˆ์Œ
๋ฌด๋ธŒ-์˜จ๋ฆฌ (unique_ptr)์ž„์‹œ + ์ด๋™์ง์ ‘ ์ƒ์„ฑ์žˆ์Œ
explicit ์ƒ์„ฑ์ž๋ช…์‹œ ๋ณ€ํ™˜ ํ•„์š”์ž๋™ ํ˜ธ์ถœ (์œ„ํ—˜!)ํ•จ์ • ยง4

4. emplace_back ์˜ ํ•จ์ •

4-1. explicit ์ƒ์„ฑ์ž ์šฐํšŒ

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Wrapper {
    explicit Wrapper(int x) : x(x) {}   // explicit โ€” ์•”์‹œ์  ๋ณ€ํ™˜ ๊ธˆ์ง€
    int x;
};

std::vector<Wrapper> v;

// push_back โ€” explicit ๊ฐ•์ œ โ†’ ๋ช…์‹œ์  ๋ณ€ํ™˜ ํ•„์š”
v.push_back(42);                  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ
v.push_back(Wrapper(42));         // โœ… ๋ช…์‹œ์ 

// emplace_back โ€” explicit ์šฐํšŒ (์ƒ์„ฑ์ž ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋ฏ€๋กœ)
v.emplace_back(42);               // โœ… ์ปดํŒŒ์ผ OK โ€” ์˜๋„ํ•œ ๊ฒŒ ๋งž๋‚˜?

explicit ์˜ ์˜๋„๋Š” ์•”์‹œ์  ๋ณ€ํ™˜ ๊ธˆ์ง€์ธ๋ฐ emplace_back ์€ ๊ทธ๊ฑธ ์šฐํšŒํ•œ๋‹ค. ๊ฐ•์ œ ๋ณ€ํ™˜์„ ํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค๋ฉด OK, ์‹ค์ˆ˜์˜€๋‹ค๋ฉด ๋ฒ„๊ทธ.

4-2. ์ฝ”๋“œ ์˜๋„ ํ๋ฆผ

1
2
v.push_back(SomeType(arg1, arg2));      // ํƒ€์ž…์ด ๋ณด์ž„ โ€” ์˜๋„ ๋ช…ํ™•
v.emplace_back(arg1, arg2);             // ์–ด๋–ค ํƒ€์ž…์ด ๋งŒ๋“ค์–ด์ง€๋Š”์ง€ ํ—ค๋”๋ฅผ ๋ด์•ผ ์•Œ ์ˆ˜ ์žˆ์Œ

๋ฆฌ๋ทฐยท๋””๋ฒ„๊น… ์‹œ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง„๋‹ค. ํƒ€์ž…์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์€ ์ž๋ฆฌ(std::variant, ํ…œํ”Œ๋ฆฟ ์ปจํ…์ŠคํŠธ) ์—์„œ๋Š” ๋” ํ—ท๊ฐˆ๋ฆผ.

4-3. ์ธ์ž ๊ฐœ์ˆ˜ ๋ถˆ์ผ์น˜ ์‹œ ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€

1
2
3
4
5
6
struct Point { Point(int, int); };
std::vector<Point> v;

v.emplace_back(1);                // ์ปดํŒŒ์ผ ์—๋Ÿฌ โ€” Point(int) ์—†์Œ
// ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ "no matching constructor for ..." ๊ฐ™์€ ๊นŠ์€ ํ…œํ”Œ๋ฆฟ ์˜ค๋ฅ˜๋กœ ๋‚˜์˜ด
// push_back ์ด๋ผ๋ฉด "no matching function for push_back" ์œผ๋กœ ๋” ์ง๊ด€์ 

4-4. ๋™๋“ฑ ํšจ์œจ์ผ ๋•Œ๋Š” push_back ๊ถŒ์žฅ

โ€œํ™•์‹คํ•œ ์ด๋“์ด ์—†์œผ๋ฉด push_backโ€ โ€” Effective Modern C++ Item 42 ์˜ ๊ถŒ๊ณ . ๊ฐ€๋…์„ฑ + ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋ช…ํ™•์„ฑ + explicit ์•ˆ์ „์„ฑ ๋ชจ๋‘ push_back ์šฐ์œ„.

emplace ๋Š” ๋ฌด๋ธŒ-์˜จ๋ฆฌ ํƒ€์ž… / ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด / in-place ์ƒ์„ฑ์ด ๋ณธ์งˆ์ ์ธ ์ž๋ฆฌ ์—์„œ๋งŒ ์„ ํƒ.


5. vector capacity ์™€ ์žฌํ• ๋‹น (rehash ์™€ ๋น„๊ต)

ํ•ต์‹ฌ ํ•œ ๋ฌธ์žฅ

vector ๋Š” size > capacity ๊ฐ€ ๋˜๋Š” ์ˆœ๊ฐ„ ์ƒˆ ๋ฐฐ์—ด ํ• ๋‹น + ์ „์ฒด ๋ณต์‚ฌยท์ด๋™ + ๊ธฐ์กด ๋ฐฐ์—ด ํ•ด์ œ ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด๊ฒŒ unordered_map ์˜ rehash ์™€ ๋™์ผํ•œ ํŒจํ„ด์ด๋ฉฐ, iteratorยทํฌ์ธํ„ฐยท์ฐธ์กฐ ๋ชจ๋‘ ๋ฌดํšจํ™”์‹œํ‚จ๋‹ค.

5-1. size vs capacity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::vector<int> v;
std::cout << v.size() << " / " << v.capacity() << "\n";   // 0 / 0

v.push_back(1);
std::cout << v.size() << " / " << v.capacity() << "\n";   // 1 / 1 (๊ตฌํ˜„๋งˆ๋‹ค ๋‹ค๋ฆ„)

v.push_back(2);   // ์žฌํ• ๋‹น ๋ฐœ์ƒ (size > capacity)
std::cout << v.size() << " / " << v.capacity() << "\n";   // 2 / 2

v.push_back(3);   // ์žฌํ• ๋‹น ๋ฐœ์ƒ
std::cout << v.size() << " / " << v.capacity() << "\n";   // 3 / 4

v.push_back(4);   // ์žฌํ• ๋‹น ์—†์Œ (capacity ์—ฌ์œ )
std::cout << v.size() << " / " << v.capacity() << "\n";   // 4 / 4

v.push_back(5);   // ์žฌํ• ๋‹น ๋ฐœ์ƒ
std::cout << v.size() << " / " << v.capacity() << "\n";   // 5 / 8
  • size() โ€” ์‹ค์ œ ๋“ค์–ด์žˆ๋Š” ์›์†Œ ์ˆ˜
  • capacity() โ€” ์žฌํ• ๋‹น ์—†์ด ๋‹ด์„ ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ์›์†Œ ์ˆ˜
  • capacity > size ๊ฐ€ ์ •์ƒ โ€” ์—ฌ์œ ๋ถ„์ด ๋‹ค์Œ push_back ์˜ ์žฌํ• ๋‹น์„ ๋ง‰์•„์คŒ

5-2. ์žฌํ• ๋‹น ๋™์ž‘

1
2
3
4
5
6
7
8
9
์žฌํ• ๋‹น ์•Œ๊ณ ๋ฆฌ์ฆ˜:
  1) ์ƒˆ ๋ฐฐ์—ด ํ• ๋‹น (๋ณดํ†ต capacity ร— 2 ๋˜๋Š” ร— 1.5)
  2) ๊ธฐ์กด ์›์†Œ๋“ค์„ ์ƒˆ ๋ฐฐ์—ด๋กœ ์ด๋™(๋˜๋Š” ๋ณต์‚ฌ) ์ƒ์„ฑ
  3) ๊ธฐ์กด ๋ฐฐ์—ด์˜ ์›์†Œ๋“ค ์†Œ๋ฉธ์ž ํ˜ธ์ถœ
  4) ๊ธฐ์กด ๋ฐฐ์—ด ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
  5) data_ ํฌ์ธํ„ฐยทcapacity_ ๊ฐฑ์‹ 

๋น„์šฉ: O(n) โ€” ๋ชจ๋“  ์›์†Œ ์ด๋™
๋ถ€์ˆ˜ ํšจ๊ณผ: ๋ชจ๋“  iteratorยทํฌ์ธํ„ฐยท์ฐธ์กฐ ๋ฌดํšจํ™” โ˜…

5-3. growth factor โ€” ์™œ 1.5x ๋˜๋Š” 2x?

1
2
3
4
5
2๋ฐฐ ์„ฑ์žฅ (gcc, clang ์ผ๋ฐ˜):
  capacity ์ง„ํ–‰: 0 โ†’ 1 โ†’ 2 โ†’ 4 โ†’ 8 โ†’ 16 โ†’ ...

1.5๋ฐฐ ์„ฑ์žฅ (MSVC):
  capacity ์ง„ํ–‰: 0 โ†’ 1 โ†’ 2 โ†’ 3 โ†’ 4 โ†’ 6 โ†’ 9 โ†’ 13 โ†’ ...
ย 2x1.5x
์žฌํ• ๋‹น ํšŸ์ˆ˜์ ์Œ (logโ‚‚ n)์•ฝ๊ฐ„ ๋” ๋งŽ์Œ
๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„์ตœ๋Œ€ 50%์ตœ๋Œ€ 33%
๋ฉ”๋ชจ๋ฆฌ ์žฌ์‚ฌ์šฉ์–ด๋ ค์›€ (์ด์ „ ๋ธ”๋ก๋ณด๋‹ค ํฌ๋‹ˆ ๋ชป ๋“ค์–ด๊ฐ)๊ฐ€๋Šฅ (์ž‘์€ ๋ธ”๋ก๋“ค ํ•ฉ์น˜๋ฉด ์ƒˆ ํฌ๊ธฐ ๊ฐ€๋Šฅ)

amortized O(1) โ€” ๋งค push_back ์˜ ํ‰๊ท  ๋น„์šฉ์ด O(1) ์ด๋ผ๋Š” ๋ณด์žฅ. ๊ฐ€๋” O(n) ์žฌํ• ๋‹น์ด ์ผ์–ด๋‚˜๋„ ์ž์ฃผ ์ผ์–ด๋‚˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ‰๊ท ํ™”ํ•˜๋ฉด ์ƒ์ˆ˜ ์‹œ๊ฐ„.

5-4. reserve ๋กœ ์žฌํ• ๋‹น ํšŒํ”ผ

1
2
3
4
5
6
7
std::vector<int> v;
v.reserve(10000);   // capacity ๋ฅผ 10000 ์œผ๋กœ ๋ฏธ๋ฆฌ ํ™•๋ณด (size ๋Š” 0)

for (int i = 0; i < 10000; ++i)
    v.push_back(i);   // ์žฌํ• ๋‹น 0ํšŒ โ€” ์ด๋ฏธ capacity ์ถฉ๋ถ„

// ๋น„๊ต: reserve ์—†์ด 10000๋ฒˆ push_back ํ•˜๋ฉด logโ‚‚(10000) โ‰ˆ 14๋ฒˆ ์žฌํ• ๋‹น

์„ฑ๋Šฅ ๊ฒฐ์ •์  ์ฝ”๋“œ๋Š” ์˜ˆ์ƒ ํฌ๊ธฐ๋ฅผ ์•Œ๋ฉด reserve ๊ฐ€ ํ‘œ์ค€. unordered_map ์˜ u.reserve(n) ๊ณผ ๊ฐ™์€ ์—ญํ• .

5-5. vector ์žฌํ• ๋‹น vs unordered_map rehash โ€” ๋ฐ์นผ์ฝ”๋งˆ๋‹ˆ

ย vector ์žฌํ• ๋‹นunordered_map rehash
ํŠธ๋ฆฌ๊ฑฐsize > capacityload_factor > max_load_factor
๋™์ž‘์ƒˆ ๋ฐฐ์—ด + ์ „์ฒด ์ด๋™ + ํ•ด์ œ์ƒˆ ๋ฒ„ํ‚ท + ์ „์ฒด ์žฌ๋ฐฐ์น˜ + ํ•ด์ œ
๋น„์šฉO(n)O(n)
ํ‰๊ท  ๋ถ„ํ• ์ƒํ™˜O(1) (push_back)O(1) (insert)
iterator ๋ฌดํšจํ™”๋ชจ๋‘๋ชจ๋‘
ํšŒํ”ผreserve(n)reserve(n)

๋‘˜ ๋‹ค โ€œ์šฉ๋Ÿ‰ ์ดˆ๊ณผ ์‹œ ์ƒˆ๋กœ ํ• ๋‹นํ•ด ํ†ต์งธ๋กœ ์˜ฎ๊ธฐ๋Š” ํŒจํ„ดโ€. STL ์˜ ๋ถ„ํ• ์ƒํ™˜ ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ๊ณต์œ ํ•˜๋Š” ํŒจํ„ด์ด๊ณ , ์ด ๋•Œ๋ฌธ์— iterator ๋ณด๊ด€ ์‹œ ํ•ญ์ƒ ์œ„ํ—˜. 14๋ฒˆ ยง12 ์™€ ๊ฐ™์€ ๋งฅ๋ฝ.

5-6. shrink_to_fit

1
2
3
std::vector<int> v(1000);
v.resize(10);                    // size = 10, capacity = 1000 (๋‚ญ๋น„)
v.shrink_to_fit();               // capacity ๋ฅผ size ๋กœ ์ถ•์†Œ ์‹œ๋„ (๊ตฌํ˜„ ๊ถŒ๊ณ )

shrink_to_fit ์€ ๊ถŒ๊ณ ์ผ ๋ฟ ๋ณด์žฅ์ด ์•„๋‹ˆ๋‹ค. ์ผ๋ถ€ ๊ตฌํ˜„์€ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ์Œ. ์‹ค์ œ๋กœ ๋ฉ”๋ชจ๋ฆฌ ํšŒ์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋ฉด swap idiom (std::vector<T>(v).swap(v)) ์‚ฌ์šฉ.


6. ํ•ด์‹œ ์ถฉ๋Œ ์ฒ˜๋ฆฌ โ€” ์ฒด์ด๋‹ (Separate Chaining)

ํ•ต์‹ฌ ํ•œ ๋ฌธ์žฅ

์ฒด์ด๋‹์€ ๊ฐ™์€ ๋ฒ„ํ‚ท์— ๋งคํ•‘๋œ ์›์†Œ๋“ค์„ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ(๋˜๋Š” ์ž‘์€ ์ปจํ…Œ์ด๋„ˆ) ๋กœ ๋ฌถ๋Š” ๋ฐฉ์‹์ด๋‹ค. STL std::unordered_map ์ด ์ฑ„ํƒํ•œ ํ‘œ์ค€์  ๋ฐฉ์‹.

6-1. ๋™์ž‘

1
2
3
4
5
6
hash("foo") % 16 = 5
hash("xyz") % 16 = 5    โ† ์ถฉ๋Œ
hash("abc") % 16 = 5    โ† ๋˜ ์ถฉ๋Œ

buckets[5] โ”€โ†’ [foo, 100] โ”€โ†’ [xyz, 200] โ”€โ†’ [abc, 300] โ”€โ†’ nullptr
              (์ฒด์ธ ๋…ธ๋“œ 1)  (์ฒด์ธ ๋…ธ๋“œ 2)  (์ฒด์ธ ๋…ธ๋“œ 3)

๊ฐ ๋ฒ„ํ‚ท ์Šฌ๋กฏ์ด ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ํ—ค๋“œ ํฌ์ธํ„ฐ๋ฅผ ๋“ค๊ณ  ์žˆ๊ณ , ์ถฉ๋Œ ์‹œ ์ƒˆ ๋…ธ๋“œ๋ฅผ ๊ทธ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

6-2. ๊ตฌํ˜„ ๊ณจ๊ฒฉ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Node {
    Key key;
    Value value;
    Node* next;
};

std::vector<Node*> buckets;   // ๋ฒ„ํ‚ท ๋ฐฐ์—ด (๊ฐ ์นธ์ด ๋ฆฌ์ŠคํŠธ ํ—ค๋“œ)

void insert(Key k, Value v) {
    size_t idx = hash(k) % buckets.size();
    Node* n = new Node{k, v, buckets[idx]};   // ์ƒˆ ๋…ธ๋“œ๋ฅผ ํ—ค๋“œ๋กœ
    buckets[idx] = n;
}

Value* find(Key k) {
    size_t idx = hash(k) % buckets.size();
    for (Node* n = buckets[idx]; n; n = n->next) {
        if (n->key == k) return &n->value;
    }
    return nullptr;
}

6-3. ์‹œ๊ฐ„ ๋ณต์žก๋„

์—ฐ์‚ฐํ‰๊ท  (๊ท ๋“ฑ ๋ถ„ํฌ)์ตœ์•… (๋ชจ๋“  ํ‚ค ์ถฉ๋Œ)
insertO(1)O(n)
findO(1)O(n)
eraseO(1)O(n)

ํ‰๊ท ์€ ์ฒด์ธ ๊ธธ์ด โ‰ˆ load_factor ๋ผ์„œ load_factor ๊ฐ€ 1.0 ์ด๋ฉด ํ‰๊ท  1ํšŒ ๋น„๊ต๋กœ ๋.

6-4. ์žฅ์ 

  • ๊ตฌํ˜„ ๊ฐ„๋‹จ โ€” ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ์ถ”๊ฐ€/์‚ญ์ œ๋งŒ ์•Œ๋ฉด ๋จ
  • load_factor 1.0 ์ดˆ๊ณผํ•ด๋„ ๋™์ž‘ (์„ฑ๋Šฅ ์ €ํ•˜๋งŒ)
  • ์‚ญ์ œ ๋‹จ์ˆœ โ€” ๋…ธ๋“œ ํ•œ ๊ฐœ๋งŒ ์ œ๊ฑฐํ•˜๋ฉด ๋
  • iterator ์•ˆ์ •์„ฑ (๊ฐœ๋ณ„ ๋…ธ๋“œ ๋‹จ์œ„) โ€” rehash ๋งŒ ์•„๋‹ˆ๋ฉด ๋…ธ๋“œ ์•ˆ์ „

6-5. ๋‹จ์ 

  • ๋…ธ๋“œ ๋ถ„์‚ฐ โ€” ํž™์— ๋”ฐ๋กœ ํ• ๋‹น, ์บ์‹œ ์ ๋Œ€์ 
  • ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ โ€” ๋…ธ๋“œ๋งˆ๋‹ค next ํฌ์ธํ„ฐ + ํž™ ํ—ค๋”
  • ํฌ์ธํ„ฐ ์ถ”์  ๋น„์šฉ โ€” ์บ์‹œ ๋ฏธ์Šค ๊ฐ€๋Šฅ์„ฑ

์ด ๋‹จ์ ๋“ค ๋•Œ๋ฌธ์— ๊ฒŒ์ž„ ์—”์ง„์—์„œ๋Š” ํšŒํ”ผ โ€” Unreal TMap ์ด ๋‹ค๋ฅธ ๋ฐฉ์‹์„ ์“ฐ๋Š” ์ด์œ .


7. ํ•ด์‹œ ์ถฉ๋Œ ์ฒ˜๋ฆฌ โ€” ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ (Open Addressing)

ํ•ต์‹ฌ ํ•œ ๋ฌธ์žฅ

์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์€ ์ถฉ๋Œ ์‹œ ๊ฐ™์€ ๋ฒ„ํ‚ท ๋ฐฐ์—ด ์•ˆ์—์„œ ๋‹ค์Œ ๋นˆ ์Šฌ๋กฏ์„ ์ฐพ์•„ ๊ฐ€๋Š” ๋ฐฉ์‹์ด๋‹ค. ๋…ธ๋“œ๋ฅผ ๋”ฐ๋กœ ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ  ๋ฐฐ์—ด ํ•œ ๋ฉ์–ด๋ฆฌ ๋งŒ ์‚ฌ์šฉํ•ด ์บ์‹œ ์นœํ™”์ .

7-1. ๊ธฐ๋ณธ ์•„์ด๋””์–ด

1
2
3
4
5
6
hash("foo") % 16 = 5   โ†’ buckets[5] ์— ์ €์žฅ
hash("xyz") % 16 = 5   โ†’ buckets[5] ์ฐจ์žˆ์Œ โ†’ buckets[6] ์‹œ๋„ โ†’ ๋น„์–ด์žˆ์œผ๋ฉด ๊ฑฐ๊ธฐ

buckets: [_, _, _, _, _, foo, xyz, _, _, _, ...]
                         โ†‘    โ†‘
                    ์›๋ž˜ ์œ„์น˜  probing ์œผ๋กœ ์ด๋™

์ฒด์ด๋‹๊ณผ ๋‹ฌ๋ฆฌ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ™์€ ๋ฐฐ์—ด ์•ˆ์— ์žˆ๋‹ค.

7-2. probing ์ „๋žต 3๊ฐ€์ง€

(1) ์„ ํ˜• ํƒ์‚ฌ (Linear Probing)

1
2
3
4
์ถฉ๋Œ ์‹œ +1 ์นธ์”ฉ ์ด๋™
hash(k) % N โ†’ +1 โ†’ +2 โ†’ +3 โ†’ ... ๋นˆ ์Šฌ๋กฏ๊นŒ์ง€

buckets[5] ์ฐจ์žˆ์Œ โ†’ buckets[6] โ†’ buckets[7] โ†’ ...
  • ์žฅ์ : ์บ์‹œ ์นœํ™” ์ตœ๊ฐ• (์ธ์ ‘ ์Šฌ๋กฏ)
  • ๋‹จ์ : ํด๋Ÿฌ์Šคํ„ฐ๋ง (Primary Clustering) โ€” ์ถฉ๋Œ์ด ์ธ์ ‘ ์Šฌ๋กฏ์— ๋ชฐ๋ ค ๊ธธ์–ด์ง

(2) ์ด์ฐจ ํƒ์‚ฌ (Quadratic Probing)

1
2
์ถฉ๋Œ ์‹œ +1ยฒ, +2ยฒ, +3ยฒ, ... ์ด๋™
hash(k) % N โ†’ +1 โ†’ +4 โ†’ +9 โ†’ +16 โ†’ ...
  • ์žฅ์ : ํด๋Ÿฌ์Šคํ„ฐ๋ง ์™„ํ™”
  • ๋‹จ์ : load_factor 0.5 ์ด์ƒ์—์„œ ๋ฌดํ•œ ๋ฃจํ”„ ๊ฐ€๋Šฅ (๋ชจ๋“  ์Šฌ๋กฏ์„ ๋ชป ๋ฐฉ๋ฌธ)

(3) ์ด์ค‘ ํ•ด์‹ฑ (Double Hashing)

1
2
๋‘ ๋ฒˆ์งธ ํ•ด์‹œ ํ•จ์ˆ˜ hash2(k) ๋กœ step ๊ฒฐ์ •
hash1(k) % N โ†’ +hash2(k) โ†’ +2*hash2(k) โ†’ ...
  • ์žฅ์ : ํด๋Ÿฌ์Šคํ„ฐ๋ง ๊ฑฐ์˜ ์—†์Œ
  • ๋‹จ์ : ํ•ด์‹œ ํ•จ์ˆ˜ 2๊ฐœ ํ•„์š”, ๊ณ„์‚ฐ ๋น„์šฉ ์ฆ๊ฐ€

7-3. ์‹œ๊ฐ„ ๋ณต์žก๋„

์—ฐ์‚ฐload_factor 0.5load_factor 0.9load_factor 1.0
find~1.5ํšŒ ๋น„๊ต~5.5ํšŒ ๋น„๊ต๋ฌดํ•œ ๋ฃจํ”„ โ˜…
insert~1.5ํšŒ ๋น„๊ต~5.5ํšŒ ๋น„๊ต๋ถˆ๊ฐ€

์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์€ load_factor < 1.0 ๊ฐ•์ œ โ€” ๊ฐ€๋“ ์ฐจ๋ฉด ๋™์ž‘ ์ž์ฒด๊ฐ€ ์•ˆ ๋จ. ๋ณดํ†ต 0.7~0.75 ์ž„๊ณ„๋กœ rehash.

7-4. ์‚ญ์ œ์˜ ์–ด๋ ค์›€ โ€” Tombstone

1
2
3
4
5
6
7
8
9
10
11
์‚ญ์ œ ์‹œ ๊ทธ๋ƒฅ ๋น„์›Œ๋ฒ„๋ฆฌ๋ฉด probing ์‚ฌ์Šฌ์ด ๋Š์–ด์ง:
  buckets: [_, _, _, _, _, foo, xyz, abc]
  erase("xyz") โ†’ buckets[6] = empty
  buckets: [_, _, _, _, _, foo, _,   abc]
                                โ†‘
                          ์—ฌ๊ธฐ์„œ probing ๋Š๊น€ โ†’ "abc" ๋ชป ์ฐพ์Œ

ํ•ด๊ฒฐ: tombstone (์‚ญ์ œ ํ‘œ์‹œ)
  buckets: [_, _, _, _, _, foo, โŒซ, abc]
                                โ†‘
                          ๋น„์–ด์žˆ์ง€๋งŒ probing ์€ ํ†ต๊ณผ

tombstone ์€ ๋ณ„๋„ ๋น„ํŠธ ๋˜๋Š” sentinel ๊ฐ’์œผ๋กœ ํ‘œ์‹œ. ๋„ˆ๋ฌด ๋งŽ์ด ์Œ“์ด๋ฉด rehash ๋กœ ์ •๋ฆฌ.

7-5. ๋กœ๋นˆ ํ›„๋“œ ํ•ด์‹ฑ (Robin Hood Hashing)

probing ๊ฑฐ๋ฆฌ๊ฐ€ ๋” ๊ธด ์›์†Œ๋ฅผ ๋งŒ๋‚˜๋ฉด ์ž๋ฆฌ๋ฅผ ๋นผ์•—์•„ probing ๊ฑฐ๋ฆฌ๋ฅผ ๊ท ๋“ฑํ™”. ํ‰๊ท  + ์ตœ์•… ๋ชจ๋‘ ์ตœ์„ ์˜ ๋ถ„ํฌ.

์ด๋ฆ„์˜ ์œ ๋ž˜: โ€œ๋ถ€์ž(์งง์€ probing) ํ•œํ…Œ์„œ ๋นผ์•—์•„ ๊ฐ€๋‚œํ•œ(๊ธด probing) ์‚ฌ๋žŒํ•œํ…Œ ์คŒโ€

7-6. ์žฅ์ ยท๋‹จ์ 

์žฅ์ 

  • ์บ์‹œ ์นœํ™” โ€” ๊ฐ™์€ ๋ฐฐ์—ด ์•ˆ์—์„œ ๋‹ค์Œ ์Šฌ๋กฏ์ด๋ผ prefetch ํšจ๊ณผ
  • ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ ์—†์Œ โ€” next ํฌ์ธํ„ฐ ๋ถˆํ•„์š”, ํž™ ํ—ค๋” ์—†์Œ
  • ํ• ๋‹น ํšŸ์ˆ˜ ์ ์Œ โ€” ๋ฒ„ํ‚ท ๋ฐฐ์—ด 1๊ฐœ๋งŒ

๋‹จ์ 

  • load_factor ํ•œ๊ณ„ โ€” 0.7~0.75 ๊ฐ•์ œ (์ฒด์ด๋‹์€ 1.0 ์ดˆ๊ณผ๋„ OK)
  • ํด๋Ÿฌ์Šคํ„ฐ๋ง โ€” ์ถฉ๋Œ์ด ์ธ์ ‘์— ๋ชฐ๋ฆผ (probing ์ „๋žต์œผ๋กœ ์™„ํ™”)
  • ์‚ญ์ œ ๋ณต์žก โ€” tombstone ๊ด€๋ฆฌ ํ•„์š”

8. STL ยท Unreal ์˜ ์ถฉ๋Œ ํ•ด๊ฒฐ ์ •์ฑ…

8-1. ๋น„๊ตํ‘œ

ย STL std::unordered_mapUnreal TMap
์ถฉ๋Œ ํ•ด๊ฒฐSeparate Chaining โ˜…Open Addressing ๋ณ€ํ˜• โ˜…
์บ์‹œ ์นœํ™”๋ณดํ†ต (์ฒด์ธ ๋…ธ๋“œ ๋ถ„์‚ฐ)๋” ์ข‹์Œ (๋ฐฐ์—ด ์ธ์ ‘)
๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๋ฒ„ํ‚ท + ๋…ธ๋“œ next ํฌ์ธํ„ฐ + ํž™ ํ—ค๋”์Šฌ๋กฏ ๋ฐฐ์—ด + ์•ฝ๊ฐ„์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
max_load_factor1.0 (๊ตฌํ˜„ ์ •์˜ ๊ฐ€๋Šฅ)์ž‘์€ ๊ฐ’ (๊ตฌํ˜„ ์ •์˜)
์‚ญ์ œ๋…ธ๋“œ ์ œ๊ฑฐtombstone ๋˜๋Š” backshift
ํ• ๋‹น ํšŸ์ˆ˜๋งค insert ๋งˆ๋‹ค ๋…ธ๋“œ 1๊ฐœ๊ฑฐ์˜ ์—†์Œ (๋ฒ„ํ‚ท ๋ฐฐ์—ด 1๊ฐœ)

8-2. ์™œ STL ์€ ์ฒด์ด๋‹์ธ๊ฐ€

  • ํ‘œ์ค€ ๋ช…์„ธ์ƒ iterator ์•ˆ์ •์„ฑ ์š”๊ตฌ์‚ฌํ•ญ โ€” ์ฒด์ด๋‹์ด ๋งŒ์กฑ์‹œํ‚ค๊ธฐ ์‰ฌ์›€
  • load_factor 1.0 ์ดˆ๊ณผ ํ—ˆ์šฉ โ€” ์ผ๋ถ€ ์›Œํฌ๋กœ๋“œ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ
  • ๊ตฌํ˜„ ๋‹จ์ˆœ์„ฑ โ€” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์•ˆ์ •์„ฑ ์šฐ์„ 

8-3. ์™œ Unreal ์€ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์ธ๊ฐ€

  • ์บ์‹œ ์นœํ™” โ€” ๊ฒŒ์ž„ frame budget (16.6ms) ์•ˆ์—์„œ lookup ๋น„์šฉ ์ตœ์†Œํ™”
  • ํ• ๋‹น ํšŸ์ˆ˜ โ†“ โ€” ๋งค insert ๋งˆ๋‹ค new ์•ˆ ํ•จ โ†’ GC + ๋‹จํŽธํ™” ๊ฐ์†Œ
  • ๋ฉ”๋ชจ๋ฆฌ ์ง€์—ญ์„ฑ โ€” TArray + TMap + TSet ๋ชจ๋‘ ๊ฐ™์€ ์ฒ ํ•™ (1๊ธ‰ ์‹œ๋ฏผ)
  • GC ํ†ตํ•ฉ โ€” UPROPERTY ์™€ ํ•จ๊ป˜ ์ž‘๋™, raw ํฌ์ธํ„ฐ ๋…ธ๋“œ ํšŒํ”ผ

14๋ฒˆ ยง9-6 ๊ฒฐ๋ก ๊ณผ ๊ฐ™์Œ: โ€œ๋ถ„์‚ฐ ๋…ธ๋“œ ์ž๋ฃŒ๊ตฌ์กฐ ํšŒํ”ผโ€ ๊ฐ€ ๊ฒŒ์ž„ ์—”์ง„์˜ ์ผ๊ด€๋œ ์ฒ ํ•™.


9. ๋ฉด์ ‘ ๋‹จ๊ณจ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ

Q1. push_back ๊ณผ emplace_back ์˜ ์ฐจ์ด๋Š”?

push_back ์€ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ์Šฌ๋กฏ์œผ๋กœ ๋ณต์‚ฌยท์ด๋™ํ•˜๊ณ , emplace_back ์€ ์Šฌ๋กฏ ์œ„์—์„œ ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ + perfect forwarding ์œผ๋กœ ์ง์ ‘ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋ฌด๋ธŒ-์˜จ๋ฆฌ ํƒ€์ž…์ด๋‚˜ ํฐ ๊ฐ์ฒด์—์„œ ์ž„์‹œ ๊ฐ์ฒด ์ƒ์„ฑ ๋น„์šฉ์„ ์ค„์ด๋Š” ๊ฒŒ emplace ์˜ ์˜์˜์ž…๋‹ˆ๋‹ค.

Q2. ํ•ญ์ƒ emplace_back ์ด ๋” ๋น ๋ฅธ๊ฐ€?

์•„๋‹™๋‹ˆ๋‹ค. POD ๋‚˜ ์ž‘์€ ๊ฐ์ฒด๋Š” ์ปดํŒŒ์ผ๋Ÿฌ ์ตœ์ ํ™”๋กœ ๋‘˜์ด ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. emplace ๋Š” explicit ์šฐํšŒ, ํƒ€์ž… ๋ชจํ˜ธ์„ฑ, ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๊ฐ€๋…์„ฑ ๊ฐ™์€ ํ•จ์ •์ด ์žˆ์–ด ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด๋‚˜ ๋ฌด๋ธŒ-์˜จ๋ฆฌ ํƒ€์ž…์—์„œ๋งŒ ๋ถ„๋ช…ํžˆ ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Q3. vector ์˜ capacity ๊ฐ€ size ์™€ ๋‹ค๋ฅธ ์ด์œ ?

push_back ๋งˆ๋‹ค ์žฌํ• ๋‹นํ•˜๋ฉด ๋งค๋ฒˆ O(n) ์ด๋ผ ๋ˆ„์  ๋น„์šฉ์ด ์ปค์ง‘๋‹ˆ๋‹ค. capacity ๋ฅผ ๋ฏธ๋ฆฌ size ๋ณด๋‹ค ํฌ๊ฒŒ ์žก์•„๋‘๊ณ , ์ดˆ๊ณผํ•  ๋•Œ๋งŒ ์žฌํ• ๋‹นํ•˜๋ฉด amortized O(1) ์ด ๋ฉ๋‹ˆ๋‹ค. ๋ณดํ†ต 1.5~2๋ฐฐ ์„ฑ์žฅ ์ •์ฑ…์„ ์”๋‹ˆ๋‹ค.

Q4. ์žฌํ• ๋‹น ์‹œ iterator ๋Š”?

๋ชจ๋‘ ๋ฌดํšจํ™”๋ฉ๋‹ˆ๋‹ค. ์ƒˆ ๋ฉ”๋ชจ๋ฆฌ์— ๋ณต์‚ฌ๋œ ํ›„ ๊ธฐ์กด ๋ฉ”๋ชจ๋ฆฌ๋Š” ํ•ด์ œ๋˜๋ฏ€๋กœ, ๊ธฐ์กด iteratorยทํฌ์ธํ„ฐยท์ฐธ์กฐ๋Š” ๋Œ•๊ธ€๋ง์ด ๋ฉ๋‹ˆ๋‹ค. 14๋ฒˆ unordered_map rehash ์™€ ๊ฐ™์€ ํŒจํ„ด์ด๊ณ , reserve ๋กœ ํšŒํ”ผํ•ฉ๋‹ˆ๋‹ค.

Q5. ํ•ด์‹œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด?

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ฒด์ด๋‹(Separate Chaining) ์€ ๊ฐ™์€ ๋ฒ„ํ‚ท์— ๋งคํ•‘๋œ ์›์†Œ๋“ค์„ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฌถ๋Š” ๋ฐฉ์‹์ด๊ณ , STL std::unordered_map ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ(Open Addressing) ์€ ๊ฐ™์€ ๋ฐฐ์—ด ์•ˆ์—์„œ ๋‹ค์Œ ๋นˆ ์Šฌ๋กฏ์œผ๋กœ probing ํ•˜๋Š” ๋ฐฉ์‹์ด๊ณ , Unreal TMap ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์บ์‹œ ์นœํ™”์„ฑ์€ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์ด ์šฐ์œ„, load_factor ํ•œ๊ณ„๋Š” ์ฒด์ด๋‹์ด ์ž์œ ๋กœ์›€์ž…๋‹ˆ๋‹ค.

Q6. ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์˜ probing ์ข…๋ฅ˜?

์„ ํ˜• ํƒ์‚ฌ(+1 ์”ฉ), ์ด์ฐจ ํƒ์‚ฌ(+1ยฒ, +2ยฒ, +3ยฒ ์”ฉ), ์ด์ค‘ ํ•ด์‹ฑ(๋‘ ๋ฒˆ์งธ ํ•ด์‹œ ํ•จ์ˆ˜ step) ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์„ ํ˜•์€ ์บ์‹œ ์นœํ™” ์ตœ๊ฐ•์ด์ง€๋งŒ ํด๋Ÿฌ์Šคํ„ฐ๋ง์ด ์‹ฌํ•˜๊ณ , ์ด์ค‘ ํ•ด์‹ฑ์€ ๋ถ„ํฌ๋Š” ์ข‹์ง€๋งŒ ํ•ด์‹œ 2๊ฐœ ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋“ญ๋‹ˆ๋‹ค. ๋กœ๋นˆ ํ›„๋“œ ํ•ด์‹ฑ์€ probing ๊ฑฐ๋ฆฌ๋ฅผ ๊ท ๋“ฑํ™”ํ•˜๋Š” ๋ณ€ํ˜•์ž…๋‹ˆ๋‹ค.

Q7. ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์˜ ์‚ญ์ œ๋Š” ์–ด๋–ป๊ฒŒ?

๊ทธ๋ƒฅ ๋น„์šฐ๋ฉด probing ์‚ฌ์Šฌ์ด ๋Š์–ด์ ธ ๋’ค์˜ ์›์†Œ๋ฅผ ๋ชป ์ฐพ์Šต๋‹ˆ๋‹ค. tombstone ์œผ๋กœ โ€œ์‚ญ์ œ๋จโ€ ํ‘œ์‹œ๋ฅผ ๋‘๊ณ  probing ์€ ํ†ต๊ณผํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. tombstone ์ด ๋„ˆ๋ฌด ์Œ“์ด๋ฉด rehash ๋กœ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Q8. STL ์€ ์™œ ์ฒด์ด๋‹, ์–ธ๋ฆฌ์–ผ์€ ์™œ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ?

STL ์€ ํ‘œ์ค€ ๋ช…์„ธ์˜ iterator ์•ˆ์ •์„ฑ ์š”๊ตฌ์™€ load_factor 1.0 ์ดˆ๊ณผ ํ—ˆ์šฉ์„ ์œ„ํ•ด ์ฒด์ด๋‹์„ ํƒํ–ˆ๊ณ , Unreal ์€ ๋งค ํ”„๋ ˆ์ž„ 16.6ms ์•ˆ์—์„œ lookup ๋น„์šฉ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ์บ์‹œ ์นœํ™”์ ์ธ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ + open addressing ๋ณ€ํ˜•์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. โ€œ๋ถ„์‚ฐ ๋…ธ๋“œ ์ž๋ฃŒ๊ตฌ์กฐ ํšŒํ”ผโ€ ๋ผ๋Š” ๊ฒŒ์ž„ ์—”์ง„์˜ ์ผ๊ด€๋œ ์ฒ ํ•™์ž…๋‹ˆ๋‹ค.

Q9. vector ์žฌํ• ๋‹น๊ณผ ํ•ด์‹œ rehash ์˜ ๊ณตํ†ต์ ?

๋‘˜ ๋‹ค ์šฉ๋Ÿ‰ ์ดˆ๊ณผ ์‹œ ์ƒˆ๋กœ ํ• ๋‹นํ•ด ํ†ต์งธ๋กœ ์˜ฎ๊ธฐ๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. vector ๋Š” size > capacity, ํ•ด์‹œ๋Š” load_factor > max_load_factor ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ์ด๊ณ , ๋‘˜ ๋‹ค O(n) ๋น„์šฉ์— ๋ชจ๋“  iterator ๋ฅผ ๋ฌดํšจํ™”ํ•˜๋ฉฐ, reserve ๋กœ ํšŒํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Q10. amortized O(1) ์ด๋ž€?

๋งค ์—ฐ์‚ฐ์˜ ํ‰๊ท  ๋น„์šฉ์ด O(1) ์ด๋ผ๋Š” ๋ณด์žฅ์ž…๋‹ˆ๋‹ค. push_back 1๋งŒ ๋ฒˆ ์ค‘ 14๋ฒˆ(2๋ฐฐ ์„ฑ์žฅ ์‹œ logโ‚‚ 1๋งŒ) ๋งŒ ์žฌํ• ๋‹น์ด๋ผ O(n) ์ด๊ณ  ๋‚˜๋จธ์ง€ 9986๋ฒˆ์€ O(1) ์ž…๋‹ˆ๋‹ค. ํ•ฉ์‚ฐํ•ด์„œ 1๋งŒ์œผ๋กœ ๋‚˜๋ˆ„๋ฉด ํ‰๊ท ์€ ์ƒ์ˆ˜. โ€œ๊ฐ€๋” ๋น„์‹ผ ์—ฐ์‚ฐ์ด ํ‰๊ท ์œผ๋กœ ๋ฌปํžˆ๋Š” ๋ถ„์„ ๊ธฐ๋ฒ•โ€์ž…๋‹ˆ๋‹ค.


10. ํšŒ๊ท€ ๋‹ค๋ฆฌ

  • 13_vector_vs_list.md โ€” vector ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒยท์บ์‹œ ์นœํ™”์„ฑ. ยง5 capacity/์žฌํ• ๋‹น ์˜ ์›๋ณธ.
  • 14_std_map.md โ€” std::map ์˜ RB-Tree, unordered_map rehash ์™€ ๋น„๊ต. ยง5-5 ์˜ ๋ฐ์นผ์ฝ”๋งˆ๋‹ˆ ํ‘œ.
  • 14_std_map_followup.md โ€” ๋ชจ์˜๋ฉด์ ‘ ๊ผฌ๋ฆฌ๋ฌผ๊ธฐ 16๊ฐœ. ยง11 (ํ•ด์‹œยท๋ฒ„ํ‚ท), ยง12 (load_factor) ์™€ ๋ณธ ๋ฌธ์„œ ยง6ยทยง7 ๊ฐ€ ํ•œ ์„ธํŠธ.
  • 11_smart_pointer.md โ€” unique_ptr + emplace_back ์•ˆ์ „ ํŒจํ„ด.
  • 10_pointer_deepdive.md โ€” vector ์žฌํ• ๋‹น ํ›„ ๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ.

๋ชจ์˜๋ฉด์ ‘ ๋‹ต๋ณ€ ํ…œํ”Œ๋ฆฟ (1๋ถ„ / 3๋ถ„)

1๋ถ„ ๋‹ต๋ณ€

push_back ์€ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ vector ์˜ ๋งˆ์ง€๋ง‰ ์Šฌ๋กฏ์— ๋ณต์‚ฌยท์ด๋™ํ•˜๊ณ , emplace_back ์€ ๊ฐ€๋ณ€ ํ…œํ”Œ๋ฆฟ + perfect forwarding ์œผ๋กœ ์Šฌ๋กฏ ์œ„์—์„œ ์ƒ์„ฑ์ž๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ž„์‹œ ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•„ ๋ฌด๋ธŒ-์˜จ๋ฆฌ ํƒ€์ž…์ด๋‚˜ ๋ฌด๊ฑฐ์šด ๊ฐ์ฒด์—์„œ ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ explicit ์ƒ์„ฑ์ž๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๊ณ  ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ณด์—ฌ์ฃผ์ง€ ์•Š์•„ ์˜๋„๊ฐ€ ํ๋ ค์งˆ ์ˆ˜ ์žˆ์–ด, ํ™•์‹คํ•œ ์ด๋“์ด ์—†์œผ๋ฉด push_back ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

3๋ถ„ ๋‹ต๋ณ€ (๊ผฌ๋ฆฌ๋ฌผ๊ธฐ ํฌํ•จ)

(์•ž 1๋ถ„ + ์ด์–ด์„œ)

vector ๋Š” size > capacity ๊ฐ€ ๋˜๋Š” ์ˆœ๊ฐ„ ์ƒˆ ๋ฐฐ์—ด์„ ํ• ๋‹นํ•˜๊ณ  ์ „์ฒด๋ฅผ ์˜ฎ๊ธด ๋’ค ๊ธฐ์กด์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค. growth factor ๋Š” ๋ณดํ†ต 1.5 ๋˜๋Š” 2๋ฐฐ์ด๋ฉฐ, ์ด ๋ถ„ํ• ์ƒํ™˜์œผ๋กœ push_back ์ด amortized O(1) ์ด ๋ฉ๋‹ˆ๋‹ค. ์žฌํ• ๋‹น์ด ์ผ์–ด๋‚˜๋ฉด ๋ชจ๋“  iteratorยทํฌ์ธํ„ฐยท์ฐธ์กฐ๊ฐ€ ๋ฌดํšจํ™”๋˜๋ฏ€๋กœ, ํฌ๊ธฐ๋ฅผ ์•Œ๋ฉด reserve(n) ์œผ๋กœ ๋ฏธ๋ฆฌ ํ™•๋ณดํ•˜๋Š” ๊ฒƒ์ด ํ‘œ์ค€์ž…๋‹ˆ๋‹ค.

์ด ํŒจํ„ด์€ unordered_map ์˜ rehash ์™€ ์ •ํ™•ํžˆ ๊ฐ™์Šต๋‹ˆ๋‹ค. unordered_map ์€ load_factor > max_load_factor ์ผ ๋•Œ ์ƒˆ ๋ฒ„ํ‚ท ๋ฐฐ์—ด์„ ํ• ๋‹นํ•ด ๋ชจ๋“  ์›์†Œ๋ฅผ ์žฌ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด๋งŒ ๋‹ค๋ฅผ ๋ฟ ๋น„์šฉ O(n), iterator ์ „์ฒด ๋ฌดํšจํ™”, reserve ๋กœ ํšŒํ”ผ ๊ฐ€๋Šฅ โ€” ๋ชจ๋‘ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

ํ•ด์‹œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ๋•Œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. STL std::unordered_map ์€ ๊ฐ™์€ ๋ฒ„ํ‚ท์— ๋งคํ•‘๋œ ์›์†Œ๋“ค์„ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฌถ๋Š” ์ฒด์ด๋‹(Separate Chaining) ์„, Unreal TMap ์€ ๊ฐ™์€ ๋ฐฐ์—ด ์•ˆ์—์„œ ๋‹ค์Œ ๋นˆ ์Šฌ๋กฏ์œผ๋กœ probing ํ•˜๋Š” ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ(Open Addressing) ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ์ž„ ์—”์ง„์€ ๋งค ํ”„๋ ˆ์ž„ 16.6ms ์•ˆ์—์„œ lookup ๋น„์šฉ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ์บ์‹œ ์นœํ™”์ ์ธ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ์„ ํƒํ–ˆ๊ณ , ์ด๋Š” vector โ†’ TArray, list ํšŒํ”ผ ์™€ ๊ฐ™์€ ์ผ๊ด€๋œ ์ฒ ํ•™์ž…๋‹ˆ๋‹ค.

์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

ยฉ GoldBoll. ์ผ๋ถ€ ๊ถŒ๋ฆฌ ๋ณด์œ 

Powered by Jekyll with Chirpy theme

์ธ๊ธฐ ํƒœ๊ทธ