4 changed files with 172 additions and 0 deletions
@ -0,0 +1,34 @@ |
|||||||
|
# Benchmarking |
||||||
|
|
||||||
|
On Linux, use the `tools/linux_reduced_cpu_variance_run.sh` wrapper |
||||||
|
to [reduce CPU variance](https://google.github.io/benchmark/reducing_variance.html). |
||||||
|
|
||||||
|
Timedemo: |
||||||
|
|
||||||
|
```bash |
||||||
|
tools/linux_reduced_cpu_variance_run.sh tools/measure_timedemo_performance.py -n 5 --binary build-rel/devilutionx |
||||||
|
``` |
||||||
|
|
||||||
|
Individual benchmarks (built when `BUILD_TESTING` is `ON`): |
||||||
|
|
||||||
|
```bash |
||||||
|
tools/build_and_run_benchmark.py clx_render_benchmark |
||||||
|
``` |
||||||
|
|
||||||
|
You can pass arguments to the benchmark binary with `--`, e.g.: |
||||||
|
|
||||||
|
```bash |
||||||
|
tools/build_and_run_benchmark.py clx_render_benchmark -- --benchmark_repetitions=5 |
||||||
|
``` |
||||||
|
|
||||||
|
The `tools/build_and_run_benchmark.py` script basically does something like this: |
||||||
|
|
||||||
|
```bash |
||||||
|
{ [ -d build-reld ] || cmake -S. -Bbuild-reld -DCMAKE_BUILD_TYPE=RelWithDebInfo; } && \ |
||||||
|
cmake --build build-reld --target clx_render_benchmark && \ |
||||||
|
tools/linux_reduced_cpu_variance_run.sh build-reld/clx_render_benchmark |
||||||
|
``` |
||||||
|
|
||||||
|
See `tools/build_and_run_benchmark.py --help` for more information. |
||||||
|
|
||||||
|
You can also [profile](profiling-linux.md) your benchmarks. |
||||||
@ -0,0 +1,94 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
|
||||||
|
import subprocess |
||||||
|
import argparse |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import pathlib |
||||||
|
import shlex |
||||||
|
import platform |
||||||
|
|
||||||
|
_PROFILE = "/tmp/out.profile" |
||||||
|
|
||||||
|
|
||||||
|
def run(*args: list[str], env: dict[str, str] | None = None): |
||||||
|
print( |
||||||
|
"+", |
||||||
|
*(map(shlex.quote, [f"{k}={v}" for k, v in env.items()] if env else [])), |
||||||
|
*map(shlex.quote, args), |
||||||
|
file=sys.stderr, |
||||||
|
) |
||||||
|
full_env = None |
||||||
|
if env: |
||||||
|
full_env = os.environ.copy() |
||||||
|
for k, v in env.items(): |
||||||
|
full_env[k] = v |
||||||
|
subprocess.run(args, stdout=sys.stdout, stderr=sys.stderr, check=True, env=full_env) |
||||||
|
|
||||||
|
|
||||||
|
def nproc(): |
||||||
|
return len(os.sched_getaffinity(0)) |
||||||
|
|
||||||
|
|
||||||
|
def maybe_create_build_dir(dir: str, args: list[str]): |
||||||
|
if os.path.isdir(dir): |
||||||
|
return |
||||||
|
print("Creating build directory at ", dir, file=sys.stderr) |
||||||
|
run("cmake", "-S.", f"-B{dir}", "-DCMAKE_BUILD_TYPE=RelWithDebInfo", *args) |
||||||
|
|
||||||
|
|
||||||
|
def build_target(dir: str, target: str): |
||||||
|
run("cmake", "--build", dir, "-j", str(nproc()), "--target", target) |
||||||
|
|
||||||
|
|
||||||
|
def run_benchmark(dir: str, target: str, benchmark_args: list[str], gperf: bool): |
||||||
|
args = [] |
||||||
|
if platform.system() == "Linux": |
||||||
|
args.append("tools/linux_reduced_cpu_variance_run.sh") |
||||||
|
env = None |
||||||
|
if gperf: |
||||||
|
env: dict[str, str] = {"CPUPROFILE": _PROFILE} |
||||||
|
if not "CPUPROFILE_FREQUENCY" in env: |
||||||
|
env["CPUPROFILE_FREQUENCY"] = "1000" |
||||||
|
run(*args, f"{dir}/{target}", *benchmark_args, env=env) |
||||||
|
|
||||||
|
|
||||||
|
def run_pprof(dir: str, target: str, port: int): |
||||||
|
run("pprof", f"--http=localhost:{port}", f"{dir}/{target}", _PROFILE) |
||||||
|
|
||||||
|
|
||||||
|
def main(): |
||||||
|
os.chdir(pathlib.Path(__file__).resolve().parent.parent) |
||||||
|
parser = argparse.ArgumentParser(description="Builds and runs a benchmark") |
||||||
|
parser.add_argument("-B", "--build", help="build directory") |
||||||
|
parser.add_argument( |
||||||
|
"--gperf", action=argparse.BooleanOptionalAction, help="profile with gperftools" |
||||||
|
) |
||||||
|
parser.add_argument("--port", type=int, default=1337, help="pprof server port") |
||||||
|
parser.add_argument("target", help="benchmark target") |
||||||
|
parser.add_argument( |
||||||
|
"benchmark_args", |
||||||
|
nargs="*", |
||||||
|
help="arguments passed to the benchmark binary", |
||||||
|
) |
||||||
|
args = parser.parse_args() |
||||||
|
build = args.build |
||||||
|
if not build: |
||||||
|
build = "build-gperf" if args.gperf else "build-reld" |
||||||
|
configure_args = [] |
||||||
|
if args.gperf: |
||||||
|
configure_args.append("-DGPERF=ON") |
||||||
|
try: |
||||||
|
maybe_create_build_dir(build, configure_args) |
||||||
|
build_target(build, args.target) |
||||||
|
run_benchmark(build, args.target, args.benchmark_args, args.gperf) |
||||||
|
if args.gperf: |
||||||
|
run_pprof(build, args.target, args.port) |
||||||
|
except subprocess.CalledProcessError as e: |
||||||
|
print("Error:", e.cmd[0], "failed", file=sys.stderr) |
||||||
|
return e.returncode |
||||||
|
except KeyboardInterrupt as e: |
||||||
|
return 1 |
||||||
|
|
||||||
|
|
||||||
|
main() |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
# See https://google.github.io/benchmark/reducing_variance.html |
||||||
|
set -x |
||||||
|
|
||||||
|
sudo cpupower frequency-set --governor performance 1>/dev/null |
||||||
|
echo 0 | sudo tee /sys/devices/system/cpu/cpufreq/boost 1>/dev/null |
||||||
|
|
||||||
|
taskset -c 0 "$@" |
||||||
|
{ set +x; } 2> /dev/null |
||||||
|
result=$? |
||||||
|
set -x |
||||||
|
|
||||||
|
sudo cpupower frequency-set --governor ondemand 1>/dev/null |
||||||
|
echo 1 | sudo tee /sys/devices/system/cpu/cpufreq/boost 1>/dev/null |
||||||
|
|
||||||
|
exit "$result" |
||||||
Loading…
Reference in new issue