CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
152 lines
5.6 KiB
Python
Executable File
152 lines
5.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
# See https://llvm.org/LICENSE.txt for license information.
|
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
#
|
|
# ==------------------------------------------------------------------------==#
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
|
|
# The purpose of this script is to measure the performance effect
|
|
# of an lld change in a statistically sound way, automating all the
|
|
# tedious parts of doing so. It copies the test case into /tmp as well as
|
|
# running the test binaries from /tmp to reduce the influence on the test
|
|
# machine's storage medium on the results. It accounts for measurement
|
|
# bias caused by binary layout (using the --randomize-section-padding
|
|
# flag to link the test binaries) and by environment variable size
|
|
# (implemented by hyperfine [1]). Runs of the base and test case are
|
|
# interleaved to account for environmental factors which may influence
|
|
# the result due to the passage of time. The results of running hyperfine
|
|
# are collected into a results.csv file in the output directory and may
|
|
# be analyzed by the user with a tool such as ministat.
|
|
#
|
|
# Requirements: Linux host, hyperfine [2] in $PATH, run from a build directory
|
|
# configured to use ninja and a recent version of lld that supports
|
|
# --randomize-section-padding, /tmp is tmpfs.
|
|
#
|
|
# [1] https://github.com/sharkdp/hyperfine/blob/3cedcc38d0c430cbf38b4364b441c43a938d2bf3/src/util/randomized_environment_offset.rs#L1
|
|
# [2] https://github.com/sharkdp/hyperfine
|
|
#
|
|
# Example invocation for comparing the performance of the current commit
|
|
# against the previous commit which is treated as the baseline, without
|
|
# linking debug info:
|
|
#
|
|
# lld/utils/run_benchmark.py \
|
|
# --base-commit HEAD^ \
|
|
# --test-commit HEAD \
|
|
# --test-case lld/utils/speed-test-reproducers/result/firefox-x64/response.txt \
|
|
# --num-iterations 512 \
|
|
# --num-binary-variants 16 \
|
|
# --output-dir outdir \
|
|
# --ldflags=-S
|
|
#
|
|
# Then this bash command will compare the real time of the base and test cases.
|
|
#
|
|
# ministat -A \
|
|
# <(grep lld-base outdir/results.csv | cut -d, -f2) \
|
|
# <(grep lld-test outdir/results.csv | cut -d, -f2)
|
|
|
|
# We don't want to copy stat() information when we copy the reproducer
|
|
# to the temporary directory. Files in the Nix store are read-only so this will
|
|
# cause trouble when the linker writes the output file and when we want to clean
|
|
# up the temporary directory. Python doesn't provide a way to disable copying
|
|
# stat() information in shutil.copytree so we just monkeypatch shutil.copystat
|
|
# to do nothing.
|
|
shutil.copystat = lambda *args, **kwargs: 0
|
|
|
|
parser = argparse.ArgumentParser(prog="benchmark_change.py")
|
|
parser.add_argument("--base-commit", required=True)
|
|
parser.add_argument("--test-commit", required=True)
|
|
parser.add_argument("--test-case", required=True)
|
|
parser.add_argument("--num-iterations", type=int, required=True)
|
|
parser.add_argument("--num-binary-variants", type=int, required=True)
|
|
parser.add_argument("--output-dir", required=True)
|
|
parser.add_argument("--ldflags", required=False)
|
|
args = parser.parse_args()
|
|
|
|
test_dir = tempfile.mkdtemp()
|
|
print(f"Using {test_dir} as temporary directory")
|
|
|
|
os.makedirs(args.output_dir)
|
|
print(f"Using {args.output_dir} as output directory")
|
|
|
|
|
|
def extract_link_command(target):
|
|
# We assume that the last command printed by "ninja -t commands" containing a
|
|
# "-o" flag is the link command (we need to check for -o because subsequent
|
|
# commands create symlinks for ld.lld and so on). This is true for CMake and
|
|
# gn.
|
|
link_command = None
|
|
for line in subprocess.Popen(
|
|
["ninja", "-t", "commands", target], stdout=subprocess.PIPE
|
|
).stdout.readlines():
|
|
commands = line.decode("utf-8").split("&&")
|
|
for command in commands:
|
|
if " -o " in command:
|
|
link_command = command.strip()
|
|
return link_command
|
|
|
|
|
|
def generate_binary_variants(case_name):
|
|
subprocess.run(["ninja", "lld"])
|
|
link_command = extract_link_command("lld")
|
|
|
|
for i in range(0, args.num_binary_variants):
|
|
print(f"Generating binary variant {i} for {case_name} case")
|
|
command = f"{link_command} -o {test_dir}/lld-{case_name}{i} -Wl,--randomize-section-padding={i}"
|
|
subprocess.run(command, check=True, shell=True)
|
|
|
|
|
|
# Make sure that there are no local changes.
|
|
subprocess.run(["git", "diff", "--exit-code", "HEAD"], check=True)
|
|
|
|
# Resolve the base and test commit, since if they are relative to HEAD we will
|
|
# check out the wrong commit below.
|
|
resolved_base_commit = subprocess.check_output(
|
|
["git", "rev-parse", args.base_commit]
|
|
).strip()
|
|
resolved_test_commit = subprocess.check_output(
|
|
["git", "rev-parse", args.test_commit]
|
|
).strip()
|
|
|
|
test_case_dir = os.path.dirname(args.test_case)
|
|
test_case_respfile = os.path.basename(args.test_case)
|
|
|
|
test_dir_test_case_dir = f"{test_dir}/testcase"
|
|
shutil.copytree(test_case_dir, test_dir_test_case_dir)
|
|
|
|
subprocess.run(["git", "checkout", resolved_base_commit], check=True)
|
|
generate_binary_variants("base")
|
|
|
|
subprocess.run(["git", "checkout", resolved_test_commit], check=True)
|
|
generate_binary_variants("test")
|
|
|
|
|
|
def hyperfine_link_command(case_name):
|
|
return f'../lld-{case_name}$(({{iter}}%{args.num_binary_variants})) -flavor ld.lld @{test_case_respfile} {args.ldflags or ""}'
|
|
|
|
|
|
results_csv = f"{args.output_dir}/results.csv"
|
|
subprocess.run(
|
|
[
|
|
"hyperfine",
|
|
"--export-csv",
|
|
os.path.abspath(results_csv),
|
|
"-P",
|
|
"iter",
|
|
"0",
|
|
str(args.num_iterations - 1),
|
|
hyperfine_link_command("base"),
|
|
hyperfine_link_command("test"),
|
|
],
|
|
check=True,
|
|
cwd=test_dir_test_case_dir,
|
|
)
|
|
|
|
shutil.rmtree(test_dir)
|