Skip to content

Commit f08e662

Browse files
excelle08meta-codesync[bot]
authored andcommitted
Add scaled-up feature extraction pipeline (#704)
Summary: Pull Request resolved: #704 Add mock feature extraction pipeline to FeedSim with large-scale code generation for I-cache and frontend pressure. 27 genuinely diverse code patterns (derived from studying 696 production feature extractors) generate ~700 variants × 1000 copies = ~700K unique functions at install time. Key components: - 6 hand-written extractors based on production leaf function profiling - 27 pattern-specific code generators (P01-P27) producing genuinely different instruction sequences (different branch topologies, loop nesting, data access patterns, code sizes from 10 to 2300 lines) - Flat shuffled dispatch: all copy function pointers shuffled into one vector, iterated sequentially per request for maximum I-cache pressure - DLRM medium/large model generation on-server during install - Configurable via --num_stories, --extractors_per_story, --feature_complexity Results on T1_BGM (Bergamo, 176 cores): 500K calls/req: L1 I-Cache MPKI 21.34 (prod target 21), IPC 0.69 (prod 0.6-0.8) 100K calls/req: Frontend Bound 23.5%, IPC 1.22, QPS 242 Results on T11_GRC_ARM (Grace, 72 cores): 100K calls/req: IPC 0.52, L1 I-Cache MPKI 15.91 Medium DLRM + 100K calls: IPC 1.03 (prod target 1.05) Reviewed By: YifanYuan3, charles-typ Differential Revision: D97022149
1 parent c9d87e5 commit f08e662

44 files changed

Lines changed: 5178 additions & 18 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

benchpress/config/jobs.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,10 @@
646646
- '--client-feature-seed={client_feature_seed}'
647647
- '--client-num-dense={client_num_dense}'
648648
- '--client-num-sparse={client_num_sparse}'
649+
- '--feature-extractors={feature_extractors}'
650+
- '--feature-complexity={feature_complexity}'
651+
- '--num-stories={num_stories}'
652+
- '--extractors-per-story={extractors_per_story}'
649653
vars:
650654
- 'port=11222'
651655
- 'output=feedsim_results.txt'
@@ -662,6 +666,10 @@
662666
- 'client_feature_seed=42'
663667
- 'client_num_dense=13'
664668
- 'client_num_sparse=26'
669+
- 'feature_extractors=0'
670+
- 'feature_complexity=5'
671+
- 'num_stories=100'
672+
- 'extractors_per_story=50'
665673
hooks:
666674
- hook: cpu-mpstat
667675
options:
@@ -731,6 +739,10 @@
731739
- '--client-feature-seed={client_feature_seed}'
732740
- '--client-num-dense={client_num_dense}'
733741
- '--client-num-sparse={client_num_sparse}'
742+
- '--feature-extractors={feature_extractors}'
743+
- '--feature-complexity={feature_complexity}'
744+
- '--num-stories={num_stories}'
745+
- '--extractors-per-story={extractors_per_story}'
734746
- '{extra_args}'
735747
vars:
736748
- 'num_instances=-1'
@@ -747,6 +759,10 @@
747759
- 'client_feature_seed=42'
748760
- 'client_num_dense=13'
749761
- 'client_num_sparse=26'
762+
- 'feature_extractors=0'
763+
- 'feature_complexity=5'
764+
- 'num_stories=100'
765+
- 'extractors_per_story=50'
750766
- 'extra_args='
751767
hooks:
752768
- hook: cpu-mpstat
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
"""Generate DLRM medium and large TorchScript models with random weights.
7+
8+
For benchmarking, trained weights aren't needed — the compute pattern
9+
(embedding lookups, MLP forward passes, feature interactions) is identical
10+
regardless of weight values. These models are architecture-compatible with
11+
the existing dlrm_small.pt model used by FeedSim's DLRM inference path.
12+
13+
Usage:
14+
python3 generate_dlrm_models.py <output_dir>
15+
"""
16+
17+
import os
18+
import sys
19+
20+
import torch
21+
from torch import nn
22+
23+
24+
class DLRM(nn.Module):
25+
"""Simplified DLRM model matching production inference patterns."""
26+
27+
def __init__(
28+
self,
29+
emb_dim: int = 64,
30+
num_dense: int = 13,
31+
num_sparse: int = 26,
32+
max_emb_rows: int = 250000,
33+
bottom_mlp_dims: list = None,
34+
top_mlp_dims: list = None,
35+
):
36+
super().__init__()
37+
if bottom_mlp_dims is None:
38+
bottom_mlp_dims = [256, 128, emb_dim]
39+
if top_mlp_dims is None:
40+
top_mlp_dims = [256, 128, 1]
41+
42+
# Bottom MLP: dense features -> embedding dimension
43+
layers = []
44+
in_dim = num_dense
45+
for out_dim in bottom_mlp_dims:
46+
layers.append(nn.Linear(in_dim, out_dim))
47+
layers.append(nn.ReLU())
48+
in_dim = out_dim
49+
self.bottom_mlp = nn.Sequential(*layers)
50+
51+
# Embedding tables for sparse features
52+
emb_sizes = [
53+
40000000,
54+
39060,
55+
17295,
56+
7424,
57+
20265,
58+
3,
59+
7122,
60+
1543,
61+
63,
62+
40000000,
63+
3067956,
64+
405282,
65+
10,
66+
2209,
67+
11938,
68+
155,
69+
4,
70+
976,
71+
14,
72+
40000000,
73+
40000000,
74+
40000000,
75+
590152,
76+
12973,
77+
108,
78+
36,
79+
]
80+
# Use min(actual_size, max_emb_rows) to control model size
81+
self.embeddings = nn.ModuleList(
82+
[
83+
nn.EmbeddingBag(min(s, max_emb_rows), emb_dim, mode="sum")
84+
for s in emb_sizes[:num_sparse]
85+
]
86+
)
87+
88+
# Top MLP: interaction output -> prediction. ReLU only on hidden
89+
# layers; the final Linear feeds raw logits into sigmoid in forward().
90+
n = 1 + num_sparse # bottom_mlp output + embedding outputs
91+
interaction_size = emb_dim + (n * (n - 1)) // 2
92+
top_layers = []
93+
in_dim = interaction_size
94+
for i, out_dim in enumerate(top_mlp_dims):
95+
top_layers.append(nn.Linear(in_dim, out_dim))
96+
if i < len(top_mlp_dims) - 1:
97+
top_layers.append(nn.ReLU())
98+
in_dim = out_dim
99+
self.top_mlp = nn.Sequential(*top_layers)
100+
101+
def forward(self, dense: torch.Tensor, sparse: torch.Tensor) -> torch.Tensor:
102+
# Bottom MLP
103+
d = self.bottom_mlp(dense)
104+
105+
# Embedding lookups
106+
embs = [emb(sparse[:, i].unsqueeze(1)) for i, emb in enumerate(self.embeddings)]
107+
108+
# Feature interaction (dot product)
109+
combined = torch.cat([d.unsqueeze(1)] + [e.unsqueeze(1) for e in embs], dim=1)
110+
interact = torch.bmm(combined, combined.transpose(1, 2))
111+
n = combined.size(1)
112+
idx = torch.triu_indices(n, n, offset=1)
113+
flat = interact[:, idx[0], idx[1]]
114+
115+
# Top MLP
116+
x = torch.cat([d, flat], dim=1)
117+
return torch.sigmoid(self.top_mlp(x))
118+
119+
120+
def generate_model(output_path: str, **kwargs):
121+
"""Generate and save a TorchScript DLRM model."""
122+
model = DLRM(**kwargs)
123+
param_bytes = sum(p.numel() * p.element_size() for p in model.parameters())
124+
print(f" Parameters: {sum(p.numel() for p in model.parameters()):,}")
125+
print(f" Model size: {param_bytes / 1e6:.0f} MB")
126+
127+
scripted = torch.jit.script(model)
128+
scripted.save(output_path)
129+
file_size = os.path.getsize(output_path)
130+
print(f" Saved to: {output_path} ({file_size / 1e6:.0f} MB on disk)")
131+
132+
133+
def main():
134+
if len(sys.argv) < 2:
135+
print(f"Usage: {sys.argv[0]} <output_dir>")
136+
sys.exit(1)
137+
138+
output_dir = sys.argv[1]
139+
os.makedirs(output_dir, exist_ok=True)
140+
141+
# Medium model: larger embeddings (~500MB)
142+
medium_path = os.path.join(output_dir, "dlrm_medium.pt")
143+
if not os.path.exists(medium_path):
144+
print("Generating DLRM medium model...")
145+
generate_model(
146+
medium_path,
147+
emb_dim=64,
148+
max_emb_rows=500000,
149+
bottom_mlp_dims=[256, 128, 64],
150+
top_mlp_dims=[256, 128, 1],
151+
)
152+
else:
153+
print(f"[SKIPPED] {medium_path} already exists")
154+
155+
# Large model: even larger embeddings (~1GB)
156+
large_path = os.path.join(output_dir, "dlrm_large.pt")
157+
if not os.path.exists(large_path):
158+
print("Generating DLRM large model...")
159+
generate_model(
160+
large_path,
161+
emb_dim=128,
162+
max_emb_rows=500000,
163+
bottom_mlp_dims=[512, 256, 128],
164+
top_mlp_dims=[512, 256, 1],
165+
)
166+
else:
167+
print(f"[SKIPPED] {large_path} already exists")
168+
169+
170+
if __name__ == "__main__":
171+
main()

packages/feedsim/install_feedsim.sh

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ if [ -f "third_party/fizz/fizz/tool/FizzServerCommand.cpp" ]; then
242242
sed -i 's/EVP_PKEY_cmp(pubKey.get(), key.get()) == 1/EVP_PKEY_eq(pubKey.get(), key.get())/g' "third_party/fizz/fizz/tool/FizzServerCommand.cpp"
243243
fi
244244

245+
# Generate feature extractor variants (1M+ unique functions for I-cache pressure)
246+
msg "Generating feature extractor variants..."
247+
CODEGEN_DIR="${FEEDSIM_ROOT_SRC}/src/workloads/ranking/feature_extractors/generated"
248+
if [ -f "${CODEGEN_DIR}/generate_extractors.py" ]; then
249+
python3 "${CODEGEN_DIR}/generate_extractors.py" --output-dir "${CODEGEN_DIR}"
250+
msg "Feature extractor codegen complete"
251+
else
252+
msg "[SKIPPED] No codegen script found at ${CODEGEN_DIR}/generate_extractors.py"
253+
fi
254+
245255
mkdir -p build && cd build/
246256

247257
# Build FeedSim with DLRM support
@@ -261,7 +271,14 @@ cmake -G Ninja \
261271
-DCMAKE_PREFIX_PATH="${FEEDSIM_THIRD_PARTY_SRC}/libtorch" \
262272
../
263273

264-
ninja -v -j1
274+
# Dependencies (fmt, folly, fbthrift, etc.) are already installed above via
275+
# separate make commands in their own build directories. This ninja step only
276+
# builds FeedSim itself (LeafNodeRank, DriverNodeRank, feature extractors),
277+
# so parallel builds are safe here. Use nproc/2 to avoid OOM.
278+
NINJA_JOBS="${BP_NINJA_JOBS:-$(( $(nproc) / 2 ))}"
279+
[ "$NINJA_JOBS" -lt 1 ] && NINJA_JOBS=1
280+
msg "Building FeedSim with ninja -j${NINJA_JOBS} (set BP_NINJA_JOBS to override)"
281+
ninja -j"${NINJA_JOBS}"
265282

266283
msg ""
267284
msg "=== FeedSim Installation Complete ==="

packages/feedsim/install_feedsim_aarch64.sh

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,6 @@ else
273273
msg "[SKIPPED] DLRM model already installed"
274274
fi
275275

276-
277276
# Installing FeedSim
278277
cd "${FEEDSIM_ROOT_SRC}/src"
279278

@@ -303,6 +302,16 @@ if [ -f "third_party/fizz/fizz/tool/FizzServerCommand.cpp" ]; then
303302
sed -i 's/EVP_PKEY_cmp(pubKey.get(), key.get()) == 1/EVP_PKEY_eq(pubKey.get(), key.get())/g' "third_party/fizz/fizz/tool/FizzServerCommand.cpp"
304303
fi
305304

305+
# Generate feature extractor variants (1M+ unique functions for I-cache pressure)
306+
msg "Generating feature extractor variants..."
307+
CODEGEN_DIR="${FEEDSIM_ROOT_SRC}/src/workloads/ranking/feature_extractors/generated"
308+
if [ -f "${CODEGEN_DIR}/generate_extractors.py" ]; then
309+
python3 "${CODEGEN_DIR}/generate_extractors.py" --output-dir "${CODEGEN_DIR}"
310+
msg "Feature extractor codegen complete"
311+
else
312+
msg "[SKIPPED] No codegen script found at ${CODEGEN_DIR}/generate_extractors.py"
313+
fi
314+
306315
msg "Building FeedSim ..."
307316
mkdir -p build && cd build/
308317

@@ -328,7 +337,15 @@ cmake -G Ninja \
328337
-DTorch_DIR="${FEEDSIM_THIRD_PARTY_SRC}/libtorch/share/cmake/Torch" \
329338
../
330339

331-
ninja-build -j 1
340+
# Third-party deps (fmt, folly, fizz, wangle, mvfst, fbthrift) are built by this
341+
# ninja step via ExternalProject_Add. Their build order is declared in
342+
# third_party/src/CMake/build-*.cmake via add_dependencies() and
343+
# ExternalProject_Add_StepDependencies(), so ninja respects the DAG under -jN.
344+
# Use nproc/2 to avoid OOM during heavy template-instantiation steps.
345+
NINJA_JOBS="${BP_NINJA_JOBS:-$(( $(nproc) / 2 ))}"
346+
[ "$NINJA_JOBS" -lt 1 ] && NINJA_JOBS=1
347+
msg "Building FeedSim with ninja -j${NINJA_JOBS} (set BP_NINJA_JOBS to override)"
348+
ninja-build -j"${NINJA_JOBS}"
332349

333350
msg ""
334351
msg "=== FeedSim Installation Complete ==="

packages/feedsim/install_feedsim_aarch64_ubuntu.sh

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,6 @@ else
142142
msg "[SKIPPED] libevent-2.1.11-stable"
143143
fi
144144

145-
# Installing openssl
146-
if ! [ -d "openssl" ]; then
147-
mkdir -p build-deps
148-
git clone --branch OpenSSL_1_1_1b --depth 1 https://github.com/openssl/openssl.git
149-
cd "openssl"
150-
./config --prefix="${FEEDSIM_THIRD_PARTY_SRC}/build-deps"
151-
make -j"$(nproc)"
152-
make install
153-
cd ../
154-
else
155-
msg "[SKIPPED] openssl"
156-
fi
157-
158145
msg "Installing third-party dependencies ... DONE"
159146

160147
# Installing LibTorch via pip for aarch64
@@ -268,6 +255,16 @@ fi
268255
# which uses io_uring zero-copy RX APIs requiring liburing >= 2.6
269256
apt remove -y liburing-dev 2>/dev/null || true
270257

258+
# Generate feature extractor variants (1M+ unique functions for I-cache pressure)
259+
msg "Generating feature extractor variants..."
260+
CODEGEN_DIR="${FEEDSIM_ROOT_SRC}/src/workloads/ranking/feature_extractors/generated"
261+
if [ -f "${CODEGEN_DIR}/generate_extractors.py" ]; then
262+
python3 "${CODEGEN_DIR}/generate_extractors.py" --output-dir "${CODEGEN_DIR}"
263+
msg "Feature extractor codegen complete"
264+
else
265+
msg "[SKIPPED] No codegen script found at ${CODEGEN_DIR}/generate_extractors.py"
266+
fi
267+
271268
mkdir -p build && cd build/
272269

273270
# Build FeedSim with DLRM support

packages/feedsim/install_feedsim_ubuntu.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ if [ -f "third_party/fizz/fizz/tool/FizzServerCommand.cpp" ]; then
235235
sed -i 's/EVP_PKEY_cmp(pubKey.get(), key.get()) == 1/EVP_PKEY_eq(pubKey.get(), key.get())/g' "third_party/fizz/fizz/tool/FizzServerCommand.cpp"
236236
fi
237237

238+
# Generate feature extractor variants (1M+ unique functions for I-cache pressure)
239+
msg "Generating feature extractor variants..."
240+
CODEGEN_DIR="${FEEDSIM_ROOT_SRC}/src/workloads/ranking/feature_extractors/generated"
241+
if [ -f "${CODEGEN_DIR}/generate_extractors.py" ]; then
242+
python3 "${CODEGEN_DIR}/generate_extractors.py" --output-dir "${CODEGEN_DIR}"
243+
msg "Feature extractor codegen complete"
244+
else
245+
msg "[SKIPPED] No codegen script found at ${CODEGEN_DIR}/generate_extractors.py"
246+
fi
247+
238248
mkdir -p build && cd build/
239249

240250
# Build FeedSim with DLRM support

0 commit comments

Comments
 (0)