Danh mục:
Bài viết này tổng hợp các phương pháp cải thiện hiệu năng cho API server xử lý embedding dựa trên FastAPI + sentence-transformers, bằng cách sử dụng song song đa tiến trình và worker bất đồng bộ (asynchronous worker).
Tổng quan kiến trúc hệ thống
Server được xây dựng như sau:
- Dùng FastAPI chạy trên uvicorn (ASGI)
- Khi nhận
/embed, nó: - Đưa dữ liệu vào asyncio.Queue
- Worker nền (background EmbeddingWorker) lấy task
- Tạo vector embedding bằng sentence-transformers
- Lưu vector vào Qdrant (vector DB)
Các thành phần chính
-
uvicorn --workers=N - Khởi tạo N tiến trình OS level
- Mỗi tiến trình chứa:
+ App FastAPI
+ Queue riêng
+ Một instance embedding_worker
Kernel sẽ phân phối các request đều đặn giữa các tiến trình này (round-robin).
-
EmbeddingWorker(worker_count=M)
Bên trong mỗi tiến trình FastAPI:
- Tạo M tasks bất đồng bộ bằng asyncio.create_task()
- Mỗi task lắng nghe và lấy job từ queue để xử lý song song
-
sentence-transformers model - Dùng PyTorch để tính embedding vector
- Thiết lập device có thể là:
+ cpu
+ cuda (GPU NVIDIA)
+ mps (GPU Apple Silicon)
Mô tả song song hóa (Parallelism)
- Process level: nhờ
uvicorn --workers=N, tận dụng đa nhân CPU - Coroutine level: mỗi tiến trình có M worker bất đồng bộ
- Queue: mỗi tiến trình có queue riêng → xử lý nhiều embedding song song
Ví dụ thực tế:
uvicorn main:app --workers 4
Trong khi đó trong code:
worker_count = 10
→ Có tổng 4 × 10 = 40 worker xử lý embedding chạy song song.
Các câu hỏi quan trọng về hiệu năng
Q1. Tăng số --workers có giúp nhanh hơn không?
✔️ Có — thường tăng số tiến trình giúp xử lý nhiều request đồng thời hơn.
Tuy nhiên, cũng cần cân nhắc:
- CPU core thực tế
- Tài nguyên RAM/GPU
Q2. Tăng worker_count trong EmbeddingWorker thì sao?
✔️ Có thể tăng throughput khi I/O chậm hoặc GPU chưa bị saturate.
Nhưng quá nhiều worker sẽ gây:
- tranh chấp tài nguyên
- overhead scheduler
→ Cần tìm điểm cân bằng phù hợp.
Q3. Queue size nhỏ có vấn đề gì?
✔️ Nếu queue quá nhỏ khi request tăng mạnh:
- queue nhanh đầy
- server trả lỗi 429 Too Many Requests
- request bị delay
Giải pháp:
- Tăng queue size vừa đủ
- Hoặc thay queue nội bộ bằng queue ngoài như Redis
Q4. Phải làm sao nếu muốn chia sẻ queue giữa nhiều tiến trình?
→ Câu trả lời:
Dùng queue ngoại vi như Redis hoặc RabbitMQ.
Ví dụ Redis:
import redis r = redis.Redis() # enqueue r.rpush("embedding_tasks", json.dumps({"text": "hello"})) # dequeue (blocking pop) task = json.loads(r.blpop("embedding_tasks")[1])
Hướng dẫn thử nghiệm hiệu năng
Bạn có thể thực hiện các thay đổi sau để đo hiệu năng:
📊 Thử với số uvicorn --workers khác nhau
- workers = 1, 2, 4, 8
- Đo thời gian xử lý request
- So sánh throughput & latency
🧪 Check queue trong logs
- Theo dõi queue size bằng htop hoặc logs custom
📈 Giám sát CPU/MPS
- Trên macOS: Activity Monitor / powermetrics
- Trên Linux: htop, nvidia-smi
Kết luận — Tóm tắt các chiến lược tuning
--workers=N: Tăng xử lý song song ở tiến trình
worker_count: Tăng concurrency trong mỗi tiến trình
Queue sizing: Cân bằng backlog & xử lý request
Shared queue (Redis): Hợp nhất task giữa nhiều tiến trình
Các chiến lược tuning được gợi ý
- Đặt --workers phù hợp với số core CPU
- Điều chỉnh worker_count dựa trên I/O hoặc GPU
- Đặt queue size đủ lớn để giảm lỗi queue full
- Ưu tiên sử dụng shared queue (Redis) khi cần scale-out
Nguồn bài viết - Dịch từ ryukato.github.io