๐ English Abstract
This technical deep-dive addresses severe event loop latency and starvation issues in Python real-time telemetry ingestion pipelines combining asyncio and multiprocessing. It examines how IPC serialization (pickle) synchronously blocks the asynchronous event loop thread during high-throughput metric handling. Furthermore, it presents concrete troubleshooting steps, architectural optimizations using shared memory, zero-copy techniques, fast binary serialization formats, and thread-executor offloading to achieve sub-millisecond telemetry ingestion latency.
๋๊ท๋ชจ ์์ง ํ์ดํ๋ผ์ธ(Ingestion Pipeline)์ ๊ตฌ์ถํ ๋ ํ์ด์ฌ(Python)์ ๋ฐ์ด๋ ์ํ๊ณ์ ์์ฐ์ฑ ๋๋ถ์ ์์ฃผ ์ ํ๋๋ ์ธ์ด์ ๋๋ค. ํนํ ๋๋์ ๋คํธ์ํฌ I/O ๋์์ฑ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด asyncio๋ฅผ ํ์ฉํ๊ณ , CPython์ GIL(Global Interpreter Lock) ํ๊ณ๋ฅผ ๊ทน๋ณตํ์ฌ CPU ์ฐ์ฐ(๋ฐ์ดํฐ ๊ฒ์ฆ, ํฉ ํ์ฑ ๋ฑ)์ ๋ถ์ฐํ๊ธฐ ์ํด multiprocessing ๋ชจ๋์ ํผ์ฉํ๋ ์ํคํ ์ฒ๊ฐ ์์ฃผ ์ฌ์ฉ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ ์ด๋น ์๋ง ๊ฑด ์ด์์ ํ
๋ ๋ฉํธ๋ฆฌ(Telemetry) ๋ฐ์ดํฐ๊ฐ ์ ์
๋๋ ์ค์๊ฐ ํ์ดํ๋ผ์ธ์ ์ด์ํ๋ค ๋ณด๋ฉด, ๋คํธ์ํฌ ํ์์์์ด ๋ฐ์ํ๊ฑฐ๋ asyncio ์ด๋ฒคํธ ๋ฃจํ(Event Loop)์ ์ง์ฐ ์๊ฐ(Latency)์ด ๊ธฐํ๊ธ์์ ์ผ๋ก ์ฆ๊ฐํ๋ ํ์์ ๋ชฉ๊ฒฉํ๊ฒ ๋ฉ๋๋ค. ์ด ๋ฌธ์ ์ ๊ทผ๋ณธ ์์ธ์ ํ๋ก์ธ์ค ๊ฐ ํต์ (IPC, Inter-Process Communication) ๊ณผ์ ์์ ๋ฐ์ํ๋ ๋ฐ์ดํฐ ์ง๋ ฌํ(Serialization) ๋ณ๋ชฉ์ ์์ต๋๋ค. ๋ณธ ๊ธ์์๋ ์ด ํธ๋ฌ๋ธ์ํ
๊ณผ์ ๊ณผ ์ด๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํ ์ํคํ
์ฒ ์ต์ ํ ๋ฐฉ์์ ์์ธํ ๋ค๋ฃน๋๋ค.
1. ๋ฌธ์ ์ ๋ณธ์ง: asyncio ์ด๋ฒคํธ ๋ฃจํ์ multiprocessing IPC์ ์ถฉ๋
asyncio๋ ๋จ์ผ ์ค๋ ๋ ๊ธฐ๋ฐ์ ๋น๋๊ธฐ I/O ์ด๋ฒคํธ ๋ฃจํ(Event Loop)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ํฉ๋๋ค. ํ์คํฌ(Task)๊ฐ I/O ์์
์ ๊ธฐ๋ค๋ฆฌ๋ ๋์ await ํค์๋๋ฅผ ํตํด ์ ์ด๊ถ์ ์ด๋ฒคํธ ๋ฃจํ์ ์๋(Yield)ํจ์ผ๋ก์จ ํ๊ธฐ์ ์ธ ๋์์ฑ์ ํ๋ณดํฉ๋๋ค. ๋ฐ๋ฉด ํ์ด์ฌ์ multiprocessing ๋ชจ๋์ ํ๋ก์ธ์ค ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๊ธฐ ์ํด ๋ด๋ถ์ ์ผ๋ก pickle ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๊ฐ์ฒด๋ฅผ ๋ฐ์ด๋๋ฆฌ๋ก ์ง๋ ฌํํ๊ณ , Pipe๋ Queue๋ฅผ ํตํด ์ ์กํฉ๋๋ค.
์ด๋ฒคํธ ๋ฃจํ ์ฐจ๋จ(Event Loop Blocking)์ ๋ฉ์ปค๋์ฆ
์ค์๊ฐ ํ
๋ ๋ฉํธ๋ฆฌ ์์ง๊ธฐ์์ ๋ฉ์ธ ํ๋ก์ธ์ค์ asyncio ๋ฃจํ๊ฐ ๋คํธ์ํฌ ์์ผ์ผ๋ก๋ถํฐ ๋ณต์กํ JSON ๋๋ ๋์
๋๋ฆฌ ํํ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ ํ, ์ด๋ฅผ ์์
์ ํ๋ก์ธ์ค(Worker Process)์ ์ ๋ฌํ๊ธฐ ์ํด multiprocessing.Queue.put()์ ํธ์ถํ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
- ๋๊ธฐ์ ์ง๋ ฌํ ์ฐ์ฐ:
multiprocessing.Queue.put()๋ด๋ถ์์ ์คํ๋๋pickle.dumps()๊ณผ์ ์ ์์ ํ CPU ๋ฐ์ด๋(CPU-bound) ์์ ์ด๋ฉฐ ๋๊ธฐ(Blocking) ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค. - ์ด๋ฒคํธ ๋ฃจํ ๋ฉ์ถค(Starvation): ์ง๋ ฌํํ ๋ฐ์ดํฐ์ ํฌ๊ธฐ๊ฐ ํฌ๊ฑฐ๋ ๊ฐ์๊ฐ ๋ง์์ง๋ฉด
pickle.dumps()์ฐ์ฐ์ด ์๋ฃ๋ ๋๊น์ง ์ด๋ฒคํธ ๋ฃจํ ์ค๋ ๋ ์ ์ฒด๊ฐ ๋ธ๋กํน๋ฉ๋๋ค. - ๋คํธ์ํฌ ์ง์ฐ ํ๊ธ: ์ด๋ฒคํธ ๋ฃจํ๊ฐ ๋ฉ์ถ๋ฉด ์๋ก์ด ์์ผ ์ ์ ์ฒ๋ฆฌ, ํ/ํ(Ping/Pong) ํฌ์ค์ฒดํฌ, ์ฝ๋ฃจํด ์ค์ผ์ค๋ง์ด ๋ชจ๋ ์ค๋จ๋์ด ํด๋ผ์ด์ธํธ ์ธก์์ ์ปค๋ฅ์ ํ์์์(Connection Timeout)์ด ๋ฐ์ํฉ๋๋ค.
2. ํ์ ์ง๋จ ๋ฐ ํธ๋ฌ๋ธ์ํ ๋จ๊ณ๋ณ ์ ๊ทผ
์์คํ ์ฑ๋ฅ์ด ์ ํ๋์์ ๋ ๋ณ๋ชฉ์ ์์ธ์ด ๋คํธ์ํฌ์ธ์ง, CPU ์ฐ์ฐ์ธ์ง, ์๋๋ฉด IPC ์ง๋ ฌํ์ธ์ง๋ฅผ ์ ํํ ํ๋ณํ๋ ๊ตฌ์ฒด์ ์ธ ์ง๋จ ์ ์ฐจ์ ๋๋ค.
๋จ๊ณ 1: asyncio ์ด๋ฒคํธ ๋ฃจํ ์ง์ฐ ๋ชจ๋ํฐ๋ง
๊ฐ์ฅ ๋จผ์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ํน์ ํ์คํฌ์ ์ํด ์ผ๋ง๋ ์ค๋ซ๋์ ๋ฉ์ถฐ ์๋์ง๋ฅผ ์ธก์ ํด์ผ ํฉ๋๋ค. ํ์ด์ฌ ๊ธฐ๋ณธ ์ ๊ณต ๋๋ฒ๊ทธ ๋ชจ๋๋ ์ปค์คํ ๋ฃจํ ๋ชจ๋ํฐ๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
import asyncio
import time
import logging
# asyncio ๋๋ฒ๊ทธ ๋ชจ๋ ํ์ฑํ (์คํ์ 100ms ์ด์ ๊ฑธ๋ฆฌ๋ ๋ธ๋กํน ํ์คํฌ ๋ก๊น
)
loop = asyncio.get_event_loop()
loop.set_debug(True)
loop.slow_callback_duration = 0.1 # 100ms ๊ธฐ์ค
async def monitor_event_loop_drift():
"""์ด๋ฒคํธ ๋ฃจํ์ ์ค์ ์ง์ฐ ์๊ฐ์ ์ง์์ ์ผ๋ก ์ธก์ ํ๋ ์ฝ๋ฃจํด"""
while True:
before = time.monotonic()
await asyncio.sleep(1)
after = time.monotonic()
drift = (after - before) - 1.0
if drift > 0.05: # 50ms ์ด์์ ์ง์ฐ์ด ๋ฐ์ํ ๊ฒฝ์ฐ ๊ฒฝ๊ณ
logging.warning(f"[Event Loop Lag] Drift: {drift * 1000:.2f}ms")
๋จ๊ณ 2: IPC ์ง๋ ฌํ ํ๋กํ์ผ๋ง (Pickle Overhead Quantification)
cProfile ๋๋ yappi ํ๋กํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํ์ฌ ์คํ ์๊ฐ์ ๋ถ์ํ๋ฉด, multiprocessing/queues.py ๋ด๋ถ์ _feed ๋ฉ์๋์ pickle.dumps์์ ์ ์ฒด CPU ์๊ฐ์ 40~60% ์ด์์ ์๋นํ๋ ์ฃ์ง ์ผ์ด์ค๋ฅผ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
| ์์ง ๋ฐ์ดํฐ ์ ํ | ๊ฑด๋น ํ๊ท ๋ฐ์ดํฐ ํฌ๊ธฐ | ์ด๋น ์ฒ๋ฆฌ ๊ฑด์ (TPS) | Pickle ์ง๋ ฌํ ์ ์ด๋ฒคํธ ๋ฃจํ ์ง์ฐ์๊ฐ |
|---|---|---|---|
| ๋จ์ ๋ฉํธ๋ฆญ (Integer/Float) | 1 KB ์ดํ | 50,000 TPS | 12ms ~ 25ms (์ํธ) |
| ์ค์ฒฉ ๊ตฌ์กฐ ํ ๋ ๋ฉํธ๋ฆฌ (Dict/List) | 50 KB | 10,000 TPS | 180ms ~ 450ms (์ฌ๊ฐํ ๋ณ๋ชฉ) |
| ๊ณ ๋ฐ๋ ์ผ์ ๋ก๊ทธ (String/Array) | 500 KB | 2,000 TPS | 1,200ms ์ด์ (์ด๋ฒคํธ ๋ฃจํ ๋ง๋น) |
3. IPC ๋ณ๋ชฉ ํด๊ฒฐ์ ์ํ 4๊ฐ์ง ์ํคํ ์ฒ ์ต์ ํ ์ ๋ต
์ง๋ ฌํ ๋ณ๋ชฉ์ ๊ทน๋ณตํ๊ณ ์ค์๊ฐ ์์ง ์์คํ ์ ์ฒ๋ฆฌ๋(Throughput)์ ๊ทน๋ํํ๊ธฐ ์ํ ๋จ๊ณ๋ณ ์ ์ฉ ๊ธฐ์ ์ ๋๋ค.
์ ๋ต 1: loop.run_in_executor๋ฅผ ํตํ ์ง๋ ฌํ ์์ ๋ณ๋ ์ค๋ ๋ ์์
๊ฐ์ฅ ์์ฝ๊ฒ ์ ์ฉ ๊ฐ๋ฅํ ์์ ๋ฐฉํธ์ multiprocessing.Queue.put()๊ณผ ๊ฐ์ blocking ์์
์ ์ด๋ฒคํธ ๋ฃจํ์ ๋ฉ์ธ ์ค๋ ๋๊ฐ ์๋ ThreadPoolExecutor๋ก ์คํ๋ก๋ฉ(Offloading)ํ๋ ๊ฒ์
๋๋ค.
import asyncio
from concurrent.futures import ThreadPoolExecutor
import multiprocessing as mp
executor = ThreadPoolExecutor(max_workers=8)
mp_queue = mp.Queue()
async def send_telemetry_async(payload):
loop = asyncio.get_running_loop()
# Queue.put(payload) ์์
์ ThreadPool์์ ์คํํ์ฌ ์ด๋ฒคํธ ๋ฃจํ ์ฐจ๋จ์ ๋ฐฉ์งํจ
await loop.run_in_executor(executor, mp_queue.put, payload)
์ฃผ์์ : ์ค๋ ๋ ์์ฑ ๋น์ฉ๊ณผ ์ปจํ ์คํธ ์ค์์นญ(Context Switching) ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ๋ฏ๋ก ๋ฐ์ดํฐ๋์ด ๋งค์ฐ ๋ง์ ๋๋ ๊ทผ๋ณธ์ ์ธ ํด๊ฒฐ์ฑ ์ด ๋์ง ๋ชปํฉ๋๋ค.
์ ๋ต 2: ๊ณ ์ฑ๋ฅ ๋ฐ์ด๋๋ฆฌ ์ง๋ ฌํ ํฌ๋งท ๋์ (MessagePack, Orjson, Protobuf)
ํ์ด์ฌ์ ํ์ค pickle์ ์ ์ฐํ์ง๋ง ์๋๊ฐ ๋ฌด๊ฒ์ต๋๋ค. C ์ธ์ด๋ก ์์ฑ๋ ๋ฐ์ธ๋ฉ(C-extension)์ ์ฌ์ฉํ๋ Orjson์ด๋ MessagePack, Protocol Buffers(Protobuf)๋ก ์ง๋ ฌํ ํฌ๋งท์ ๊ต์ฒดํ๋ฉด CPU ์ ์ ์จ์ ํฌ๊ฒ ๋ฎ์ถ ์ ์์ต๋๋ค.
- Orjson: ํ์ด์ฌ ์ต์์ JSON ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, Rust ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋์ด ๋น ๋ฅธ
bytes๋ณํ์ด ๊ฐ๋ฅํฉ๋๋ค. - MessagePack: JSON ๊ตฌ์กฐ๋ฅผ ์ ์งํ๋ฉด์ ๋ฐ์ด๋๋ฆฌ ํํ๋ก ์ถ์ํ์ฌ ์ง๋ ฌํ ์๋์ ์ ์ก ํฌ๊ธฐ๋ฅผ ๋์์ ์ค์ ๋๋ค.
์ ๋ต 3: ๊ณต์ ๋ฉ๋ชจ๋ฆฌ(SharedMemory) ๊ธฐ๋ฐ ์ ๋ก์นดํผ(Zero-Copy) ์ํคํ ์ฒ ๋์
๋์ฉ๋ ํ ๋ ๋ฉํธ๋ฆฌ ์์ง์ ๊ฐ์ฅ ์ฐ์ํ ํด๊ฒฐ์ฑ ์ ๋ฐ์ดํฐ๋ฅผ IPC ํ์ดํ๋ ํ๋ก ์ง์ ๋ณต์ฌ ์ ์กํ์ง ์๊ณ , Python 3.8+ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ(multiprocessing.shared_memory) ๊ณต๊ฐ์ ์ด ๋ค ์์ ์ ํ๋ก์ธ์ค์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ๋ฐ ์ด๊ฒฉ๊ฑฐ๋ฆฌ(Offset/Size)๋ง ์ ๋ฌํ๋ ์ ๋ก์นดํผ ๊ธฐ๋ฒ์ ๋๋ค.
from multiprocessing import shared_memory
import numpy as np
# 1. ๋ฉ์ธ ํ๋ก์ธ์ค: ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น ๋ฐ ๋ฐ์ดํฐ ๊ธฐ๋ก
shm = shared_memory.SharedMemory(create=True, size=1024 * 1024) # 1MB ๊ณต๊ฐ
buffer = shm.buf
# ์ง๋ ฌํ๋ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ์ ์ง์ ๋ณต์ฌ
raw_bytes = b"Telemetry High Velocity Packet Data..."
buffer[:len(raw_bytes)] = raw_bytes
# 2. Worker ํ๋ก์ธ์ค์๋ ๋ฉํ๋ฐ์ดํฐ(๋ฉ๋ชจ๋ฆฌ ์ด๋ฆ๊ณผ ๊ธธ์ด)๋ง Queue๋ก ์ ๋ฌ (์ง๋ ฌํ ๋น์ฉ ์ต์ํ)
ipc_metadata = {"shm_name": shm.name, "data_size": len(raw_bytes)}
# Queue์๋ ์์ฃผ ์๊ณ ๊ฒฝ๋ํ๋ Dict๋ง ์ ๋ฌ๋๋ฏ๋ก ์ด๋ฒคํธ ๋ฃจํ ์ง์ฐ์ด 1ms ์ดํ๋ก ์ ์ง๋จ
์ ๋ต 4: ๋ง์ดํฌ๋ก ๋ฐฐ์น(Micro-batching) ๋ฐ๋ง ๋ฒํผ(Ring Buffer) ํจํด
๋จ์ผ ๋ฐ์ดํฐ ๋จ์๋ก IPC๋ฅผ ์ํํ๋ ๋์ , asyncio ์ธก์์ ๋ฐ์ดํฐ๋ฅผ ์ํ ๋ฉ๋ชจ๋ฆฌ ๋ฒํผ์ ๋ชจ์ ํ ์ผ์ ์๊ฐInterval(์: 10ms)์ด๋ ๊ฐ์Threshold(์: 500๊ฑด)์ ๋๋ฌํ์ ๋ ๋จ 1ํ์ IPC ํธ์ถ๋ก ์ ์กํ๋ **๋ฐฐ์น ๋ฌถ์ ์ฒ๋ฆฌ(Micro-Batching)**๋ฅผ ์ ์ฉํฉ๋๋ค. ์ด๋ก ์ธํด ์ด๋น ๋ฐ์ํ๋ ์ง๋ ฌํ ํ์ ์์ฒด๊ฐ 1/500 ์ดํ๋ก ๊ฐ์ํฉ๋๋ค.
4. ์ค๋ฌด ์ด์ ์ ์ฃ์ง ์ผ์ด์ค ๋ฐ ๋ถ์์ฉ(Pitfalls) ๊ด๋ฆฌ
์ ์ต์ ํ ์ ๋ต์ ํ์ ์ด์ ํ๊ฒฝ์ ๋์ ํ ๋ ๋ฐ๋์ ๋๋นํด์ผ ํ๋ ๋ฌธ์ ์ ๋ค๊ณผ ๊ทธ ๋์ฒ ๋ฐฉ์์ ๋๋ค.
1) SharedMemory ๋ฉ๋ชจ๋ฆฌ ๋์(Memory Leak)์ ํ๋ก์ธ์ค ์ถฉ๋
SharedMemory๋ OS ์ฐจ์์ RAM ์์์ ์ง์ ์ ์ ํฉ๋๋ค. ์์
์ ๋ง์น ์์
์ ํ๋ก์ธ์ค์์ shm.close()๋ฅผ ์ํํ๊ณ , ์ต์ข
์ ์ผ๋ก ๋ฆฌ์์ค๋ฅผ ํด์ ํ๋ ํ๋ก์ธ์ค์์ shm.unlink()๋ฅผ ๋ช
์์ ์ผ๋ก ํธ์ถํ์ง ์์ผ๋ฉด, ์์ธ(Exception) ๋ฐ์ ์ ๋น๋์ค ๋ฉ๋ชจ๋ฆฌ๋ ์์คํ
RAM์ด ๋ฐ๋ฅ๋๋ ๊ณ ์ง์ ์ธ ๋์ ํ์์ด ์ผ์ด๋ฉ๋๋ค.
ํด๊ฒฐ์ฑ
: try...finally ๋ธ๋ก์ ๋ณด์ฅํ๊ฑฐ๋ contextlib.contextmanager๋ฅผ ํ์ฉํด ๋ฆฌ์์ค ํด์ ๋ฅผ ๊ฐ์ ํด์ผ ํฉ๋๋ค.
2) ๋ฐฐ์ธํ๋ ์ (Backpressure) ๋ฏธ๋น๋ก ์ธํ Queue Full ๋ฐ๋๋ฝ
์์
์ ํ๋ก์ธ์ค์ CPU ์ฒ๋ฆฌ ์๋๋ณด๋ค asyncio์ ๋ฐ์ดํฐ ์์ง ์๋๊ฐ ์๋ฑํ ๋น ๋ฅด๋ฉด multiprocessing.Queue๊ฐ ๋ฌดํ์ ํฝ์ฐฝํ์ฌ OOM(Out of Memory)์ด ๋ฐ์ํฉ๋๋ค. ๋ฐ๋๋ก Queue(maxsize=1000)๋ก ํฌ๊ธฐ๋ฅผ ์ ํํด ๋๋ฉด Queue.put()์ด ๋ฌดํ ๋ธ๋กํน๋์ด ์ด๋ฒคํธ ๋ฃจํ๊ฐ ์์ ํ ๋ฉ์ถ๊ฒ ๋ฉ๋๋ค.
ํด๊ฒฐ์ฑ
: non-blocking ๋ฐฉ์์ธ Queue.put_nowait()์ ์คํํ๊ณ , queue.Full ์์ธ ๋ฐ์ ์ ์์ง ์ธก์์ ์์ฒญ์ ๊ฑฐ์ (Drop)ํ๊ฑฐ๋ ํด๋ผ์ด์ธํธ์๊ฒ Throttling ์๋ต์ ๋ณด๋ด๋ **๋ฐฐํ๋ ์
์ ์ด ํ๋ฆ(Backpressure Control)**์ ๊ตฌํํด์ผ ํฉ๋๋ค.
5. ๊ฒฐ๋ก ๋ฐ ์์ฝ
Python์ asyncio์ multiprocessing์ ๊ณ ์ฑ๋ฅ ํ
๋ ๋ฉํธ๋ฆฌ ํ์ดํ๋ผ์ธ์ ํผ์ฉํ ๋ ๋ฐ์ํ๋ ์ด๋ฒคํธ ๋ฃจํ ์ง์ฐ์ ํต์ฌ ๋ฒ์ธ์ ๋๊ธฐ์ IPC ์ง๋ ฌํ(Pickle) ์ฐ์ฐ์
๋๋ค.
์์ ์ ์ธ ์ค์๊ฐ ํ์ดํ๋ผ์ธ ๊ตฌ์ถ์ ์ํด ๋ค์ 3๊ฐ์ง ํต์ฌ ์์๋ฅผ ๋ฐ๋์ ๊ธฐ์ตํ๊ณ ์ ์ฉํด์ผ ํฉ๋๋ค:
- ์ง๋ ฌํ ๋น์ฉ ์ธก์ : ์ด๋ฒคํธ ๋ฃจํ ๋๋ฆฌํํธ ๋ชจ๋ํฐ๋ง์ ํตํด
pickle์ ์ํ ์ง์ฐ์ ์์นํํฉ๋๋ค. - ์ ๋ก์นดํผ ๋ฐ ๋ฐ์ด๋๋ฆฌ ์ง๋ ฌํ ํ์ฉ: ๋์ฉ๋ ๋ฐ์ดํฐ๋
SharedMemory๋ฐOrjson/MessagePack์ ๊ฒฐํฉํ์ฌ ์ง๋ ฌํ ๋ถํ๋ฅผ ๊ทน์ ์ผ๋ก ์ค์ ๋๋ค. - ๋ฐฐ์น ์ ์ก ๋ฐ ๋ฐฑํ๋ ์
์ค๊ณ:
Queue.put์ ๋๊ธฐ ์ฐจ๋จ์ ๋ฐฉ์งํ๊ธฐ ์ํด micro-batching ๊ธฐ๋ฒ๊ณผ non-blocking ๋ฐฑํ๋ ์ ์ ์ฑ ์ ๊ตฌํํฉ๋๋ค.
์ด๋ฌํ ์ํคํ ์ฒ ๊ฐ์ ์ ์ ์ฉํ๋ฉด CPython ํ๊ฒฝ์์๋ ์ด๋ฒคํธ ๋ฃจํ ์ง์ฐ ์๊ฐ์ ๋ฐ๋ฆฌ์ด(ms) ์ดํ ์์ค์ผ๋ก ์์ ์ ์ผ๋ก ์ ์งํ๋ฉด์ ์ด๋น ์๋ง ๊ฑด์ ํ ๋ ๋ฉํธ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๋ณ๋ ฌ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.