Chapter 17 · Performance Tuning and RPS Optimization
Chapter goals: Understand Locust's single-process concurrency bottleneck, master ulimit, FastHttpUser, --processes multi-process techniques to maximize request throughput, and learn to judge "is the bottleneck Locust or the target system?"
17.1 Locust's Concurrency Model and Bottleneck
Locust implements high concurrency based on the gevent coroutine library. Each virtual user runs in a greenlet, with monkey-patching making standard I/O non-blocking.
Single-process bottleneck:
┌─────────────────────────────┐
│ Single Python process │
│ ┌─────────────────────┐ │
│ │ gevent event loop │ ← All users share one event loop
│ │ user1 user2 ...userN│ │
│ └─────────────────────┘ │
│ CPU: uses only one core │
│ Memory: users × per-user RAM │
└─────────────────────────────┘When user count reaches thousands, a single Python process's CPU becomes the bottleneck. The official docs explicitly state: when you see the CPU usage above 90%! warning, Locust itself is overloaded — the measured response times are unreliable.
Key insight
When the load-test tool itself is overloaded, the data is distorted — you are measuring "Locust's processing capacity", not "the target system's performance". Always ensure the load-test machine has headroom.
17.2 Raising the File Descriptor Limit
Each concurrent HTTP connection consumes one file descriptor (fd). Linux/macOS default fd limits are typically only 256 or 1024 — far too low for high-concurrency testing:
# Check current limit
ulimit -n
# Temporarily raise to 100,000 (current shell only)
ulimit -n 100000
# Permanent: edit /etc/security/limits.conf
# * soft nofile 100000
# * hard nofile 100000
# macOS special handling (must run before starting Locust)
sudo launchctl limit maxfiles 100000 200000If fd is insufficient, you will see Too many open files errors — one of the most common environment issues in load testing.
17.3 Using FastHttpUser to Reduce CPU Overhead
The default HttpUser uses the requests library underneath — feature-rich but CPU-heavy. Locust ships FastHttpUser, a more efficient HTTP client based on fasthttp:
from locust import HttpUser, task, between
from locust.contrib.fasthttp import FastHttpUser
class StandardUser(HttpUser):
"""Traditional approach — requests underneath"""
wait_time = between(1, 2)
@task
def get_data(self):
self.client.get("/api/data")
class FastUser(FastHttpUser):
"""Efficient approach — fasthttp underneath, significantly lower CPU"""
wait_time = between(1, 2)
host = "https://staging.example.com"
@task
def get_data(self):
self.client.get("/api/data")
@task
def post_data(self):
self.client.post("/api/data", json={"key": "value"})Benchmarks typically show 2–5× higher RPS with FastHttpUser at the same user count, at the cost of losing some requests features (e.g., streaming responses).
17.4 Multi-Process Mode: --processes
For extreme throughput, run multiple Locust processes on one machine:
# Master process (no users, just stats aggregation)
locust -f locustfile.py --master --web-host 127.0.0.1
# Worker processes (each generates traffic)
locust -f locustfile.py --worker --master-host 127.0.0.1 &
locust -f locustfile.py --worker --master-host 127.0.0.1 &
locust -f locustfile.py --worker --master-host 127.0.0.1 &
locust -f locustfile.py --worker --master-host 127.0.0.1 &Or with Docker Compose (see Chapter 11).
17.5 Judging the Bottleneck: Locust vs Target System
| Symptom | Likely Bottleneck | Fix |
|---|---|---|
CPU usage above 90%! on load-test机 | Locust itself | Switch to FastHttpUser; add more worker processes |
fd limit hit (Too many open files) | OS config | Raise ulimit -n; use Docker |
| Target system RPS flat, response time rising | Target system saturated | Optimize the target; do not keep adding users |
| Target system RPS rising linearly, response time flat | Locust has headroom | Keep increasing users to find the breaking point |
17.6 Chapter Summary
- Single-process Locust is limited by one CPU core; watch for
CPU usage above 90%!; - Raise
ulimit -nto 100,000+ for high-concurrency testing; - FastHttpUser gives 2–5× RPS improvement over HttpUser;
- Multi-process mode (
--worker× N) scales beyond single-machine limits; - Always distinguish "Locust is saturated" from "target system is saturated".
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What does the "CPU usage above 90%!" warning mean?
2. How much should you raise ulimit -n for a 5,000-user test?
3. What is the typical RPS improvement when switching from HttpUser to FastHttpUser?
4. How do you distinguish "Locust bottleneck" from "target system bottleneck"?
🛠️ Hands-On Exercises
- Run a test with HttpUser at 5,000 users; note the CPU warning. Switch to FastHttpUser and compare RPS.
- Set
ulimit -n 100000; run a 10,000-user test; verify noToo many open fileserrors. - Write a multi-process setup with 1 master + 4 workers on a single machine; compare total RPS vs single-process.
Proceed to Chapter 18: CI/CD Integration.