Skip to content

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:

text
┌─────────────────────────────┐
│  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:

bash
# 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 200000

If 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:

python
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:

bash
# 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 ​

SymptomLikely BottleneckFix
CPU usage above 90%! on load-test机Locust itselfSwitch to FastHttpUser; add more worker processes
fd limit hit (Too many open files)OS configRaise ulimit -n; use Docker
Target system RPS flat, response time risingTarget system saturatedOptimize the target; do not keep adding users
Target system RPS rising linearly, response time flatLocust has headroomKeep 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 -n to 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 ​

  1. Run a test with HttpUser at 5,000 users; note the CPU warning. Switch to FastHttpUser and compare RPS.
  2. Set ulimit -n 100000; run a 10,000-user test; verify no Too many open files errors.
  3. 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.