Optimize network interrupt handling with vCPU pinning

更新时间:
复制 MD 格式

In high-concurrency, low-latency network scenarios like caching services, message queues, and web proxies, network interrupt handling can become a system bottleneck as both Hardware Interrupts (Hard IRQs) and Software Interrupts (Soft IRQs) compete with application logic for vCPU resources. Pinning network interrupt handling and application processes to separate vCPU cores significantly reduces context switches, cache pollution, and scheduling delays, improving overall throughput and tail latency. This guide provides a general method for optimizing vCPU pinning on Alibaba Cloud Elastic Compute Service (ECS) instances. For example, testing on a g9i.8xlarge instance with Alibaba Cloud Linux 3 showed that separating Redis worker threads from network interrupts increased Queries Per Second (QPS) by 26%.

Use cases

This optimization is recommended for the following use cases:

  • The application is both CPU-intensive and network I/O-heavy, such as Redis, Kafka, Nginx, or a MySQL Proxy.

  • The application runs on a multi-core Elastic Compute Service (ECS) instance with 16 or more vCPUs.

  • You observe a high percentage of CPU time spent on irq or softirq on the cores where the application process is running. You can check this using mpstat.

This optimization is not recommended for the following scenarios:

  • Small instances with fewer than 8 vCPUs.

  • Mixed deployments with multiple high-load services.

  • Containerized environments, unless CPU resource isolation is configured with tools like the Kubernetes CPU Manager or cgroups.

How it works

Linux network packet processing involves two key stages:

  • Hardware Interrupt (Hard IRQ): The Network Interface Card (NIC) receives a packet and triggers a vCPU interrupt that the driver then handles.

  • Software Interrupt (Soft IRQ): The kernel protocol stack, such as NET_RX, processes the packet in the context of ksoftirqd or NAPI.

Sharing cores with application threads can cause:

  • L1/L2 Cache Pollution

  • Frequent flushing of the vCPU pipeline

  • Increased scheduling latency

Using IRQ affinity and Receive Packet Steering (RPS) confines interrupt handling to a dedicated set of vCPUs, allowing application threads to exclusively use the remaining cores and achieve resource isolation.

RPS is a software mechanism in the Linux kernel that distributes packets received from a single-queue NIC across multiple vCPU cores for parallel processing during the software interrupt stage. This overcomes single-core bottlenecks and improves network throughput.

Configuration steps

Step 1: Plan the vCPU partition

This example uses an instance with 32 vCPUs (vCPU 0–31):

Purpose

vCPU range

Recommended vCPU cores

Application process

vCPUs 0–7

8 or more (adjust based on the number of application threads)

Network Interrupt Handling

vCPUs 8–31

8 or more (more cores are needed for more queues)

Step 2: Bind Hard IRQs to dedicated vCPUs

This example uses the primary network interface eth0, which uses the virtio driver:

# Get the interrupt numbers for the virtio device corresponding to eth0 (e.g., virtio1)
IRQS=$(grep 'virtio1-' /proc/interrupts | cut -d: -f1 | tr -d ' ')

# Calculate the vCPU mask: vCPUs 8–31 correspond to the hexadecimal mask ffffff00
# For a 32-core system (0–31), this mask sets bits 8 through 31 to 1.
for irq in $IRQS; do
    echo ffffff00 > /proc/irq/$irq/smp_affinity
done

Step 3: Enable RPS load balancing

RPS prevents single-core bottlenecks by distributing received packets to multiple vCPUs for software interrupt processing:

# Set the RPS vCPU mask for each RX queue to match the IRQ mask
for rps_file in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
    echo ffffff00 > "$rps_file"
done

Step 4: Bind the application to dedicated vCPUs

# Pin the Redis process to vCPUs 0-7 on startup
taskset -c 0-7 redis-server /etc/redis.conf

Example: Improving Redis Performance

Deploying Redis 6.2 on a g9i.8xlarge (32 vCPU) instance yielded the following results:

Configuration

QPS (GET)

Default (no pinning)

195K

Optimized

246K (+26%)

Test command:

./memtier_benchmark -s $SERVER_IP -p $port -t 8 --test-time=60 -c 10 --ratio=0:1 --pipeline=1 -d 32 --key-maximum=100000

Key observation:

  • mpstat showed that the %irq and %soft values on application cores dropped to nearly zero.

Verify the optimization

mpstat -P ALL 1

Before pinning:

image.png

After pinning:

image.png