Skip to content

Commit eea0f5b

Browse files
Reolving mediawiki runtime and score variations (facebookresearch#158)
Summary: Pull Request resolved: facebookresearch#158 Both Mediawiki and Mediawiki_mini were exhibiting variations in execution time and achieved scores, particularly on ARM machines. Such variations in benchmarking can lead to incorrect conclusions for anyone conducting experiments. Since one of the purposes of DCPerf_mini is to be used in machine health checks, and a key requirement for this is to maintain variation below 3%, it is essential to address this inconsistency. This diff addresses the variation by resolving three sources of randomness: Randomness in Load Generation: By applying a fixed seed, we can control the randomness in load generation. Since the seed affects the score, this diff allows the DCPerf user to configure the seed or use the current time (os.time) as the seed if a negative seed is provided. Random and Prolonged Warmup Times: This issue is resolved by addressing the scenario where overlapping translation frequencies and the duration of each load generated for warmup result in a pending translation every time the server checks the warmup status. By distinguishing when the server is waiting for a pending translation, we can choose to wait rather than initiate a new load. Benchmark Execution Method: Running the benchmark directly from a script, as opposed to executing the command from a subprocess in Python, results in a simpler trace being observed by HHVM. This leads to more efficient translation. Differential Revision: D77906847
1 parent 3ff6e68 commit eea0f5b

6 files changed

Lines changed: 114 additions & 40 deletions

File tree

benchpress/config/jobs.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
- benchmark: oss_performance_mediawiki
3434
name: oss_performance_mediawiki_mini
3535
description: Default run for oss_performance_mediawiki
36+
execute_cmd_from_file: true
3637
args:
3738
- '-r/usr/local/hphpi/legacy/bin/hhvm'
3839
- '-nnginx'
@@ -49,21 +50,24 @@
4950
- '--shorten-health-check'
5051
- '--skip-single-request-warmup'
5152
- '--skip-sleep-between-warmups'
52-
- '--hhvm-extra-arguments="-vEval.JitRetranslateAllSeconds={warmup_seconds}"'
53+
- '--hhvm-extra-arguments="-vEval.JitRetranslateAllSeconds={translation_interval_seconds}"'
5354
- '--num-multi-req-warmups={num_multi_req_warmups}'
55+
- '--no-load-if-pending-translate'
5456
- '--first-multi-warmup-duration={first_multi_warmup_duration}'
5557
- '--subseq-multi-warmup-duration={subseq_multi_warmup_duration}'
58+
- '--load-gen-seed={load_generator_seed}'
5659
- '{extra_args}'
5760
vars:
5861
- 'load_generator=wrk'
5962
- 'lg_path=benchmarks/oss_performance_mediawiki/wrk/wrk'
6063
- 'duration=4s'
61-
- 'timeout=20s'
62-
- 'warmup_seconds=5'
63-
- 'first_multi_warmup_duration=10'
64-
- 'subseq_multi_warmup_duration=20'
64+
- 'timeout=60s'
65+
- 'translation_interval_seconds=5'
66+
- 'first_multi_warmup_duration=25' # long enough to get a high score
67+
- 'subseq_multi_warmup_duration=5'
6568
- 'temp_dir=default_no_temp_dir'
66-
- 'num_multi_req_warmups=1'
69+
- 'num_multi_req_warmups=-1' # -1 means the warmup status will define the number of iterations
70+
- 'load_generator_seed=1000'
6771
- 'extra_args='
6872
hooks:
6973
- hook: copymove

benchpress/lib/job.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import errno
88
import logging
9+
import os
910
import subprocess
1011
import sys
1112
import tempfile
@@ -89,6 +90,7 @@ def __init__(self, job_config, benchmark_config, toolchain_config) -> None:
8990
# if this option is a string, the output will be written to the file
9091
# named by this value
9192
self.tee_output = job_config.get("tee_output", False)
93+
self.execute_cmd_from_file = job_config.get("execute_cmd_from_file", False)
9294

9395
self.tags = formalize_tags([benchmark_config, job_config])
9496

@@ -199,6 +201,22 @@ def dry_run(self, role=None, role_input=None):
199201
self.check_role(role, role_input)
200202
return get_safe_cmd([self.binary] + self.args)
201203

204+
def get_file_based_cmd(self, role=None, role_input=None, fp=None):
205+
"""Dump the run command in a file and execute it."""
206+
logger.info('Starting "{}"'.format(self.name))
207+
cmd = self.dry_run(role, role_input)
208+
click.echo("Job execution command: {}".format(cmd))
209+
# add string to cmd so that it dumps the stdout and strerr to different files
210+
# cmd = cmd + " > benchpress_run_output.txt 2> benchpress_run_error.txt"
211+
212+
# write the command to a file
213+
fp.write(b"#!/bin/bash\n")
214+
fp.write((" ".join(cmd)).encode("utf-8"))
215+
fp.write(b"\n") # Add a newline at the end
216+
fp.flush() # Ensure the file is written to disk
217+
os.chmod(fp.name, 0o755)
218+
return [str(fp.name)]
219+
202220
def run(self, role=None, role_input=None):
203221
"""Run the benchmark and return the metrics that are reported.
204222
check if user type role correctly
@@ -207,14 +225,25 @@ def run(self, role=None, role_input=None):
207225

208226
try:
209227
logger.info('Starting "{}"'.format(self.name))
210-
cmd = get_safe_cmd([self.binary] + self.args)
211-
click.echo("Job execution command: {}".format(cmd))
212-
process = subprocess.Popen(
213-
cmd,
214-
stdout=subprocess.PIPE,
215-
stderr=subprocess.PIPE,
216-
text=True,
217-
)
228+
if self.execute_cmd_from_file:
229+
fp = tempfile.NamedTemporaryFile(delete=False)
230+
cmd = self.get_file_based_cmd(role, role_input, fp)
231+
fp.close() # Close the file before executing it
232+
process = subprocess.Popen(
233+
cmd,
234+
stdout=subprocess.PIPE,
235+
stderr=subprocess.PIPE,
236+
text=True,
237+
)
238+
else:
239+
cmd = get_safe_cmd([self.binary] + self.args)
240+
click.echo("Job execution command: {}".format(cmd))
241+
process = subprocess.Popen(
242+
cmd,
243+
stdout=subprocess.PIPE,
244+
stderr=subprocess.PIPE,
245+
text=True,
246+
)
218247
stdout_storage = tempfile.TemporaryFile(
219248
mode="w+",
220249
encoding="utf-8",

packages/mediawiki/0007-oss-performance-more-warmup-options.diff

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,66 +41,74 @@ index 420014d..d8e45df 100644
4141
public function queueEmpty(): bool { return true; }
4242
}
4343
diff --git a/base/PerfOptions.php b/base/PerfOptions.php
44-
index add42c7..7603fdf 100644
44+
index add42c7..dfe40a6 100644
4545
--- a/base/PerfOptions.php
4646
+++ b/base/PerfOptions.php
47-
@@ -58,6 +58,15 @@ final class PerfOptions {
47+
@@ -58,6 +58,17 @@ final class PerfOptions {
4848
public bool $noTimeLimit = false;
4949
public bool $runAsRoot = false;
5050

5151
+ // Options for shortening the execution time
5252
+ public bool $shortenHealthCheck = false;
5353
+ public bool $skipSingleRequestWarmup = false;
5454
+ public bool $skipSleepBetweenWarmups = false;
55+
+ public bool $noLoadIfPendingTranslate = false;
5556
+ public int $firstMultiWarmupDuration = 60;
5657
+ public int $subseqMultiWarmupDuration = 10;
58+
+ public int $loadGenSeed = 1000;
5759
+ public int $numMultiReqWarmups = -1;
5860
+
5961
+
6062
// Pause once benchmarking is complete to allow for manual inspection of the
6163
// HHVM or PHP process.
6264
public bool $waitAtEnd = false;
63-
@@ -105,6 +114,7 @@ final class PerfOptions {
65+
@@ -105,6 +116,7 @@ final class PerfOptions {
6466
public float $delayProcessLaunch; // secs to wait after start process
6567
public float $delayCheckHealth; // secs to wait before hit /check-health
6668

6769
+
6870
//
6971
// Maximum wait times, as for example given to file_get_contents
7072
// or the configuration file for nginx. These times may be truncated
71-
@@ -221,6 +231,12 @@ final class PerfOptions {
73+
@@ -221,6 +233,14 @@ final class PerfOptions {
7274
'memcached-threads:',
7375
'no-memcached', // do not use memcached (even if target supports it)
7476
'scale-out:',
7577
+ 'shorten-health-check',
7678
+ 'skip-single-request-warmup',
7779
+ 'skip-sleep-between-warmups',
80+
+ 'no-load-if-pending-translate',
7881
+ 'first-multi-warmup-duration:',
7982
+ 'subseq-multi-warmup-duration:',
83+
+ 'load-gen-seed:',
8084
+ 'num-multi-req-warmups:'
8185
};
8286
$targets = $this->getTargetDefinitions()->keys();
8387
$def->addAll($targets);
84-
@@ -312,6 +328,14 @@ final class PerfOptions {
88+
@@ -312,6 +332,18 @@ final class PerfOptions {
8589
$this->applyPatches = $this->getBool('apply-patches');
8690
$this->useMemcached = !$this->getBool('no-memcached');
8791

8892
+ $this->shortenHealthCheck = $this->getBool('shorten-health-check');
8993
+ $this->skipSingleRequestWarmup = $this->getBool('skip-single-request-warmup');
9094
+ $this->skipSleepBetweenWarmups = $this->getBool('skip-sleep-between-warmups');
95+
+ $this->noLoadIfPendingTranslate = $this->getBool('no-load-if-pending-translate');
9196
+ $this->firstMultiWarmupDuration = $this->getInt('first-multi-warmup-duration', 60);
9297
+ $this->subseqMultiWarmupDuration = $this->getInt('subseq-multi-warmup-duration', 10);
98+
+ $this->loadGenSeed = $this->getInt('load-gen-seed', 1000);
99+
+
100+
+
93101
+ $this->numMultiReqWarmups = $this->getInt('num-multi-req-warmups', -1);
94102
+
95103
+
96104
$fraction = $this->getFloat('cpu-fraction', 1.0);
97105
if ($fraction !== 1.0) {
98106
$this->cpuBind = true;
99107
diff --git a/base/PerfRunner.php b/base/PerfRunner.php
100-
index 5201a2d..7c44632 100644
108+
index 5201a2d..478edd3 100644
101109
--- a/base/PerfRunner.php
102110
+++ b/base/PerfRunner.php
103-
@@ -219,27 +219,53 @@ final class PerfRunner {
111+
@@ -219,27 +219,71 @@ final class PerfRunner {
104112
exec($options->scriptBeforeWarmup);
105113
}
106114

@@ -113,43 +121,61 @@ index 5201a2d..7c44632 100644
113121

114122
if (!$options->skipWarmUp) {
115123
- self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines);
116-
+ self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines, $options->firstMultiWarmupDuration);
124+
+ self::RunLoadGenerator(RequestModes::WARMUP_MULTI,
125+
+ $options, $target, $engines, $options->firstMultiWarmupDuration);
117126
} else {
118127
- self::PrintProgress('Skipping multi request warmup');
119128
+ self::PrintProgress('Skipping first multi request warmup');
120-
}
129+
+ }
121130
+ if ($options->numMultiReqWarmups >= 0){
122-
+ self::PrintProgress('Deterministic warmpup with '.($options->numMultiReqWarmups). ' iterations');
131+
+ self::PrintProgress('Deterministic warmpup with '
132+
+ .($options->numMultiReqWarmups). ' iterations');
123133
+ for ($i = 0; $i < $options->numMultiReqWarmups; $i++){
124-
+ self::PrintProgress(' Performing '. ($i+1). 'th multi-request warmup iteration out of '.($options->numMultiReqWarmups). ' iterations');
134+
+ self::PrintProgress(' Performing '. ($i+1).
135+
+ 'th multi-request warmup iteration out of '.
136+
+ ($options->numMultiReqWarmups));
125137
+ if (!$options->skipSleepBetweenWarmups){
126138
+ sleep(3);
127139
+ }
128-
+ self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines, $options->subseqMultiWarmupDuration);
129-
+
140+
+ self::RunLoadGenerator(RequestModes::WARMUP_MULTI,
141+
+ $options, $target, $engines, $options->subseqMultiWarmupDuration);
130142
+ $status_message = null;
131143
+ if (self::NeedsRetranslatePause($engines, inout $status_message)){
132-
+ self::PrintProgress('Server is not done warming up. Status: ' . ($status_message ?? 'Unknown'));
144+
+ self::PrintProgress("Server is not done warming up.\n Status: " .
145+
+ ($status_message ?? 'Unknown'));
133146
+ }else{
134147
+ self::PrintProgress('Server is done warming up.');
135148
+ }
136149
+ }
137-
138-
- while (!$options->skipWarmUp && self::NeedsRetranslatePause($engines)) {
139-
- self::PrintProgress('Extending warmup, server is not done warming up.');
140-
- sleep(3);
141-
- self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines, 10);
142-
+ }
150+
+
151+
}
143152
+ else{
144153
+ while (!$options->skipWarmUp) {
145154
+ $status_message = null;
146155
+ if (self::NeedsRetranslatePause($engines, inout $status_message)) {
147-
+ self::PrintProgress('Extending warmup, server is not done warming up. Status: ' . ($status_message ?? 'Unknown'));
156+
+ self::PrintProgress("Extending warmup ... \n server is not done warming up.
157+
+ \n Status: " . ($status_message ?? 'Unknown'));
148158
+ if (!$options->skipSleepBetweenWarmups){
149159
+ sleep(3);
150160
+ }
151-
+ self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines, $options->subseqMultiWarmupDuration);
152-
+ } else {
161+
+ # if $status_message contains Waiting on retranslateAll(),
162+
+ # we might only need to wait a bit longer
163+
+ if ($options->noLoadIfPendingTranslate &&
164+
+ $status_message !== null &&
165+
+ strpos($status_message, "Waiting on retranslateAll()") !== false){
166+
+ self::PrintProgress('no new load if pending translate is set,
167+
+ waiting for retranslateAll');
168+
+ }else{
169+
+ self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines,
170+
+ $options->subseqMultiWarmupDuration);
171+
+ }
172+
173+
- while (!$options->skipWarmUp && self::NeedsRetranslatePause($engines)) {
174+
- self::PrintProgress('Extending warmup, server is not done warming up.');
175+
- sleep(3);
176+
- self::RunLoadGenerator(RequestModes::WARMUP_MULTI, $options, $target, $engines, 10);
177+
+ }else {
178+
+ self::PrintProgress('Server is done warming up');
153179
+ break;
154180
+ }
155181
+ }
@@ -163,7 +189,7 @@ index 5201a2d..7c44632 100644
163189
sleep(10);
164190
}
165191

166-
@@ -327,9 +353,9 @@ final class PerfRunner {
192+
@@ -327,9 +371,9 @@ final class PerfRunner {
167193
return $combined_stats;
168194
}
169195

packages/mediawiki/Wrk.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ public function __construct(
2626
} else {
2727
$this->logfile = tempnam($options->tempDir, 'wrk_warmup');
2828
}
29+
if ($this->options->loadGenSeed > 0) {
30+
# Create template path by adding .template suffix
31+
$template_path = $this->script . '.template';
32+
33+
# Read the content from the template file
34+
$script_content = file_get_contents($template_path);
35+
36+
# Find and replace the math.randomseed line with our custom seed value
37+
$pattern = '/math\.randomseed\s*\(\s*os\.time\s*\(\s*\)\s*\)/';
38+
$replacement = "math.randomseed(".((string)$this->options->loadGenSeed).")";
39+
$modified_content = preg_replace($pattern, $replacement, $script_content);
40+
41+
# Write the modified content to the script file
42+
file_put_contents($this->script, $modified_content);
43+
}
2944
}
3045

3146
public function start(): void {

packages/mediawiki/install_oss_performance_mediawiki.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ git apply --check "${TEMPLATES_DIR}/0005-scale-out-memcached.diff" \
140140
# Copy wrk related stuff
141141
cp "${TEMPLATES_DIR}/Wrk.php" ./base/Wrk.php
142142
cp "${TEMPLATES_DIR}/WrkStats.php" ./base/WrkStats.php
143-
cp "${TEMPLATES_DIR}/multi-request-txt.lua" ./scripts/multi-request-txt.lua
143+
cp "${TEMPLATES_DIR}/multi-request-txt.lua" ./scripts/multi-request-txt.lua.template
144144

145145
# shellcheck disable=SC2046
146146
curl -O https://getcomposer.org/installer

packages/mediawiki/multi-request-txt.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
-- Module instantiation
77
-- Initialize the pseudo random number generator
88
-- Resource: http://lua-users.org/wiki/MathLibraryTutorial
9-
math.randomseed(1000)
9+
math.randomseed(os.time())
1010
math.random(); math.random(); math.random()
1111

1212
-- Shuffle array

0 commit comments

Comments
 (0)