Skip to content
LinkState
Go back

Exporter overhead on already busy routers

Introduction

High‑frequency scraping of network interface statistics and TX/RX queues forces the kernel to copy counters from per‑CPU or per‑queue structures into user space on each poll. Each poll incurs CPU cycles for system‑call entry/exit, copying of 64‑bit counters, and any required locking (e.g., u64_stats_sync). It also generates memory traffic as counters are read from cache lines that may be dirty due to NAPI processing, and can cause contention if the scraper runs on the same core that handles NAPI poll loops. The tax scales roughly linearly with poll frequency until the overhead saturates a core or exhausts memory bandwidth, at which point packet forwarding begins to drop or latency spikes.

Benchmarking provides a repeatable method to isolate the cost of the scraping loop from the cost of actual traffic, yields metrics such as CPU % per kHz of poll and extra memory bandwidth per poll, and helps decide when to switch from polling to event‑driven mechanisms (netlink notifications, eBPF maps, XDP‑driven counters).


Setting Up the Benchmarking Environment

Choosing the Right Tools and Software

PurposeToolReason
Traffic generatorpktgen (kernel) or trex (userspace)Generates deterministic, high‑rate UDP/TCP flows with controllable burst size.
Interface/queue stats scraperCustom C program using ethtool -S, sysfs (/sys/class/net/<if>/statistics/), or netlink RTM_GETLINKPrecise control over poll interval; avoids extra layers (e.g., snmpd).
CPU/memory monitoringperf stat, pidstat, /proc/stat, /proc/<pid>/statmLow‑overhead, kernel‑sourced counters.
Queue depth observationtc -s qdisc show dev <if>, /sys/class/net/<if>/queues/<tx‑rx>-<n>/Direct exposure of NIC TX/RX ring occupancy.
Kernel tracing (optional)bpftrace, tracepoint:net:net_dev_queue, tracepoint:skb:kfree_skbValidate that scraper does not interfere with NAPI.
System under testTwo identical servers (Linux 5.15‑0‑generic, Intel Xeon E5‑2680 v4, 2 × 10 GbE Intel X710 NICs) – one as traffic sender, one as receiver/scraper.Enables controlled, symmetric load.

Configuring Linux and Routing Hosts for Benchmarking

  1. Disable frequency scaling
    echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
  2. Isolate cores for the scraper (and optionally the traffic generator) using isolcpus and taskset. Example kernel boot line:
    isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3
    Bind the scraper to CPU 2:
    taskset -c 2 ./scraper -i eth0 -i 1000   # 1 kHz poll
  3. Turn off unnecessary offloads
    ethtool -K eth0 tso off gso off gro off lro off
  4. Verify NAPI weighting (default usually 64)
    ethtool -c eth0 | grep -i napi
  5. Set static IRQ affinity to keep NIC queues on dedicated cores (e.g., cores 4‑7)
    echo 4-7 > /proc/irq/<irq-number>/smp_affinity_list
  6. Mount debugfs for eBPF/bpftrace if needed
    mount -t debugfs none /sys/kernel/debug

Installing and Configuring Necessary Packages

On Ubuntu 22.04 (or equivalent):

apt-get update
apt-get install -y build-essential linux-tools-common linux-tools-`uname -r` \
                   ethtool iproute2 pktgen-dkms trex \
                   perf bpftrace python3-psutil prometheus-node-exporter grafana

Compile the scraper (see Code Examples) with -O2 -march=native -flto.


Benchmarking CPU Tax

Using System Monitoring Tools to Track CPU Usage

Running Benchmarking Tests with High‑Frequency Interface and Queue Scraping

Scraper loop (simplified C):

while (!stop) {
    clock_gettime(CLOCK_MONOTONIC, &start);
    /* read rx/tx packets, bytes, drops, errors from sysfs */
    read_counter("/sys/class/net/eth0/statistics/rx_packets", &rx_pkt);
    read_counter("/sys/class/net/eth0/statistics/tx_packets", &tx_pkt);
    /* optional: per‑queue stats */
    for (int q = 0; q < num_queues; ++q) {
        char path[128];
        snprintf(path, sizeof(path),
                 "/sys/class/net/eth0/queues/rx-%d/packets", q);
        read_counter(path, &rx_q[q]);
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    /* sleep remaining time to hit target frequency */
    nanosleep(&rem, NULL);
}

Compile and run with varying -i (interval in µs):

# Baseline: 10 ms (100 Hz)
taskset -c 2 ./scraper -i 10000 -i eth0 &
# Sweep
for us in 1000 500 200 100 50 20 10; do
    taskset -c 2 ./scraper -i $us -i eth0 &
    scraper_pid=$!
    sleep 5
    pidstat -p $scraper_pid 1 3
    kill $scraper_pid
done

While the scraper runs, the traffic generator pushes a constant 5 Gbps UDP stream (≈7.5 Mpps 64‑byte packets) to stress the NIC and NAPI.

Analyzing CPU Usage Patterns and Identifying Bottlenecks


Benchmarking Memory Tax

Monitoring Memory Usage with System Tools

Running Memory‑Intensive Benchmarking Tests

To stress memory, increase the amount of data scraped per poll:

Analyzing Memory Usage Patterns and Identifying Limitations


Troubleshooting Common Issues

Identifying and Resolving CPU‑Related Bottlenecks

Symptom: Rising %softirq on NAPI cores, increased packet loss, latency jitter.

Diagnosis:

Resolution:

Identifying and Resolving Memory‑Related Issues

Symptom: Unexpected RSS growth or OOM killer activation.

Diagnosis: Check pmap <pid> for anonymous allocations; look for leaks in the scraper (e.g., failure to free() buffers).

Resolution:

CLI Examples to Troubleshoot and Optimize Performance

# 1. Verify per‑core load while scraper runs at 5 kHz
mpstat -P ALL 1 | awk '$3=="2"{print $0}'   # core 2 only

# 2. Identify hot syscalls
perf record -p $(pgrep scraper) -g sleep 10
perf report --stdio | grep -E "sys_read|vfs_read|__u64_stats_sync"

# 3. Switch to netlink‑based scraper and compare
taskset -c 2 ./scraper_netlink -i 200 -i eth0 &

Share this post on:

Previous Post
The remote SSH lockout after an atomic replace
Next Post
Score diagnosis process, not only final answer