Skip to content

Commit 441e4bb

Browse files
Feedsim: removing randomness
Summary: This diff reduce score variations in feedsim by controlling random number generation. This change is critical for ensuring fair and accurate hardware performance comparisons. This is crucial for: * **Vendor Hardware Comparisons**: Eliminating noise from random variations when comparing different hardware platforms * **DCPerf Mini Reliability**: Short-duration benchmarks are especially sensitive to random variations that can skew results * **Fair Performance Evaluation**: Ensuring benchmark scores reflect true hardware performance characteristics * **Scientific Rigor**: Providing reproducible results for performance analysis and decision-making This implementation provides **granular control** over all three sources of randomness in feedsim: #### **1. NodeRank Random Number Generation (`--node_rank_seed`)** * Controls thread-local random number generators used in LeafNodeRank * Affects latency distribution sampling and general RNG operations * **Usage**: `-R {seed_value}` or `--node_rank_seed={seed_value}` #### **2. PageRank Algorithm Randomness (`--page_rank_seed`)** * Controls PageRank graph traversal and subset selection randomness * Affects graph partitioning and algorithm initialization * **Usage**: `-P {seed_value}` or `--page_rank_seed={seed_value}` #### **3. PointerChase Memory Access Patterns (`--pointer_chase_seed`)** _(New Feature)_ * Controls memory access pattern randomization in PointerChase workload * Affects cache behavior simulation and memory stress patterns * **Usage**: `-C {seed_value}` or `--pointer_chase_seed={seed_value}` Differential Revision: D83871667
1 parent 2e08c9a commit 441e4bb

10 files changed

Lines changed: 141 additions & 50 deletions

File tree

benchpress/config/jobs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,9 @@
477477
- '-S {graph_store_path}'
478478
- '-L {graph_load_path}'
479479
- '-D {queue_drain_time}'
480+
- '-R {node_rank_seed}'
481+
- '-P {page_rank_seed}'
482+
- '-C {pointer_chase_seed}'
480483
- '-N'
481484
- '{extra_args}'
482485
vars:
@@ -489,6 +492,9 @@
489492
- 'graph_store_path=default_do_not_store'
490493
- 'graph_load_path=default_do_not_load'
491494
- 'queue_drain_time=1'
495+
- 'node_rank_seed=54321'
496+
- 'page_rank_seed=12345'
497+
- 'pointer_chase_seed=98765'
492498
- 'extra_args='
493499
hooks:
494500
- hook: copymove

packages/feedsim/run.sh

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ Usage: ${0##*/} [OPTION]...
7373
-x Maximum number of warmup iterations when using QPS threshold. Default: 10
7474
-N No retry mode. Skip sleep and PID checking in load test startup, break immediately without retrying.
7575
-D Drain time in seconds. Time to wait for queue to drain after experiments. Default: 5
76+
-R Seed for LeafNodeRank random number generator. If not provided, current time will be used.
77+
-P Seed for PageRank random number generator. If not provided, current time will be used.
78+
-C Seed for PointerChase random number generator. If not provided, current time will be used.
7679
EOF
7780
}
7881

@@ -152,6 +155,15 @@ main() {
152155
local queue_drain_time
153156
queue_drain_time="5"
154157

158+
local leafnoderank_seed
159+
leafnoderank_seed=""
160+
161+
local pagerank_seed
162+
pagerank_seed=""
163+
164+
local pointerchase_seed
165+
pointerchase_seed=""
166+
155167
if [ -z "$IS_AUTOSCALE_RUN" ]; then
156168
echo > $BREPS_LFILE
157169
fi
@@ -221,6 +233,15 @@ main() {
221233
-D)
222234
queue_drain_time="$2"
223235
;;
236+
-R)
237+
leafnoderank_seed="--node_rank_seed=$2"
238+
;;
239+
-P)
240+
pagerank_seed="--page_rank_seed=$2"
241+
;;
242+
-C)
243+
pointerchase_seed="--pointer_chase_seed=$2"
244+
;;
224245
-h|--help)
225246
show_help >&2
226247
exit 1
@@ -231,7 +252,7 @@ main() {
231252
esac
232253

233254
case $1 in
234-
-t|-c|-s|-d|-p|-q|-o|-w|-i|-l|-S|-L|-r|-x|-D)
255+
-t|-c|-s|-d|-p|-q|-o|-w|-i|-l|-S|-L|-r|-x|-D|-R|-P|-C)
235256
if [ -z "$2" ]; then
236257
echo "Invalid option: '$1' requires an argument" 1>&2
237258
exit 1
@@ -270,7 +291,10 @@ main() {
270291
--min_icache_iterations="$icache_iterations" \
271292
"$store_graph" \
272293
"$load_graph" \
273-
"$instrument_graph" >> $BREPS_LFILE 2>&1 &
294+
"$instrument_graph" \
295+
"$leafnoderank_seed" \
296+
"$pagerank_seed" \
297+
"$pointerchase_seed" >> $BREPS_LFILE 2>&1 &
274298

275299
LEAF_PID=$!
276300

packages/feedsim/third_party/src/workloads/ranking/LeafNodeRank.cc

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,35 @@ void ThreadStartup(
9595
this_thread.srvIOThreadPool = srvIOThreadPool;
9696
this_thread.ioThreadPool = ioThreadPool;
9797
this_thread.timekeeperPool = timekeeperPool;
98+
unsigned noderank_seed;
99+
if (args.node_rank_seed_given) {
100+
noderank_seed = static_cast<unsigned>(args.node_rank_seed_arg);
101+
} else {
102+
noderank_seed = std::chrono::system_clock::now().time_since_epoch().count();
103+
}
104+
105+
unsigned page_rank_seed;
106+
if (args.page_rank_seed_given) {
107+
page_rank_seed = static_cast<unsigned>(args.page_rank_seed_arg);
108+
} else {
109+
page_rank_seed = std::chrono::system_clock::now().time_since_epoch().count();
110+
}
111+
112+
unsigned pointer_chase_seed;
113+
if (args.pointer_chase_seed_given) {
114+
pointer_chase_seed = static_cast<unsigned>(args.pointer_chase_seed_arg);
115+
} else {
116+
pointer_chase_seed =
117+
std::chrono::system_clock::now().time_since_epoch().count();
118+
}
119+
98120
this_thread.page_ranker = std::make_unique<ranking::dwarfs::PageRank>(
99-
std::move(graph), args.cpu_threads_arg);
121+
std::move(graph), args.cpu_threads_arg, page_rank_seed);
100122
this_thread.icache_buster =
101123
std::make_unique<ICacheBuster>(kNumICacheBusterMethods);
102-
this_thread.pointer_chaser =
103-
std::make_unique<search::PointerChase>(kPointerChaseSize);
104-
105-
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
106-
this_thread.rng.seed(seed);
124+
this_thread.pointer_chaser = std::make_unique<search::PointerChase>(
125+
kPointerChaseSize, pointer_chase_seed);
126+
this_thread.rng.seed(noderank_seed);
107127

108128
const double alpha = 0.7;
109129
const double beta = 20000;
@@ -319,8 +339,12 @@ int main(int argc, char** argv) {
319339
auto start_load = std::chrono::steady_clock::now();
320340
g_shared_graph = params.loadGraphFromFile(args.load_graph_arg);
321341
auto end_load = std::chrono::steady_clock::now();
322-
auto load_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_load - start_load).count();
323-
std::cout << "Graph loading time: " << load_duration << " ms" << std::endl;
342+
auto load_duration =
343+
std::chrono::duration_cast<std::chrono::milliseconds>(
344+
end_load - start_load)
345+
.count();
346+
std::cout << "Graph loading time: " << load_duration << " ms"
347+
<< std::endl;
324348
} else {
325349
g_shared_graph = params.loadGraphFromFile(args.load_graph_arg);
326350
}
@@ -329,15 +353,23 @@ int main(int argc, char** argv) {
329353
auto start_build = std::chrono::steady_clock::now();
330354
g_shared_graph = params.buildGraph();
331355
auto end_build = std::chrono::steady_clock::now();
332-
auto build_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_build - start_build).count();
333-
std::cout << "Graph building time: " << build_duration << " ms" << std::endl;
356+
auto build_duration =
357+
std::chrono::duration_cast<std::chrono::milliseconds>(
358+
end_build - start_build)
359+
.count();
360+
std::cout << "Graph building time: " << build_duration << " ms"
361+
<< std::endl;
334362

335363
if (args.store_graph_given) {
336364
auto start_store = std::chrono::steady_clock::now();
337365
params.storeGraphToFile(g_shared_graph, args.store_graph_arg);
338366
auto end_store = std::chrono::steady_clock::now();
339-
auto store_duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_store - start_store).count();
340-
std::cout << "Graph storing time: " << store_duration << " ms" << std::endl;
367+
auto store_duration =
368+
std::chrono::duration_cast<std::chrono::milliseconds>(
369+
end_store - start_store)
370+
.count();
371+
std::cout << "Graph storing time: " << store_duration << " ms"
372+
<< std::endl;
341373
}
342374
} else {
343375
g_shared_graph = params.buildGraph();

packages/feedsim/third_party/src/workloads/ranking/LeafNodeRankCmdline.ggo

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ option "max_response_size" - "Maximum response size in bytes returned by the lea
3535
option "compression_data_size" - "Number of bytes to compress per request." int default="131072"
3636
option "rank_trials_per_thread" - "Number of iterations each CPU thread executes of rank work." int default="1"
3737
option "min_icache_iterations" - "At least this number of icache busting iteration will be executed." int default="0"
38+
option "node_rank_seed" - "Seed for random number generator. If not provided, current time will be used." long optional
39+
option "page_rank_seed" - "Seed for PageRank random number generator. If not provided, current time will be used." long optional
40+
option "pointer_chase_seed" - "Seed for PointerChase random number generator. If not provided, current time will be used." long optional
3841
option "chase_iterations" - "Number of chases to execute on handler thread." int default="5120"
3942
option "io_chase_iterations" - "Number of chases to execute on IO threads." int default="5120"
4043
option "io_time_ms" - "Milliseconds to sleep emualting I/O offcpu." int default="200"

packages/feedsim/third_party/src/workloads/ranking/dwarfs/pagerank.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ CSRGraph<int32_t> PageRankParams::makeGraphCopy(
9191
original.out_neigh(0).begin(),
9292
original.out_neigh(num_nodes - 1).end(),
9393
out_neighbors);
94-
// Set up index pointers for each node
95-
#pragma omp parallel for
94+
// Set up index pointers for each node
95+
#pragma omp parallel for
9696
for (int64_t n = 0; n < num_nodes; n++) {
97-
//TODO: check this is correct
98-
out_index[n] = out_neighbors +
97+
// TODO: check this is correct
98+
out_index[n] = out_neighbors +
9999
(original.out_neigh(n).begin() - original.out_neigh(0).begin());
100100
}
101101
// Set the last index pointer
@@ -112,8 +112,8 @@ CSRGraph<int32_t> PageRankParams::makeGraphCopy(
112112
original.in_neigh(num_nodes - 1).end(),
113113
in_neighbors);
114114

115-
// Set up index pointers for each node
116-
#pragma omp parallel for
115+
// Set up index pointers for each node
116+
#pragma omp parallel for
117117
for (int64_t n = 0; n < num_nodes; n++) {
118118
in_index[n] = in_neighbors +
119119
(original.in_neigh(n).begin() - original.in_neigh(0).begin());
@@ -177,7 +177,8 @@ void PageRankParams::storeGraphToFile(
177177
} else if (n == num_nodes) {
178178
in_offsets[n] = num_edges;
179179
} else {
180-
in_offsets[n] = original.in_neigh(n).begin() - original.in_neigh(0).begin();
180+
in_offsets[n] =
181+
original.in_neigh(n).begin() - original.in_neigh(0).begin();
181182
}
182183
}
183184
outFile.write(
@@ -222,7 +223,7 @@ CSRGraph<int32_t> PageRankParams::loadGraphFromFile(
222223

223224
// Create out_index pointer array
224225
int32_t** out_index = new int32_t*[num_nodes + 1];
225-
#pragma omp parallel for
226+
#pragma omp parallel for
226227
for (int64_t n = 0; n <= num_nodes; n++) {
227228
out_index[n] = out_neighbors + out_offsets[n];
228229
}
@@ -241,7 +242,7 @@ CSRGraph<int32_t> PageRankParams::loadGraphFromFile(
241242

242243
// Create in_index pointer array
243244
int32_t** in_index = new int32_t*[num_nodes + 1];
244-
#pragma omp parallel for
245+
#pragma omp parallel for
245246
for (int64_t n = 0; n <= num_nodes; n++) {
246247
in_index[n] = in_neighbors + in_offsets[n];
247248
}
@@ -255,8 +256,13 @@ CSRGraph<int32_t> PageRankParams::loadGraphFromFile(
255256
}
256257
}
257258

258-
PageRank::PageRank(CSRGraph<int32_t> graph, int num_pvectors_entries)
259-
: graph_(std::move(graph)), num_pvectors_entries_(num_pvectors_entries) {
259+
PageRank::PageRank(
260+
CSRGraph<int32_t> graph,
261+
int num_pvectors_entries,
262+
unsigned seed)
263+
: graph_(std::move(graph)),
264+
num_pvectors_entries_(num_pvectors_entries),
265+
seed_(seed) {
260266
const float init_score = 1.0f / graph_.num_nodes();
261267
for (int i = 0; i < num_pvectors_entries; i++) {
262268
pvector<float> scores{graph_.num_nodes(), init_score};
@@ -285,14 +291,17 @@ int PageRank::rank(
285291
0,
286292
num_nodes < graph_.num_nodes() ? graph_.num_nodes() - num_nodes
287293
: graph_.num_nodes()};
288-
std::random_device rd;
289-
std::mt19937 gen(rd());
294+
// Use the seed passed to the constructor
295+
unsigned local_seed = seed_ != 0
296+
? seed_ + thread_id
297+
: std::chrono::system_clock::now().time_since_epoch().count();
298+
std::mt19937 gen(local_seed);
290299
NodeID start = u_dist(gen);
291300

292301
const auto split_size = std::max(num_pvectors_entries_, 1);
293302
std::uniform_int_distribution<int64_t> split_dist{
294303
0, graph_.num_nodes() / split_size - 1};
295-
std::mt19937 split_gen(rd());
304+
std::mt19937 split_gen(local_seed + 1);
296305
NodeID split_start = split_dist(split_gen);
297306
NodeID split_end = split_start + (graph_.num_nodes() / split_size) - 1;
298307

packages/feedsim/third_party/src/workloads/ranking/dwarfs/pagerank.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ class PageRankParams {
4545
CSRGraph<int32_t> makeGraphCopy(const CSRGraph<int32_t>& original);
4646

4747
void storeGraphToFile(
48-
const CSRGraph<int32_t>& original,
49-
const std::string& filePath);
48+
const CSRGraph<int32_t>& original,
49+
const std::string& filePath);
5050

51-
CSRGraph<int32_t> loadGraphFromFile(
52-
const std::string& filePath);
51+
CSRGraph<int32_t> loadGraphFromFile(const std::string& filePath);
5352

5453
private:
5554
struct Impl;
@@ -63,7 +62,10 @@ class PageRank {
6362
public:
6463
constexpr static const float kDamp = 0.85;
6564

66-
explicit PageRank(CSRGraph<int32_t> graph, int num_pvectors_entries);
65+
explicit PageRank(
66+
CSRGraph<int32_t> graph,
67+
int num_pvectors_entries,
68+
unsigned seed = 0);
6769

6870
int rank(
6971
int thread_id,
@@ -75,6 +77,7 @@ class PageRank {
7577
private:
7678
CSRGraph<int32_t> graph_;
7779
int num_pvectors_entries_;
80+
unsigned seed_;
7881
folly::F14FastMap<int, pvector<float>> scores_pvectors_map_;
7982
folly::F14FastMap<int, pvector<float>> outgoing_pvectors_map_;
8083
};

packages/feedsim/third_party/src/workloads/search/LeafNode.cc

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
#include "oldisim/QueryContext.h"
2727
#include "oldisim/Util.h"
2828

29-
#include "PointerChase.h"
3029
#include "ICacheBuster.h"
3130
#include "LeafNodeCmdline.h"
31+
#include "PointerChase.h"
3232
#include "RequestTypes.h"
3333

3434
// Shared configuration flags
@@ -50,8 +50,9 @@ struct ThreadData {
5050
std::string random_string;
5151
};
5252

53-
void ThreadStartup(oldisim::NodeThread& thread,
54-
std::vector<ThreadData>& thread_data) {
53+
void ThreadStartup(
54+
oldisim::NodeThread& thread,
55+
std::vector<ThreadData>& thread_data) {
5556
ThreadData& this_thread = thread_data[thread.get_thread_num()];
5657

5758
// Initialize of I$Buster
@@ -61,8 +62,13 @@ void ThreadStartup(oldisim::NodeThread& thread,
6162
this_thread.icache_buster.reset(new ICacheBuster(kICacheBusterSize));
6263

6364
// Initialize RNG and latency sampler
64-
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
65-
this_thread.rng.seed(seed);
65+
unsigned node_seed;
66+
if (args.node_seed_given) {
67+
node_seed = static_cast<unsigned>(args.node_seed_arg);
68+
} else {
69+
node_seed = std::chrono::system_clock::now().time_since_epoch().count();
70+
}
71+
this_thread.rng.seed(node_seed);
6672

6773
const double alpha = 0.7;
6874
const double beta = 20000;
@@ -73,9 +79,10 @@ void ThreadStartup(oldisim::NodeThread& thread,
7379
this_thread.random_string = RandomString(kMaxResponseSize);
7480
}
7581

76-
void SearchRequestHandler(oldisim::NodeThread& thread,
77-
oldisim::QueryContext& context,
78-
std::vector<ThreadData>& thread_data) {
82+
void SearchRequestHandler(
83+
oldisim::NodeThread& thread,
84+
oldisim::QueryContext& context,
85+
std::vector<ThreadData>& thread_data) {
7986
ThreadData& this_thread = thread_data[thread.get_thread_num()];
8087
search::PointerChase& chaser = *this_thread.pointer_chaser;
8188
ICacheBuster& buster = *this_thread.icache_buster;
@@ -123,9 +130,11 @@ int main(int argc, char** argv) {
123130
std::bind(ThreadStartup, std::placeholders::_1, std::ref(thread_data)));
124131
server.RegisterQueryCallback(
125132
search::kSearchRequestType,
126-
std::bind(SearchRequestHandler, std::placeholders::_1,
127-
std::placeholders::_2,
128-
std::ref(thread_data)));
133+
std::bind(
134+
SearchRequestHandler,
135+
std::placeholders::_1,
136+
std::placeholders::_2,
137+
std::ref(thread_data)));
129138

130139
server.SetNumThreads(args.threads_arg);
131140
server.SetThreadPinning(!args.noaffinity_given);

packages/feedsim/third_party/src/workloads/search/LeafNodeCmdline.ggo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ option "quiet" - "Disable log messages."
2525
option "threads" - "Number of threads to use for serving." int default="1"
2626
option "port" - "Port to run server on." int default="11222"
2727
option "monitor_port" - "Port to run monitoring server on." int default="8888"
28+
option "node_seed" - "Seed for random number generator. If not provided, current time will be used." long optional
2829
option "noaffinity" - "Specify to disable thread pinning"
2930
option "noloadbalance" - "Specify to disable thread load balancing"

0 commit comments

Comments
 (0)