ITADN

Performance: Replace inefficient polling in server loop with event-driven shutdown and optimize Health check

#3355Openamadhan882 创建于 2026-02-09
A
amadhan882commented
## Summary The current implementation of the server loop in `text_generation_server/server.py` uses an inefficient polling mechanism to handle shutdown signals. Additionally, the Health check endpoint performs redundant GPU allocations, leading to unnecessary overhead and memory fragmentation. --- ## Root Cause 1. Polling Latency: The server loop relies on a blocking-style polling mechanism: ``` while signal_handler.KEEP_PROCESSING: await asyncio.sleep(0.5) ``` Even after the flag is set to False, the loop must wait for the current sleep(0.5) cycle to complete before exiting. 2. Health Check Overhead: The Health check unnecessarily touches device memory on every probe: ``` if self.model.device.type == "cuda": torch.zeros((2, 2)).cuda() ``` This causes redundant CUDA kernel launches and potential memory churn. --- ## Suggested Fix 1. Event-Driven Shutdown: Replace the boolean flag with asyncio.Event in the SignalHandler and use `await signal_handler.exit_event.wait()` in the main loop. 2. Efficient Health Check: Verify device availability without allocation: async def Health(self, request, context): ``` if self.model.device.type == "cuda": if not torch.cuda.is_available(): raise RuntimeError("GPU device not available") return generate_pb2.HealthResponse() ``` --- ## Environment - OS: Linux (Containerized) - Python: 3.12+ - Backend: Text Generation Inference (TGI) --- ### Information - [x] Docker - [x] The CLI directly ### Tasks - [x] An officially supported command - [ ] My own modifications ## Reproduction Script ``` import asyncio import time async def reproduce_tgi_latency(): keep_processing = True start_time = time.time() # Simulate OS Signal (SIGINT/SIGTERM) arriving at 0.1s async def trigger_signal(): await asyncio.sleep(0.1) nonlocal keep_processing print(f"\n[TEST] Signal Received at {time.time() - start_time:.4f}s") keep_processing = False asyncio.create_task(trigger_signal()) # --- EXACT TGI LOGIC FROM server.py --- print("[*] TGI Server Loop Started (Polling every 0.5s)") while keep_processing: await asyncio.sleep(0.5) end_time = time.time() print(f"[*] Server finally stopped at {end_time - start_time:.4f}s") await reproduce_tgi_latency() ``` --- ## Actual Behavior ``` [TEST] Signal Received at 0.1010s [*] Server finally stopped at 0.5017s [!] Latency: ~400ms delay (Loop blocked by asyncio.sleep) ``` --- ## Expected Behavior The server should respond to signals immediately (< 1ms) using an event-driven approach instead of polling with a sleep timer. --- ## Conclusion This is a performance and correctness issue. Implementing an event-driven shutdown ensures microsecond responsiveness to OS signals and prevents redundant GPU resource usage.
0 条评论