Skip to content

Commit e9db79f

Browse files
charles-typfacebook-github-bot
authored andcommitted
Add NIC IRQ affinity tuning support (#528)
Summary: Add NIC IRQ affinity configuration to ucache_bench, ported from TaoBench. This feature distributes network interrupt processing across CPUs to prevent IRQ handling from bottlenecking on a few cores. New parameters: - nic_channel_ratio: Ratio of NIC channels to logical cores (0.0 = disabled) - interface_name: Network interface for IRQ affinity tuning (default: eth0) - hard_binding: Hard bind NIC channels to specific CPU cores (default: 0) Changes: - Add affinitize_nic() function to configure NIC channels via ethtool and redistribute IRQ affinity using affinitize_nic.py script - Add new CLI arguments to server: --nic-channel-ratio, --interface-name, --hard-binding - Update install script to copy affinitize_nic scripts for OSS builds - Add NIC affinity params to benchmark configs and jobs_internal.yml - Add ucache_bench_debug_nic_affinity_configs.json for testing Differential Revision: D96763816
1 parent 15318ef commit e9db79f

2 files changed

Lines changed: 109 additions & 0 deletions

File tree

packages/ucache_bench/install_ucache_bench.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,17 @@ else
747747
echo " WARNING: ucachebench_client binary not found"
748748
fi
749749

750+
# Copy affinitize NIC scripts for IRQ affinity tuning
751+
AFFINITIZE_SRC="$BENCHPRESS_ROOT/packages/common/affinitize"
752+
AFFINITIZE_DST="$UCACHE_BENCH_DIR/affinitize"
753+
if [ -d "$AFFINITIZE_SRC" ]; then
754+
echo "Copying affinitize NIC scripts..."
755+
mkdir -p "$AFFINITIZE_DST"
756+
cp -r "$AFFINITIZE_SRC/"* "$AFFINITIZE_DST/"
757+
chmod +x "$AFFINITIZE_DST/affinitize_nic.py" 2>/dev/null || true
758+
echo " Installed: $AFFINITIZE_DST/"
759+
fi
760+
750761
echo ""
751762
echo "=============================================="
752763
echo "UCacheBench Installation Complete!"

packages/ucache_bench/run.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
BENCHPRESS_ROOT: pathlib.Path = pathlib.Path(os.path.abspath(__file__)).parents[2]
3333
UCACHE_BENCH_DIR: str = os.path.join(BENCHPRESS_ROOT, "benchmarks", "ucache_bench")
34+
AFFINITIZE_NIC_SYSTEM_PATH: str = "/usr/local/bin/affinitize_nic"
3435

3536
# Constants
3637
MEM_USAGE_FACTOR = 0.75 # to prevent OOM
@@ -158,6 +159,78 @@ def profile_server() -> None:
158159
)
159160

160161

162+
def get_affinitize_nic_path() -> str:
163+
"""Get path to the affinitize_nic script.
164+
165+
Checks system path first, then common packages dir, falls back to
166+
benchmark-local copy (created by install script for OSS builds).
167+
"""
168+
if os.path.exists(AFFINITIZE_NIC_SYSTEM_PATH):
169+
return AFFINITIZE_NIC_SYSTEM_PATH
170+
# Internal fbpkg: shared utility in packages/common/affinitize/
171+
common_path = os.path.join(
172+
BENCHPRESS_ROOT, "packages", "common", "affinitize", "affinitize_nic.py"
173+
)
174+
if os.path.exists(common_path):
175+
return common_path
176+
# OSS: copied by install script to benchmark dir
177+
return os.path.join(UCACHE_BENCH_DIR, "affinitize", "affinitize_nic.py")
178+
179+
180+
def affinitize_nic(args: argparse.Namespace) -> None:
181+
"""Configure NIC IRQ affinity to distribute interrupts across CPUs.
182+
183+
This prevents IRQ processing from bottlenecking on a few cores.
184+
Ported from TaoBench's NIC affinity tuning.
185+
186+
Steps:
187+
1. Set the number of NIC combined channels using ethtool
188+
2. Redistribute IRQ affinity across CPUs using affinitize_nic.py
189+
"""
190+
n_cores = len(os.sched_getaffinity(0))
191+
n_channels = int(n_cores * args.nic_channel_ratio)
192+
193+
if n_channels <= 0:
194+
print(
195+
f"Skipping NIC affinity: n_channels={n_channels} "
196+
f"(cores={n_cores}, ratio={args.nic_channel_ratio})"
197+
)
198+
return
199+
200+
# Step 1: Set NIC channel count
201+
try:
202+
cmd = ["ethtool", "-L", args.interface_name, "combined", str(n_channels)]
203+
print(f"Setting NIC channels: {' '.join(cmd)}")
204+
subprocess.run(cmd, check=True, capture_output=True)
205+
except (subprocess.CalledProcessError, FileNotFoundError, OSError) as e:
206+
print(f"Failed to set NIC channels to {n_channels}: {e}")
207+
208+
# Step 2: Set IRQ affinity
209+
try:
210+
cmd = [
211+
get_affinitize_nic_path(),
212+
"-f",
213+
"-a",
214+
"--xps",
215+
]
216+
if args.hard_binding:
217+
cmd += [
218+
"--cpu",
219+
" ".join(str(x) for x in range(n_channels)),
220+
]
221+
else:
222+
cmd += [
223+
"-A",
224+
"all-nodes",
225+
"--max-cpus",
226+
str(n_channels),
227+
]
228+
print(f"Setting NIC IRQ affinity: {' '.join(cmd)}")
229+
subprocess.run(cmd, check=True, capture_output=True)
230+
except (subprocess.CalledProcessError, FileNotFoundError, OSError) as e:
231+
print(f"Failed to set NIC IRQ affinity: {e}")
232+
233+
161234
def run_server(args: argparse.Namespace) -> None:
162235
"""Run the UcacheBench server.
163236
@@ -177,6 +250,10 @@ def run_server(args: argparse.Namespace) -> None:
177250
hash_power = calculate_hash_power(memory_mb)
178251
print(f"Auto-calculated hash_power={hash_power} for {memory_mb}MB memory")
179252

253+
# Configure NIC IRQ affinity before starting server
254+
if args.interface_name != "lo" and args.nic_channel_ratio > 0:
255+
affinitize_nic(args)
256+
180257
print(
181258
f"Starting UcacheBench server with {args.memory_mb}MB memory on port {args.port}"
182259
)
@@ -522,6 +599,27 @@ def init_parser() -> argparse.ArgumentParser:
522599
help="Reduce IO thread count to match non-IRQ CPU count (set to non-zero to enable)",
523600
)
524601

602+
# NIC IRQ affinity configuration (ported from TaoBench)
603+
server_parser.add_argument(
604+
"--nic-channel-ratio",
605+
type=float,
606+
default=0.0,
607+
help="Ratio of NIC channels to logical cores (0.0 = disabled, 0.5 = TaoBench default). "
608+
"Sets ethtool combined channels and redistributes IRQ affinity.",
609+
)
610+
server_parser.add_argument(
611+
"--interface-name",
612+
type=str,
613+
default="eth0",
614+
help="Network interface name for NIC IRQ affinity tuning",
615+
)
616+
server_parser.add_argument(
617+
"--hard-binding",
618+
type=int,
619+
default=0,
620+
help="Hard bind NIC channels to specific CPU cores (set to non-zero to enable)",
621+
)
622+
525623
# Navy (hybrid mode) configuration
526624
server_parser.add_argument(
527625
"--navy-cache-path",

0 commit comments

Comments
 (0)