Skip to content
Open
21 changes: 21 additions & 0 deletions benchpress/config/jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@
- '--rpc-fanout-scale={rpc_fanout_scale}'
- '--server-zstd={server_zstd}'
- '--sla-p95-ms={sla_p95_ms}'
- '--depth={depth}'
- '{extra_args}'
vars:
# Hardcode num_instances=1: this job exists specifically for single-instance
Expand Down Expand Up @@ -534,6 +535,12 @@
- 'rpc_fanout_scale=0.05'
- 'server_zstd=0'
- 'sla_p95_ms=700'
# Driver pipeline depth (max outstanding requests per driver connection).
# Default 1. Raise (e.g. 2) if the final phase saturates neither CPU nor SLA
# latency (final p95 well below sla_p95_ms while CPU util < ~90%) — often
# needed on high-perf ARM cores. With adaptive depth on (the default), this is
# the starting floor the peak search raises from; see README "Driver depth".
- 'depth=1'
- 'extra_args='
hooks:
- hook: cpu-mpstat
Expand Down Expand Up @@ -592,6 +599,7 @@
- '--rpc-fanout-scale={rpc_fanout_scale}'
- '--server-zstd={server_zstd}'
- '--sla-p95-ms={sla_p95_ms}'
- '--depth={depth}'
- '{extra_args}'
vars:
- 'num_instances=-1'
Expand Down Expand Up @@ -635,6 +643,12 @@
- 'rpc_fanout_scale=0.05'
- 'server_zstd=0'
- 'sla_p95_ms=700'
# Driver pipeline depth (max outstanding requests per driver connection).
# Default 1. Raise (e.g. 2) if the final phase saturates neither CPU nor SLA
# latency (final p95 well below sla_p95_ms while CPU util < ~90%) — often
# needed on high-perf ARM cores. With adaptive depth on (the default), this is
# the starting floor the peak search raises from; see README "Driver depth".
- 'depth=1'
- 'extra_args='
hooks:
- hook: cpu-mpstat
Expand Down Expand Up @@ -695,6 +709,7 @@
- '--rpc-fanout-scale={rpc_fanout_scale}'
- '--server-zstd={server_zstd}'
- '--sla-p95-ms={sla_p95_ms}'
- '--depth={depth}'
- '{extra_args}'
vars:
- 'num_instances=-1'
Expand Down Expand Up @@ -735,6 +750,12 @@
- 'rpc_fanout_scale=0.05'
- 'server_zstd=0'
- 'sla_p95_ms=700'
# Driver pipeline depth (max outstanding requests per driver connection).
# Default 1. Raise (e.g. 2) if the final phase saturates neither CPU nor SLA
# latency (final p95 well below sla_p95_ms while CPU util < ~90%) — often
# needed on high-perf ARM cores. With adaptive depth on (the default), this is
# the starting floor the peak search raises from; see README "Driver depth".
- 'depth=1'
- 'extra_args='
hooks:
- hook: copymove
Expand Down
42 changes: 39 additions & 3 deletions packages/feedsim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ taskset -c 0-15 ./benchpress_cli.py install feedsim_dlrm
Unlike FeedSim v1 which spawns a new FeedSim instance per 100 CPU cores,
`feedsim_dlrm` is pinned to **one FeedSim instance per host** because the
redesigned threading model in FeedSim v2 has overcome the scalability issue
on ultra-high-core-count CPUs and ARM CPUs.
on ultra-high-core-count CPUs and ARM CPUs.

The runner searches for the QPS that keeps 95th-percentile end-to-end
latency at or below **700 ms**. When it converges it runs a final 5-minute
Expand All @@ -64,7 +64,7 @@ counters) during that final window. We expect the total wall-clock runtime
to be around 30 minutes.

Please make sure to turn CPU turbo-boost on before starting, or FeedSim may
fail to converge and report a low QPS.
fail to converge and report a low QPS.

### Result report

Expand Down Expand Up @@ -211,7 +211,7 @@ feedsim server, driver and mock_services instances.

2. Use the `feedsim_autoscale_dlrm` job. This autoscale job will spawn `ceil(nproc / 100)`
FeedSim instances, each pinned to its own CPU range via `taskset`, plus one driver
and one `mock_services` process per instance (also `taskset`-isolated). For example:
and one `mock_services` process per instance (also `taskset`-isolated). For example:
```
./benchpress_cli.py run feedsim_autoscale_dlrm
```
Expand All @@ -220,6 +220,41 @@ In multi-instance mode, the overall QPS is the sum across all instances. and the
average latency will be the average of p95 latency values observed across all
instances.

### Driver depth (fixing CPU/latency under-utilization)

The `depth` parameter sets the driver's pipeline depth — the maximum number of
outstanding (in-flight) requests per driver connection. The driver's total
offered concurrency is `driver_threads × connections × depth`, so with the
default `depth=1` the driver can cap the achievable load below what the server
can actually handle.

**Increase `depth` beyond 1 when the final benchmarking phase saturates neither
CPU nor latency** — i.e. the final achieved p95 latency is well below the SLA
limit (`sla_p95_ms`, default 700 ms) *and* the CPU utilization during the final
5-minute benchmarking phase is less than ~90%. In that situation the reported QPS
is limited by driver concurrency rather than by the server, so it understates the
hardware's true capacity. Raising `depth` (start with `2`) lets the driver offer
more concurrent load until the server becomes the bottleneck — either CPU-bound
(~100% utilization) or latency-bound (p95 ≈ SLA). **This is likely necessary on
high-performance ARM cores** (e.g. NVIDIA Grace), which can otherwise sit at
80–90% CPU with p95 far below the SLA at `depth=1`.

```
# Force driver depth 2
./benchpress_cli.py run feedsim_dlrm -i '{"depth": 2}'
```

There is also an **adaptive depth** mechanism (on by default) that raises the
depth automatically during the peak-finding stage until the server saturates
(system CPU ≥ 95% or p95 ≥ SLA). It catches *severe* under-utilization early, but
because it evaluates saturation on the high-load peak/search probes rather than
on the final SLA-converged operating point, it **may not catch all
under-utilization cases**. If you still observe under-utilization in the final
result (low CPU + p95 well under SLA), increase `depth` manually as above. When
adaptive depth is on, a manually-set `depth` acts as the starting floor the
adaptive search raises from; to pin an exact fixed depth, also set the
`FEEDSIM_ADAPTIVE_DEPTH_MAX=0` environment variable to disable adaptive search.

### Other parameters

This section lists additional parameters in `feedsim_dlrm` benchmark. These parameters
Expand All @@ -233,6 +268,7 @@ Job-level parameters (can be passed via `-i` flag in Benchpress CLI):
|---|---|---|
| `num_instances` | Number of FeedSim instances to run in parallel. Defaults to 1 in `feedsim_dlrm`; set to -1 to autoscale for `feedsim_autoscale_dlrm`. | `1` |
| `sla_p95_ms` | SLA target in ms. The runner searches for the highest QPS keeping p95 ≤ this. | `700` |
| `depth` | Driver pipeline depth (max outstanding requests per connection; total in-flight = `driver_threads × connections × depth`). Raise (e.g. `2`) when the final phase saturates neither CPU nor latency — often needed on high-perf ARM. See [Driver depth](#driver-depth-fixing-cpulatency-under-utilization). | `1` |
| `io_dist` | I/O latency distribution: `fixed`, `exponential`, or `lognormal`. | `fixed` |
| `io_mean` | Mean I/O latency in ms. | `200` |
| `workload` | Ranking workload: `pagerank` or `dlrm`. `dlrm` is v2. | `dlrm` |
Expand Down
72 changes: 53 additions & 19 deletions packages/feedsim/install_feedsim.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,20 @@ FEEDSIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
BENCHPRESS_ROOT="$(readlink -f "$FEEDSIM_ROOT/../..")"
FEEDSIM_ROOT_SRC="${BENCHPRESS_ROOT}/benchmarks/feedsim"
FEEDSIM_THIRD_PARTY_SRC="${FEEDSIM_ROOT_SRC}/third_party"
LIBTORCH_VERSION="2.8.0"
LIBTORCH_VERSION="${LIBTORCH_VERSION:-2.13.0}"
# When 1, fetch LibTorch by extracting it from the prebuilt torch CPU wheel
# (download.pytorch.org/whl/cpu) instead of the libtorch-shared-with-deps zip.
# Required for LibTorch >=2.9 (2.13.0 and later publish a wheel but no
# standalone zip); harmless for older versions. Default 1 pairs with the
# LIBTORCH_VERSION=2.13.0 default so the out-of-box install works without
# additional env overrides.
LIBTORCH_FROM_WHEEL="${LIBTORCH_FROM_WHEEL:-1}"
# Dependency versions are env-overridable so experiments can bump them without
# forking this script; defaults reproduce the v2 baseline exactly.
JEMALLOC_VERSION="${FEEDSIM_JEMALLOC_VERSION:-5.3.0}"
LIBEVENT_VERSION="${FEEDSIM_LIBEVENT_VERSION:-2.1.12-stable}"
# Export so the aarch64 sub-installer (dispatched below) inherits the pins.
export LIBTORCH_VERSION LIBTORCH_FROM_WHEEL FEEDSIM_JEMALLOC_VERSION FEEDSIM_LIBEVENT_VERSION
DLRM_MODEL_URL="https://github.com/facebookresearch/DCPerf-datasets/releases/download/feedsim-dlrm/dlrm_small.tar.gz"
echo "BENCHPRESS_ROOT is ${BENCHPRESS_ROOT}"

Expand Down Expand Up @@ -45,7 +58,7 @@ dnf install -y bc ninja-build flex bison git texinfo binutils-devel \
libsodium-devel libunwind-devel bzip2-devel double-conversion-devel \
libzstd-devel lz4-devel xz-devel snappy-devel libtool bzip2 openssl-devel \
zlib-devel libdwarf libdwarf-devel libaio-devel libatomic patch jq \
xxhash xxhash-devel unzip rsync liburing-devel
xxhash xxhash-devel unzip rsync liburing-devel python3-pip

# Creates feedsim directory under benchmarks/
mkdir -p "${BENCHPRESS_ROOT}/benchmarks/feedsim"
Expand Down Expand Up @@ -178,30 +191,30 @@ else
fi

# Installing JEMalloc
if ! [ -d "jemalloc-5.3.0" ]; then
wget "https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2"
bunzip2 "jemalloc-5.3.0.tar.bz2"
tar -xvf "jemalloc-5.3.0.tar"
cd "jemalloc-5.3.0"
if ! [ -d "jemalloc-${JEMALLOC_VERSION}" ]; then
wget "https://github.com/jemalloc/jemalloc/releases/download/${JEMALLOC_VERSION}/jemalloc-${JEMALLOC_VERSION}.tar.bz2"
bunzip2 "jemalloc-${JEMALLOC_VERSION}.tar.bz2"
tar -xvf "jemalloc-${JEMALLOC_VERSION}.tar"
cd "jemalloc-${JEMALLOC_VERSION}"
./configure --enable-prof --enable-prof-libunwind
make -j"$(nproc)"
make install
cd ../
else
msg "[SKIPPED] jemalloc-5.3.0"
msg "[SKIPPED] jemalloc-${JEMALLOC_VERSION}"
fi

# Installing libevent
if ! [ -d "libevent-2.1.12-stable" ]; then
wget "https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz"
tar -xzf "libevent-2.1.12-stable.tar.gz"
cd "libevent-2.1.12-stable"
if ! [ -d "libevent-${LIBEVENT_VERSION}" ]; then
wget "https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}/libevent-${LIBEVENT_VERSION}.tar.gz"
tar -xzf "libevent-${LIBEVENT_VERSION}.tar.gz"
cd "libevent-${LIBEVENT_VERSION}"
./configure
make -j"$(nproc)"
make install
cd ../
else
msg "[SKIPPED] libevent-2.1.12-stable"
msg "[SKIPPED] libevent-${LIBEVENT_VERSION}"
fi

msg "Installing third-party dependencies ... DONE"
Expand All @@ -218,12 +231,33 @@ else
fi

if ! [ -d "libtorch" ]; then
msg "Downloading LibTorch ${LIBTORCH_VERSION}..."
wget "${LIBTORCH_URL}" -O libtorch.zip
msg "Extracting LibTorch..."
unzip -q libtorch.zip
rm libtorch.zip
msg "LibTorch installed to ${FEEDSIM_THIRD_PARTY_SRC}/libtorch"
if [ "${LIBTORCH_FROM_WHEEL}" = "1" ]; then
# Extract LibTorch from the prebuilt torch CPU wheel. The wheel's
# torch/ dir has the same lib/ include/ share/cmake/Torch/ layout as
# the standalone libtorch zip, so we just rename it to libtorch/.
msg "Downloading LibTorch ${LIBTORCH_VERSION} from torch CPU wheel..."
# pip on the box (3.9, or an internal stale mirror) can't see the cp310
# 2.13 wheels, so resolve the wheel href straight from the PEP-503 index
# and wget it. The C++ libtorch inside (torch/lib, torch/share/cmake) is
# Python-version independent, so the cp310 wheel is fine for our C++ link.
WHEEL_HREF="$(curl -s "https://download.pytorch.org/whl/cpu/torch/" \
| grep -oE "https://[^\"]*torch-${LIBTORCH_VERSION}[^\"]*cp310-cp310-manylinux_2_28_x86_64\.whl" \
| head -1)"
[ -n "${WHEEL_HREF}" ] || die "Could not find torch ${LIBTORCH_VERSION} x86_64 wheel in index"
msg "Wheel: ${WHEEL_HREF}"
wget "${WHEEL_HREF}" -O torch.whl
unzip -q torch.whl -d ./_torch_whl_x
mv ./_torch_whl_x/torch libtorch
rm -rf ./_torch_whl_x torch.whl
msg "LibTorch ${LIBTORCH_VERSION} extracted from wheel to ${FEEDSIM_THIRD_PARTY_SRC}/libtorch"
else
msg "Downloading LibTorch ${LIBTORCH_VERSION}..."
wget "${LIBTORCH_URL}" -O libtorch.zip
msg "Extracting LibTorch..."
unzip -q libtorch.zip
rm libtorch.zip
msg "LibTorch installed to ${FEEDSIM_THIRD_PARTY_SRC}/libtorch"
fi
else
msg "[SKIPPED] LibTorch already installed"
fi
Expand Down
27 changes: 20 additions & 7 deletions packages/feedsim/install_feedsim_aarch64.sh
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,15 @@ else
msg "[SKIPPED] glog-${DEP_GFLAGS_VERSION}"
fi

DEP_JEMALLOC_VERSION="5.3.0"
DEP_JEMALLOC_VERSION="${FEEDSIM_JEMALLOC_VERSION:-5.3.0}"
# Installing JEMalloc
if ! [ -d "jemalloc-${DEP_JEMALLOC_VERSION}" ]; then
wget "https://github.com/jemalloc/jemalloc/releases/download/${DEP_JEMALLOC_VERSION}/jemalloc-${DEP_JEMALLOC_VERSION}.tar.bz2" -O "jemalloc-${DEP_JEMALLOC_VERSION}.tar.bz2"
verify_checksum "jemalloc-${DEP_JEMALLOC_VERSION}.tar.bz2" "2db82d1e7119df3e71b7640219b6dfe84789bc0537983c3b7ac4f7189aecfeaa"
if [ "${DEP_JEMALLOC_VERSION}" = "5.3.0" ]; then
verify_checksum "jemalloc-${DEP_JEMALLOC_VERSION}.tar.bz2" "2db82d1e7119df3e71b7640219b6dfe84789bc0537983c3b7ac4f7189aecfeaa"
else
msg "[WARN] no pinned checksum for jemalloc ${DEP_JEMALLOC_VERSION}; skipping verify (official github release over https)"
fi
bunzip2 "jemalloc-${DEP_JEMALLOC_VERSION}.tar.bz2"
tar -xvf "jemalloc-${DEP_JEMALLOC_VERSION}.tar"
cd "jemalloc-${DEP_JEMALLOC_VERSION}"
Expand All @@ -198,11 +202,15 @@ else
msg "[SKIPPED] jemalloc-${DEP_JEMALLOC_VERSION}"
fi

DEP_LIBEVENT_VERSION="2.1.12-stable"
DEP_LIBEVENT_VERSION="${FEEDSIM_LIBEVENT_VERSION:-2.1.12-stable}"
# Installing libevent
if ! [ -d "libevent-${DEP_LIBEVENT_VERSION}" ]; then
wget "https://github.com/libevent/libevent/releases/download/release-${DEP_LIBEVENT_VERSION}/libevent-${DEP_LIBEVENT_VERSION}.tar.gz" -O "libevent-${DEP_LIBEVENT_VERSION}.tar.gz"
verify_checksum "libevent-${DEP_LIBEVENT_VERSION}.tar.gz" "92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb"
if [ "${DEP_LIBEVENT_VERSION}" = "2.1.12-stable" ]; then
verify_checksum "libevent-${DEP_LIBEVENT_VERSION}.tar.gz" "92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb"
else
msg "[WARN] no pinned checksum for libevent ${DEP_LIBEVENT_VERSION}; skipping verify (official github release over https)"
fi
tar -xzf "libevent-${DEP_LIBEVENT_VERSION}.tar.gz"
cd "libevent-${DEP_LIBEVENT_VERSION}"
./configure
Expand Down Expand Up @@ -242,9 +250,14 @@ if ! [ -d "libtorch" ]; then
export PATH="${CONDA_DIR}/bin:${PATH}"

# Install CPU-only PyTorch via pip — this is the only reliable way to get
# CPU-only libtorch on aarch64
msg "Installing PyTorch CPU-only via pip..."
pip install torch --index-url https://download.pytorch.org/whl/cpu
# CPU-only libtorch on aarch64. LIBTORCH_VERSION (env) pins the version;
# unset reproduces the v2 baseline (latest).
msg "Installing PyTorch CPU-only via pip (version='${LIBTORCH_VERSION:-latest}')..."
if [ -n "${LIBTORCH_VERSION:-}" ]; then
pip install "torch==${LIBTORCH_VERSION}+cpu" --index-url https://download.pytorch.org/whl/cpu
else
pip install torch --index-url https://download.pytorch.org/whl/cpu
fi

# Also install libstdcxx-ng to ensure compatible C++ runtime
eval "$("${CONDA_DIR}/bin/conda" shell.bash hook)"
Expand Down
14 changes: 10 additions & 4 deletions packages/feedsim/install_feedsim_aarch64_ubuntu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ FEEDSIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
BENCHPRESS_ROOT="$(readlink -f "$FEEDSIM_ROOT/../..")"
FEEDSIM_ROOT_SRC="${BENCHPRESS_ROOT}/benchmarks/feedsim"
FEEDSIM_THIRD_PARTY_SRC="${FEEDSIM_ROOT_SRC}/third_party"
LIBTORCH_VERSION="${LIBTORCH_VERSION:-2.13.0}"
DLRM_MODEL_URL="https://github.com/facebookresearch/DCPerf-datasets/releases/download/feedsim-dlrm/dlrm_small.tar.gz"
echo "BENCHPRESS_ROOT is ${BENCHPRESS_ROOT}"

apt install -y bc cmake ninja-build flex bison texinfo binutils-dev \
libunwind-dev bzip2 libbz2-dev libsodium-dev libghc-double-conversion-dev \
libzstd-dev lz4 liblz4-dev xzip libsnappy-dev libtool libssl-dev \
zlib1g-dev libdwarf-dev libaio-dev libatomic1 patch perl libiberty-dev \
sysstat jq unzip xxhash libxxhash-dev libboost-all-dev rsync
sysstat jq unzip xxhash libxxhash-dev libboost-all-dev rsync curl

# Install liburing >= 2.6 from source. Ubuntu's apt-shipped liburing is
# older than folly's minimum, so folly's io_uring integration links
Expand Down Expand Up @@ -204,9 +205,14 @@ if ! [ -d "libtorch" ]; then
export PATH="${CONDA_DIR}/bin:${PATH}"

# Install CPU-only PyTorch via pip — this is the only reliable way to get
# CPU-only libtorch on aarch64
msg "Installing PyTorch CPU-only via pip..."
pip install torch --index-url https://download.pytorch.org/whl/cpu
# CPU-only libtorch on aarch64. LIBTORCH_VERSION (env) pins the version;
# empty falls back to pip's latest resolution.
msg "Installing PyTorch CPU-only via pip (version='${LIBTORCH_VERSION:-latest}')..."
if [ -n "${LIBTORCH_VERSION:-}" ]; then
pip install "torch==${LIBTORCH_VERSION}+cpu" --index-url https://download.pytorch.org/whl/cpu
else
pip install torch --index-url https://download.pytorch.org/whl/cpu
fi

# Also install libstdcxx-ng to ensure compatible C++ runtime
eval "$("${CONDA_DIR}/bin/conda" shell.bash hook)"
Expand Down
Loading
Loading