TL;DR

  • busy polling์€ non-blocking ์ž์›์„ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๋ฐ˜๋ณต ํ™•์ธํ•˜๋Š” synchronous ๋ฐฉ์‹ โ€” ๋ณดํ†ต์€ CPU๋ฅผ ํƒœ์šฐ๋Š” ๋‚ญ๋น„
  • epoll ๊ฐ™์€ ์ค€๋น„์™„๋ฃŒ ํ†ต์ง€๊ฐ€ ๋˜๋ฌป๊ธฐ๋ฅผ ์—†์•  ํ‘œ์ค€์ด์ง€๋งŒ ์ˆ˜ ๋งˆ์ดํฌ๋กœ์ดˆ ์ง€์—ฐ์ด ์•„๊นŒ์šด ์ €์ง€์—ฐ ๋„๋ฉ”์ธ์€ ์Šคํ•€์„ ์„ ํƒ
  • ๊ณ ๋นˆ๋„ ํŠธ๋ ˆ์ด๋”ฉยทDPDKยทSO_BUSY_POLLยทspinlock์ด ๋Œ€ํ‘œ ์‚ฌ๋ก€ โ€” ์ฝ”์–ด ํ•˜๋‚˜๋ฅผ ํƒœ์›Œ ์ง€์—ฐ์„ ์‚ฌ๋Š” ๊ฑฐ๋ž˜

AI-assisted


1. busy polling์ด๋ž€

Concurrency and Parallelism์—์„œ ๋™๊ธฐ/๋น„๋™๊ธฐ์™€ ๋ธ”๋กœํ‚น/๋…ผ๋ธ”๋กœํ‚น์„ ์ง๊ตํ•˜๋Š” ๋‘ ์ถ•์œผ๋กœ ๊ฐˆ๋ž๋‹ค.
๊ทธ์ค‘ synchronous + non-blocking ์กฐํ•ฉ์ด busy polling์˜ ๋ฟŒ๋ฆฌ๋‹ค.

  • non-blocking ํ˜ธ์ถœ์€ ์ง€๊ธˆ ๋๋‚ผ ์ˆ˜ ์—†์œผ๋ฉด ์ฆ‰์‹œ ๋Œ์•„์˜จ๋‹ค
  • ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ ์ž๋ฆฌ์„œ ํŒ๋‹จํ•˜๊ณ (synchronous), ์•„์ง์ด๋ฉด ๋‹ค์‹œ ๋ฌป๋Š”๋‹ค
  • ์ด โ€œ๋‹ค์‹œ ๋ฌป๊ธฐโ€๋ฅผ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”์œ ๋Œ€๊ธฐ(busy polling)๋‹ค
sock.setblocking(False)
while True:
    try:
        data = sock.recv(4096)   # ๋ฐ์ดํ„ฐ ์—†์œผ๋ฉด ์ฆ‰์‹œ ์˜ˆ์™ธ
        break
    except BlockingIOError:
        pass                     # ๊ณ„์† ๋˜๋ฌผ์Œ โ€” ๊ทธ๋™์•ˆ ์ฝ”์–ด๋ฅผ ํƒœ์›€

์Šค๋ ˆ๋“œ๊ฐ€ ๋ฉˆ์ถ”์ง€๋Š” ์•Š๋Š”๋‹ค. ๋Œ€์‹  ์ฝ”์–ด ํ•˜๋‚˜๋ฅผ ๋ถ™์žก๊ณ  โ€œ๋๋‚˜?โ€๋งŒ ๋ฐ˜๋ณตํ•œ๋‹ค.


2. ๋ณดํ†ต์€ ๋‚ญ๋น„๋‹ค โ€” epoll์ด ํ‘œ์ค€์ธ ์ด์œ 

busy polling์˜ ๋ฌธ์ œ๋Š” ์ค€๋น„๋˜์ง€ ์•Š์€ ์ž์›์—๋„ CPU๋ฅผ ๊ณ„์† ์“ด๋‹ค๋Š” ์ ์ด๋‹ค. ์†Œ์ผ“ ํ•˜๋‚˜๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ๊นŒ์ง€ ์ˆ˜์‹ญ ๋ฐ€๋ฆฌ์ดˆ๊ฐ€ ๊ฑธ๋ฆฐ๋‹ค๋ฉด, ๊ทธ ์‚ฌ์ด ์ˆ˜๋ฐฑ๋งŒ ๋ฒˆ์„ ํ—›๋˜์ด ๋˜๋ฌป๋Š”๋‹ค.

๊ทธ๋ž˜์„œ ํ‘œ์ค€ ํ•ด๋ฒ•์€ ์šด์˜์ฒด์ œ์— ๊ฐ์‹œ๋ฅผ ๋งก๊ธฐ๋Š” ๊ฒƒ์ด๋‹ค.

  • non-blocking ์†Œ์ผ“ ์ˆ˜์ฒœ ๊ฐœ๋ฅผ ์ผ์ผ์ด ๋˜๋ฌป๋Š” ๋Œ€์‹ ,
  • epoll(๋ฆฌ๋ˆ…์Šค)ยทkqueue(BSDยทmacOS)์— โ€œ์ค€๋น„๋œ ๊ฒƒ๋งŒ ์•Œ๋ ค๋‹ฌ๋ผโ€๊ณ  ๋“ฑ๋กํ•œ๋‹ค
  • ์ค€๋น„๋œ ์ž์›๋งŒ ๊ณจ๋ผ ๊ทธ๋•Œ ํ•œ ๋ฒˆ ์ฝ๋Š”๋‹ค

์ด๋Ÿฌ๋ฉด ๋˜๋ฌป๋Š” ๋‚ญ๋น„๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค. event loop๊ฐ€ ์ˆ˜์ฒœ ์—ฐ๊ฒฐ์„ ํ•œ ์Šค๋ ˆ๋“œ๋กœ ๊ฐ๋‹นํ•˜๋Š” ์›๋ฆฌ๋‹ค. ์ž์„ธํ•œ ๋™์ž‘์€ O์ ˆ์—์„œ ๋‹ค๋ฃฌ๋‹ค.


3. ๊ทธ๋Ÿฐ๋ฐ๋„ ์Šคํ•€์„ ํƒํ•˜๋Š” ์ด์œ  โ€” ์žฌ์šฐ๊ธฐยท๊นจ์šฐ๊ธฐ ์ง€์—ฐ

blocking์€ ๊ณต์งœ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ๊ฐ€ blocking์œผ๋กœ ๋Œ€๊ธฐํ•˜๋ฉด ์ปค๋„์ด ๊ทธ ์Šค๋ ˆ๋“œ๋ฅผ ์žฌ์šฐ๊ณ (sleep), ์ž์›์ด ์ค€๋น„๋˜๋ฉด ๋‹ค์‹œ ๊นจ์šด๋‹ค(wake).

์ด ๊ณผ์ •์—๋Š” ๋น„์šฉ์ด ๋ถ™๋Š”๋‹ค.

  • ์žฌ์šฐ๊ณ  ๊นจ์šฐ๋Š” ๋‘ ๋ฒˆ์˜ context switch
  • ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๋‹ค์‹œ ์ด ์Šค๋ ˆ๋“œ๋ฅผ ๊ณจ๋ผ ์‹คํ–‰ํ•˜๊ธฐ๊นŒ์ง€์˜ ์ง€์—ฐ
  • ๊นจ์–ด๋‚œ ๋’ค ์‹์–ด๋ฒ„๋ฆฐ CPU ์บ์‹œ๋ฅผ ๋‹ค์‹œ ์ฑ„์šฐ๋Š” ๋น„์šฉ

ํ•ฉ์น˜๋ฉด ๋Œ€๋žต ์ˆ˜ ๋งˆ์ดํฌ๋กœ์ดˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋žจ์—๋Š” ๋ฌด์‹œํ•  ๊ฐ’์ด์ง€๋งŒ ๋งˆ์ดํฌ๋กœ์ดˆ ๋‹จ์œ„๊ฐ€ ์†์ต์„ ๊ฐ€๋ฅด๋Š” ๋„๋ฉ”์ธ์—์„œ๋Š” ์ด ์ง€์—ฐ์ด ์•„๊น๋‹ค. ๊ทธ๋ž˜์„œ ์Šค๋ ˆ๋“œ๋ฅผ ์•„์˜ˆ ์žฌ์šฐ์ง€ ์•Š๊ณ  non-blocking ๊ณ„์† ํ™•์ธํ•˜๋ฉฐ ๊นจ์–ด ์žˆ๊ฒŒ ๋‘”๋‹ค.

ํŠธ๋ ˆ์ด๋“œ์˜คํ”„

busy polling์€ ์ฝ”์–ด ํ•˜๋‚˜๋ฅผ ํ†ต์งธ๋กœ ํƒœ์›Œ ๊ทธ ์ˆ˜ ๋งˆ์ดํฌ๋กœ์ดˆ์˜ ๊นจ์šฐ๊ธฐ ์ง€์—ฐ์„ ์—†์•ค๋‹ค. โ€œCPU๋ฅผ ์“ด๋‹คโ€๊ฐ€ ๋‹จ์ ์ด ์•„๋‹ˆ๋ผ, ์ง€์—ฐ์„ ์‚ฌ๊ธฐ ์œ„ํ•ด ์น˜๋ฅด๋Š” ๊ฐ’์ด๋‹ค.


4. ์‹ค์ œ๋กœ ์Šคํ•€์„ ์“ฐ๋Š” ๊ณณ

  • ๊ณ ๋นˆ๋„ ํŠธ๋ ˆ์ด๋”ฉ(HFT): ์ฃผ๋ฌธ ์ง€์—ฐ ๋‚˜๋…ธยท๋งˆ์ดํฌ๋กœ์ดˆ๊ฐ€ ์ˆ˜์ต์œผ๋กœ ์ง๊ฒฐ๋œ๋‹ค. ์Šค๋ ˆ๋“œ๋ฅผ ์žฌ์šฐ์ง€ ์•Š๊ณ  ์Šคํ•€ํ•˜๋ฉฐ ์‹œ์„ธ๋ฅผ ๋ฐ›๋Š”๋‹ค
  • DPDK1 ๊ฐ™์€ ์ปค๋„ ์šฐํšŒ: ์ปค๋„ ์ธํ„ฐ๋ŸฝํŠธ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  non-blocking์œผ๋กœ NIC์˜ ๋ง ๋ฒ„ํผ๋ฅผ ์‰ฌ์ง€ ์•Š๊ณ  ํด๋งํ•ด ํŒจํ‚ท์„ ๊ฐ€์ ธ์˜จ๋‹ค
  • ๋ฆฌ๋ˆ…์Šค SO_BUSY_POLL ยท NAPI busy poll: ์†Œ์ผ“ ์ˆ˜์‹  ๊ฒฝ๋กœ์˜ ์ง€์—ฐ์„ ์ค„์ด๋ ค ์ปค๋„์ด ์ž ๊น ์Šคํ•€ํ•˜๋„๋ก ์ผœ๋Š” ์˜ต์…˜์ด๋‹ค
  • spinlock: ๋ฝ์„ ๊ธฐ๋‹ค๋ฆด ๋•Œ์˜ ์Šคํ•€. ๋‹ค์Œ ์ ˆ์—์„œ ๋”ฐ๋กœ ๋ณธ๋‹ค

5. spinlock โ€” ์งง์€ ์ž„๊ณ„ ๊ตฌ์—ญ์˜ ์Šคํ•€

๋ฝ๋„ ๊ฐ™์€ ์„ ํƒ์— ๋†“์ธ๋‹ค. ๋ฝ์„ ๋ชป ์žก์•˜์„ ๋•Œ ๋ฌด์—‡์„ ํ• ์ง€๊ฐ€ ๊ฐˆ๋ฆฐ๋‹ค.

  • ๋ฎคํ…์Šค(blocking): ์Šค๋ ˆ๋“œ๋ฅผ ์žฌ์šฐ๊ณ , ๋ฝ์ด ํ’€๋ฆฌ๋ฉด ๊นจ์šด๋‹ค
  • spinlock(spinning): ์žฌ์šฐ์ง€ ์•Š๊ณ  ์งง๊ฒŒ ๊ณ„์† ์‹œ๋„ํ•œ๋‹ค
while (!try_lock(&lock)) {
    /* ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ์•Š๊ณ  ๋‹ค์‹œ ์‹œ๋„ โ€” spin */
}

์ž„๊ณ„ ๊ตฌ์—ญ์ด ์•„์ฃผ ์งง์œผ๋ฉด ์žฌ์› ๋‹ค ๊นจ์šฐ๋Š” ๋น„์šฉ์ด ์ž„๊ณ„ ๊ตฌ์—ญ ์ž์ฒด๋ณด๋‹ค ์ปค์ง„๋‹ค. ์ด๋Ÿด ๋•Œ๋Š” ์ž ๊น ์Šคํ•€ํ•˜๋ฉฐ ๊ธฐ๋‹ค๋ฆฌ๋Š” ํŽธ์ด ์‹ธ๋‹ค. ๋ฐ˜๋Œ€๋กœ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ตฌ์—ญ์„ ์Šคํ•€์œผ๋กœ ๊ธฐ๋‹ค๋ฆฌ๋ฉด ์ฝ”์–ด๋ฅผ ํ—›๋Œ๋ฆฌ๋ฏ€๋กœ ์†ํ•ด๋‹ค. ๊ทธ๋ž˜์„œ spinlock์€ โ€œ์ž„๊ณ„ ๊ตฌ์—ญ์ด ์งง๊ณ  ์žฌ์šฐ๊ธฐ ๋น„์šฉ์ด ์•„๊นŒ์šธ ๋•Œโ€๋งŒ ์“ด๋‹ค. ๋ฉ€ํ‹ฐ์ฝ”์–ด ์ปค๋„ ๋‚ด๋ถ€๊ฐ€ ๋Œ€ํ‘œ์ ์ธ ๋ฌด๋Œ€๋‹ค.


6. ์„ ํƒ ๊ธฐ์ค€ โ€” blocking vs spin

blocking (์žฌ์šฐ๊ธฐ)spin (busy polling)
๋Œ€๊ธฐ ์ค‘ CPU๋ฐ˜๋‚ฉ๊ณ„์† ์ ์œ 
๊นจ์šฐ๊ธฐ ์ง€์—ฐ์žˆ์Œ(์ˆ˜ ๋งˆ์ดํฌ๋กœ์ดˆ)๊ฑฐ์˜ ์—†์Œ
์ ํ•ฉํ•œ ์ƒํ™ฉ๋Œ€๊ธฐ๊ฐ€ ๊ธธ๊ฑฐ๋‚˜ ์–ธ์ œ ๋๋‚ ์ง€ ๋ชจ๋ฆ„๋Œ€๊ธฐ๊ฐ€ ์งง๊ณ  ์ง€์—ฐ์ด ์†์ต์„ ๊ฐ€๋ฆ„
์˜ˆ์ผ๋ฐ˜ ์„œ๋ฒ„ I/O, ๊ธด ๋ฝHFT, ์ปค๋„ ์šฐํšŒ, ์งง์€ ๋ฝ

blocking์ด ๊ธฐ๋ณธ๊ฐ’์ด๊ณ  spin์€ ์˜ˆ์™ธ๋‹ค. โ€œ์–ธ์ œ ๊นจ์–ด๋‚ ์ง€ ๋ชจ๋ฅด๋Š” ๊ธด ๋Œ€๊ธฐโ€์—๋Š” ์žฌ์šฐ๋Š” ํŽธ์ด ๋งž๊ณ  โ€œ๊ณง ์ค€๋น„๋  ์งง์€ ๋Œ€๊ธฐ + ๋งˆ์ดํฌ๋กœ์ดˆ๊ฐ€ ์•„๊นŒ์šด ์ƒํ™ฉโ€์—์„œ๋งŒ ์Šคํ•€์ด ์ด๊ธด๋‹ค.

Footnotes

  1. ๋ฆฌ๋ˆ…์Šค ๋“ฑ ๋ฒ”์šฉ ์šด์˜์ฒด์ œ์˜ ์ปค๋„์„ ์šฐํšŒํ•˜์—ฌ(Kernel Bypass), ์‚ฌ์šฉ์ž ๊ณต๊ฐ„(User Space)์—์„œ ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค ์นด๋“œ(NIC)์™€ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๊ฒŒ ํ•ด์ฃผ๋Š” ์˜คํ”ˆ์†Œ์Šค ๊ณ ์„ฑ๋Šฅ ํŒจํ‚ท ์ฒ˜๋ฆฌ ํ”„๋ ˆ์ž„์›Œํฌ โ†ฉ