From 683dc073bd0fa28f49e027b178e9a0a95ffb6cd5 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 7 Aug 2026 09:09:31 -0400 Subject: [PATCH 1/5] Prototype profile-guided optimization for Ruff releases --- .github/workflows/build-binaries.yml | 13 ++ scripts/build_ruff_pgo.py | 253 +++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 scripts/build_ruff_pgo.py diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 8ecb1fa0cfc891..ce537be0227a2c 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -18,6 +18,7 @@ on: - pyproject.toml # And when we change this workflow itself... - .github/workflows/build-binaries.yml + - scripts/build_ruff_pgo.py concurrency: group: build-binaries-${{ github.ref }} @@ -239,6 +240,18 @@ jobs: with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 + - name: "Install LLVM profiling tools" + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + run: rustup component add llvm-tools-preview + - name: "Train PGO Ruff" + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + run: | + python scripts/build_ruff_pgo.py \ + --target "${{ matrix.target }}" \ + --target-dir "${{ github.workspace }}/target/ruff-pgo" \ + --train-only + + echo "RUSTFLAGS=${RUSTFLAGS:+${RUSTFLAGS} }-Cprofile-use=${{ github.workspace }}/target/ruff-pgo/ruff.profdata" >> "$GITHUB_ENV" - name: "Prep README.md" run: python scripts/transform_readme.py --target pypi - name: "Build wheels" diff --git a/scripts/build_ruff_pgo.py b/scripts/build_ruff_pgo.py new file mode 100644 index 00000000000000..cf6fb5164a5ba7 --- /dev/null +++ b/scripts/build_ruff_pgo.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""Build Ruff with profile-guided optimization using tracked repository files.""" + +from __future__ import annotations + +import argparse +import os +import shlex +import subprocess +import sys +import tempfile +from pathlib import Path + +REPOSITORY_ROOT = Path(__file__).resolve().parent.parent +CORPUS_DIRECTORIES = ( + "crates/ruff_benchmark/resources", + "scripts", + "python/ruff-ecosystem/ruff_ecosystem", +) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--target", help="Host-native Rust target triple") + parser.add_argument( + "--target-dir", + type=Path, + help="Cargo target directory (default: CARGO_TARGET_DIR or target/ruff-pgo)", + ) + parser.add_argument( + "--profile-dir", + type=Path, + help="Raw profile directory (default: /profiles)", + ) + parser.add_argument( + "--llvm-profdata", + type=Path, + help="Override the active Rust toolchain's llvm-profdata executable", + ) + parser.add_argument( + "--train-only", + action="store_true", + help="Only produce /ruff.profdata for a subsequent release build", + ) + args = parser.parse_args() + + host = rustc_host() + target = args.target or host + if target != host: + parser.error( + f"PGO training requires the host-native target {host}, got {target}" + ) + + target_dir = ( + args.target_dir + or Path( + os.environ.get("CARGO_TARGET_DIR", REPOSITORY_ROOT / "target" / "ruff-pgo") + ) + ).resolve() + profile_dir = (args.profile_dir or target_dir / "profiles").resolve() + merged_profile = target_dir / "ruff.profdata" + profiler = find_llvm_profdata(host, args.llvm_profdata) + corpus = tracked_python_files() + + profile_dir.mkdir(parents=True, exist_ok=True) + for profile in profile_dir.glob("ruff-*.profraw"): + profile.unlink() + + environment = os.environ.copy() + environment["CARGO_INCREMENTAL"] = "0" + if target.endswith("-apple-darwin"): + for variable in ("CFLAGS", "CXXFLAGS"): + environment[variable] = append_flags( + environment.get(variable), "-fno-profile-generate -fno-profile-use" + ) + + instrumented_target_dir = target_dir / "instrumented" + instrumented_environment = environment | { + "CARGO_TARGET_DIR": str(instrumented_target_dir), + "RUSTFLAGS": append_flags( + environment.get("RUSTFLAGS"), f"-Cprofile-generate={profile_dir}" + ), + } + print("Building instrumented release Ruff", flush=True) + run(cargo_command(target), environment=instrumented_environment) + + binary_name = "ruff.exe" if "windows" in target else "ruff" + instrumented_binary = instrumented_target_dir / target / "release" / binary_name + if not instrumented_binary.is_file(): + raise RuntimeError(f"Instrumented Ruff binary not found: {instrumented_binary}") + + training_environment = instrumented_environment | { + "LLVM_PROFILE_FILE": str(profile_dir / "ruff-%m-%p.profraw") + } + common_arguments = [ + "--isolated", + "--target-version", + "py314", + "--no-cache", + "--silent", + ] + print(f"Training on {len(corpus)} tracked Python files", flush=True) + run( + [str(instrumented_binary), "check", *common_arguments, "--exit-zero", *corpus], + environment=training_environment, + ) + run( + [ + str(instrumented_binary), + "format", + *common_arguments, + "--check", + *corpus, + ], + environment=training_environment, + allowed_exit_codes=(0, 1), + ) + + profiles = sorted(profile_dir.glob("ruff-*.profraw")) + if not profiles or any(profile.stat().st_size == 0 for profile in profiles): + raise RuntimeError(f"No complete Ruff profiling data found in {profile_dir}") + + with tempfile.NamedTemporaryFile( + dir=target_dir, prefix="ruff-", suffix=".profdata", delete=False + ) as temporary_file: + temporary_profile = Path(temporary_file.name) + try: + run( + [ + str(profiler), + "merge", + "--output", + str(temporary_profile), + *map(str, profiles), + ], + environment=environment, + ) + temporary_profile.replace(merged_profile) + finally: + temporary_profile.unlink(missing_ok=True) + print(f"Merged PGO profile: {merged_profile}", flush=True) + + if args.train_only: + return + + optimized_environment = environment | { + "CARGO_TARGET_DIR": str(target_dir), + "RUSTFLAGS": append_flags( + environment.get("RUSTFLAGS"), f"-Cprofile-use={merged_profile}" + ), + } + print("Building optimized release Ruff", flush=True) + run(cargo_command(target), environment=optimized_environment) + print( + f"Optimized Ruff: {target_dir / target / 'release' / binary_name}", flush=True + ) + + +def rustc_host() -> str: + version = subprocess.run( + ["rustc", "--version", "--verbose"], + cwd=REPOSITORY_ROOT, + check=True, + capture_output=True, + text=True, + ).stdout + for line in version.splitlines(): + if line.startswith("host: "): + return line.removeprefix("host: ") + raise RuntimeError("Could not determine the active Rust compiler's host target") + + +def find_llvm_profdata(host: str, override: Path | None) -> Path: + if override is not None: + profiler = override.resolve() + else: + sysroot = subprocess.run( + ["rustc", "--print", "sysroot"], + cwd=REPOSITORY_ROOT, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + binary_name = "llvm-profdata.exe" if "windows" in host else "llvm-profdata" + profiler = Path(sysroot) / "lib" / "rustlib" / host / "bin" / binary_name + + if not profiler.is_file() or not os.access(profiler, os.X_OK): + raise RuntimeError( + f"Rust toolchain llvm-profdata not found: {profiler}; " + "run `rustup component add llvm-tools-preview`" + ) + return profiler + + +def tracked_python_files() -> list[str]: + tracked_files = subprocess.run( + ["git", "ls-files", "-z", "--", *CORPUS_DIRECTORIES], + cwd=REPOSITORY_ROOT, + check=True, + capture_output=True, + ).stdout.split(b"\0") + paths = [ + os.fsdecode(path) + for path in tracked_files + if path and Path(os.fsdecode(path)).suffix in {".py", ".pyi"} + ] + if not paths: + raise RuntimeError("No tracked Python files found in the Ruff training corpus") + return paths + + +def cargo_command(target: str) -> list[str]: + return [ + "cargo", + "rustc", + "--release", + "--locked", + "--package", + "ruff", + "--bin", + "ruff", + "--target", + target, + "--", + "-C", + "strip=symbols", + ] + + +def append_flags(existing: str | None, additional: str) -> str: + return " ".join(flag for flag in (existing, additional) if flag) + + +def run( + command: list[str], + *, + environment: dict[str, str], + allowed_exit_codes: tuple[int, ...] = (0,), +) -> None: + print(f"> {shlex.join(command)}", flush=True) + completed = subprocess.run( + command, cwd=REPOSITORY_ROOT, env=environment, check=False + ) + if completed.returncode not in allowed_exit_codes: + raise subprocess.CalledProcessError(completed.returncode, command) + + +if __name__ == "__main__": + try: + main() + except (OSError, RuntimeError, subprocess.CalledProcessError) as error: + print(f"error: {error}", file=sys.stderr) + raise SystemExit(1) from error From f96ec0fac06a6441c6090028bec316cca8bcb0b2 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 7 Aug 2026 09:29:29 -0400 Subject: [PATCH 2/5] Train Ruff PGO on pinned ecosystem projects --- scripts/build_ruff_pgo.py | 267 +++++++++++++++++++++++++++++++++----- 1 file changed, 236 insertions(+), 31 deletions(-) diff --git a/scripts/build_ruff_pgo.py b/scripts/build_ruff_pgo.py index cf6fb5164a5ba7..714aaf16dc5034 100644 --- a/scripts/build_ruff_pgo.py +++ b/scripts/build_ruff_pgo.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Build Ruff with profile-guided optimization using tracked repository files.""" +"""Build Ruff with profile-guided optimization using pinned ecosystem projects.""" from __future__ import annotations @@ -9,13 +9,89 @@ import subprocess import sys import tempfile +from dataclasses import dataclass from pathlib import Path REPOSITORY_ROOT = Path(__file__).resolve().parent.parent -CORPUS_DIRECTORIES = ( - "crates/ruff_benchmark/resources", - "scripts", - "python/ruff-ecosystem/ruff_ecosystem", +EXCLUDED_DIRECTORIES = frozenset({"_tests", "_vendor", "test", "tests"}) + + +@dataclass(frozen=True) +class EcosystemProject: + name: str + repository: str + revision: str + source_directories: tuple[str, ...] + + +CORPUS_PROJECTS = ( + EcosystemProject( + name="pytest", + repository="pytest-dev/pytest", + revision="28e86a6c2ae0173831e4925a4af89b02a2936d09", + source_directories=("src/_pytest",), + ), + EcosystemProject( + name="httpx", + repository="encode/httpx", + revision="b5addb64f0161ff6bfe94c124ef76f6a1fba5254", + source_directories=("httpx",), + ), + EcosystemProject( + name="fastapi", + repository="fastapi/fastapi", + revision="a375f6b948b99fa4260129856bbf11d037f363ef", + source_directories=("fastapi",), + ), + EcosystemProject( + name="anyio", + repository="agronholm/anyio", + revision="ffe91331adb912c5d150f5d373f7cd28a0e96a62", + source_directories=("src/anyio",), + ), + EcosystemProject( + name="pip", + repository="pypa/pip", + revision="d1fd55753405fd728a0751a578e27c1054acdf48", + source_directories=("src/pip/_internal",), + ), + EcosystemProject( + name="sphinx", + repository="sphinx-doc/sphinx", + revision="b06d92e80eed130e1dd4e67cac4afa1267424f1a", + source_directories=( + "sphinx/builders", + "sphinx/ext/autodoc", + "sphinx/domains/python", + ), + ), + EcosystemProject( + name="astropy", + repository="astropy/astropy", + revision="b779108c7cec25c840c0f744fdf2a1550441e309", + source_directories=("astropy/units",), + ), + EcosystemProject( + name="prefect", + repository="PrefectHQ/prefect", + revision="db66b14dbaea18e726fc4ea0100fd194383c6c59", + source_directories=( + "src/prefect/server/models", + "src/prefect/concurrency", + "src/prefect/events", + "src/prefect/input", + ), + ), + EcosystemProject( + name="typeshed", + repository="python/typeshed", + revision="e0efbeef901e9b6998d016e1ab9352678f09ae77", + source_directories=( + "stdlib/asyncio", + "stdlib/collections", + "stubs/requests", + ), + ), ) @@ -42,14 +118,15 @@ def main() -> None: action="store_true", help="Only produce /ruff.profdata for a subsequent release build", ) + parser.add_argument( + "--prepare-corpus", + action="store_true", + help="Only download and prepare the pinned ecosystem training corpus", + ) args = parser.parse_args() - host = rustc_host() - target = args.target or host - if target != host: - parser.error( - f"PGO training requires the host-native target {host}, got {target}" - ) + if args.prepare_corpus and args.train_only: + parser.error("--prepare-corpus and --train-only cannot be used together") target_dir = ( args.target_dir @@ -59,14 +136,29 @@ def main() -> None: ).resolve() profile_dir = (args.profile_dir or target_dir / "profiles").resolve() merged_profile = target_dir / "ruff.profdata" + + environment = os.environ.copy() + if args.prepare_corpus: + corpus = ecosystem_python_files(target_dir / "corpus", environment=environment) + write_corpus_arguments(target_dir, corpus) + print(f"Prepared {len(corpus)} ecosystem Python files", flush=True) + return + + host = rustc_host() + target = args.target or host + if target != host: + parser.error( + f"PGO training requires the host-native target {host}, got {target}" + ) + profiler = find_llvm_profdata(host, args.llvm_profdata) - corpus = tracked_python_files() + corpus = ecosystem_python_files(target_dir / "corpus", environment=environment) + corpus_arguments = write_corpus_arguments(target_dir, corpus) profile_dir.mkdir(parents=True, exist_ok=True) for profile in profile_dir.glob("ruff-*.profraw"): profile.unlink() - environment = os.environ.copy() environment["CARGO_INCREMENTAL"] = "0" if target.endswith("-apple-darwin"): for variable in ("CFLAGS", "CXXFLAGS"): @@ -99,9 +191,15 @@ def main() -> None: "--no-cache", "--silent", ] - print(f"Training on {len(corpus)} tracked Python files", flush=True) + print(f"Training on {len(corpus)} ecosystem Python files", flush=True) run( - [str(instrumented_binary), "check", *common_arguments, "--exit-zero", *corpus], + [ + str(instrumented_binary), + "check", + *common_arguments, + "--exit-zero", + f"@{corpus_arguments}", + ], environment=training_environment, ) run( @@ -110,7 +208,7 @@ def main() -> None: "format", *common_arguments, "--check", - *corpus, + f"@{corpus_arguments}", ], environment=training_environment, allowed_exit_codes=(0, 1), @@ -192,23 +290,124 @@ def find_llvm_profdata(host: str, override: Path | None) -> Path: return profiler -def tracked_python_files() -> list[str]: - tracked_files = subprocess.run( - ["git", "ls-files", "-z", "--", *CORPUS_DIRECTORIES], - cwd=REPOSITORY_ROOT, - check=True, - capture_output=True, - ).stdout.split(b"\0") - paths = [ - os.fsdecode(path) - for path in tracked_files - if path and Path(os.fsdecode(path)).suffix in {".py", ".pyi"} - ] - if not paths: - raise RuntimeError("No tracked Python files found in the Ruff training corpus") +def ecosystem_python_files( + corpus_directory: Path, *, environment: dict[str, str] +) -> list[str]: + corpus_directory.mkdir(parents=True, exist_ok=True) + git_environment = environment | { + "GIT_CONFIG_GLOBAL": os.devnull, + "GIT_TERMINAL_PROMPT": "0", + "GIT_LFS_SKIP_SMUDGE": "1", + } + paths: list[str] = [] + + for project in CORPUS_PROJECTS: + checkout = corpus_directory / project.name + checkout.mkdir(parents=True, exist_ok=True) + git = ["git", "-c", f"core.hooksPath={os.devnull}", "-C", str(checkout)] + + if not (checkout / ".git").is_dir(): + print(f"Preparing {project.repository}@{project.revision}", flush=True) + run([*git, "init", "--quiet"], environment=git_environment) + run( + [ + *git, + "remote", + "add", + "origin", + f"https://github.com/{project.repository}.git", + ], + environment=git_environment, + ) + + run( + [*git, "sparse-checkout", "set", "--cone", *project.source_directories], + environment=git_environment, + ) + + current_revision = subprocess.run( + [*git, "rev-parse", "--verify", "HEAD"], + cwd=REPOSITORY_ROOT, + env=git_environment, + check=False, + capture_output=True, + text=True, + ) + if ( + current_revision.returncode != 0 + or current_revision.stdout.strip() != project.revision + ): + run( + [ + *git, + "fetch", + "--quiet", + "--no-tags", + "--no-recurse-submodules", + "--depth=1", + "--filter=blob:none", + "origin", + project.revision, + ], + environment=git_environment, + ) + + run( + [ + *git, + "checkout", + "--quiet", + "--detach", + "--force", + "--no-recurse-submodules", + project.revision, + ], + environment=git_environment, + ) + + for source_directory in project.source_directories: + source = checkout / source_directory + if not source.is_dir(): + raise RuntimeError( + f"Missing training source directory {source_directory!r} " + f"in {project.repository}@{project.revision}" + ) + + tracked_files = subprocess.run( + [*git, "ls-files", "-z", "--", *project.source_directories], + cwd=REPOSITORY_ROOT, + env=git_environment, + check=True, + capture_output=True, + ).stdout.split(b"\0") + project_paths = [ + str(path) + for tracked_file in tracked_files + if tracked_file + and (path := checkout / os.fsdecode(tracked_file)).suffix in {".py", ".pyi"} + and path.is_file() + and not path.is_symlink() + and not EXCLUDED_DIRECTORIES.intersection( + path.relative_to(checkout).parts[:-1] + ) + ] + + if not project_paths: + raise RuntimeError( + f"No Python training files found in {project.repository}" + ) + paths.extend(sorted(project_paths)) + print(f" {project.name}: {len(project_paths)} Python files", flush=True) + return paths +def write_corpus_arguments(target_directory: Path, corpus: list[str]) -> Path: + arguments = target_directory / "ruff-pgo.args" + arguments.write_text("\n".join(corpus) + "\n", encoding="utf-8", newline="\n") + return arguments + + def cargo_command(target: str) -> list[str]: return [ "cargo", @@ -237,7 +436,13 @@ def run( environment: dict[str, str], allowed_exit_codes: tuple[int, ...] = (0,), ) -> None: - print(f"> {shlex.join(command)}", flush=True) + logged_arguments = 16 + displayed_command = shlex.join(command[:logged_arguments]) + if len(command) > logged_arguments: + displayed_command += ( + f" ... ({len(command) - logged_arguments} arguments omitted)" + ) + print(f"> {displayed_command}", flush=True) completed = subprocess.run( command, cwd=REPOSITORY_ROOT, env=environment, check=False ) From a26e45cc1480655901fb157d036f3a7d1ccbaa4e Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 7 Aug 2026 13:29:32 -0400 Subject: [PATCH 3/5] Retry PGO corpus Git operations --- scripts/build_ruff_pgo.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/scripts/build_ruff_pgo.py b/scripts/build_ruff_pgo.py index 714aaf16dc5034..5d090d89e8b4b2 100644 --- a/scripts/build_ruff_pgo.py +++ b/scripts/build_ruff_pgo.py @@ -9,6 +9,7 @@ import subprocess import sys import tempfile +import time from dataclasses import dataclass from pathlib import Path @@ -337,7 +338,7 @@ def ecosystem_python_files( current_revision.returncode != 0 or current_revision.stdout.strip() != project.revision ): - run( + run_git_with_retry( [ *git, "fetch", @@ -352,7 +353,7 @@ def ecosystem_python_files( environment=git_environment, ) - run( + run_git_with_retry( [ *git, "checkout", @@ -402,6 +403,23 @@ def ecosystem_python_files( return paths +def run_git_with_retry(command: list[str], *, environment: dict[str, str]) -> None: + for attempt in range(3): + try: + run(command, environment=environment) + return + except subprocess.CalledProcessError: + if attempt == 2: + raise + delay = 2**attempt + print( + f"Git command failed; retrying in {delay}s (attempt {attempt + 2} of 3)", + file=sys.stderr, + flush=True, + ) + time.sleep(delay) + + def write_corpus_arguments(target_directory: Path, corpus: list[str]) -> Path: arguments = target_directory / "ruff-pgo.args" arguments.write_text("\n".join(corpus) + "\n", encoding="utf-8", newline="\n") From a752a11d6a5b4f095c2a0ce2aedc46ffeb8113e7 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 7 Aug 2026 16:29:05 -0400 Subject: [PATCH 4/5] Harden Ruff PGO training script --- scripts/build_ruff_pgo.py | 165 ++++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 50 deletions(-) diff --git a/scripts/build_ruff_pgo.py b/scripts/build_ruff_pgo.py index 5d090d89e8b4b2..ae5df882a96ae5 100644 --- a/scripts/build_ruff_pgo.py +++ b/scripts/build_ruff_pgo.py @@ -1,10 +1,15 @@ -#!/usr/bin/env python3 """Build Ruff with profile-guided optimization using pinned ecosystem projects.""" +# /// script +# requires-python = ">=3.11" +# dependencies = [] +# /// + from __future__ import annotations import argparse import os +import re import shlex import subprocess import sys @@ -17,13 +22,24 @@ EXCLUDED_DIRECTORIES = frozenset({"_tests", "_vendor", "test", "tests"}) -@dataclass(frozen=True) +@dataclass(frozen=True, slots=True) class EcosystemProject: name: str repository: str revision: str source_directories: tuple[str, ...] + def __post_init__(self) -> None: + if re.fullmatch(r"[0-9a-f]{40}", self.revision) is None: + raise ValueError( + f"{self.repository} must be pinned to a full Git commit SHA, " + f"got {self.revision!r}" + ) + + @property + def url(self) -> str: + return f"https://github.com/{self.repository}.git" + CORPUS_PROJECTS = ( EcosystemProject( @@ -114,21 +130,19 @@ def main() -> None: type=Path, help="Override the active Rust toolchain's llvm-profdata executable", ) - parser.add_argument( + mode = parser.add_mutually_exclusive_group() + mode.add_argument( "--train-only", action="store_true", help="Only produce /ruff.profdata for a subsequent release build", ) - parser.add_argument( + mode.add_argument( "--prepare-corpus", action="store_true", help="Only download and prepare the pinned ecosystem training corpus", ) args = parser.parse_args() - if args.prepare_corpus and args.train_only: - parser.error("--prepare-corpus and --train-only cannot be used together") - target_dir = ( args.target_dir or Path( @@ -182,9 +196,39 @@ def main() -> None: if not instrumented_binary.is_file(): raise RuntimeError(f"Instrumented Ruff binary not found: {instrumented_binary}") - training_environment = instrumented_environment | { - "LLVM_PROFILE_FILE": str(profile_dir / "ruff-%m-%p.profraw") + profiles = train_ruff( + instrumented_binary, + corpus_arguments, + profile_dir, + corpus_size=len(corpus), + environment=instrumented_environment, + ) + merge_profiles(profiler, profiles, merged_profile, environment=environment) + + if args.train_only: + return + + optimized_environment = environment | { + "CARGO_TARGET_DIR": str(target_dir), + "RUSTFLAGS": append_flags( + environment.get("RUSTFLAGS"), f"-Cprofile-use={merged_profile}" + ), } + print("Building optimized release Ruff", flush=True) + run(cargo_command(target), environment=optimized_environment) + print( + f"Optimized Ruff: {target_dir / target / 'release' / binary_name}", flush=True + ) + + +def train_ruff( + binary: Path, + corpus_arguments: Path, + profile_directory: Path, + *, + corpus_size: int, + environment: dict[str, str], +) -> list[Path]: common_arguments = [ "--isolated", "--target-version", @@ -192,35 +236,54 @@ def main() -> None: "--no-cache", "--silent", ] - print(f"Training on {len(corpus)} ecosystem Python files", flush=True) - run( - [ - str(instrumented_binary), - "check", - *common_arguments, - "--exit-zero", - f"@{corpus_arguments}", - ], - environment=training_environment, - ) - run( - [ - str(instrumented_binary), - "format", - *common_arguments, - "--check", - f"@{corpus_arguments}", - ], - environment=training_environment, - allowed_exit_codes=(0, 1), + workloads = ( + ("check", "--exit-zero", (0,)), + ("format", "--check", (0, 1)), ) + print(f"Training on {corpus_size} ecosystem Python files", flush=True) + profiles = [] + + for mode, mode_argument, allowed_exit_codes in workloads: + run( + [ + str(binary), + mode, + *common_arguments, + mode_argument, + f"@{corpus_arguments}", + ], + environment=environment + | { + "LLVM_PROFILE_FILE": str( + profile_directory / f"ruff-{mode}-%m-%p.profraw" + ) + }, + allowed_exit_codes=allowed_exit_codes, + ) + + workload_profiles = sorted(profile_directory.glob(f"ruff-{mode}-*.profraw")) + if not workload_profiles or any( + profile.stat().st_size == 0 for profile in workload_profiles + ): + raise RuntimeError( + f"No complete Ruff {mode} profiling data found in {profile_directory}" + ) + profiles.extend(workload_profiles) + + return profiles - profiles = sorted(profile_dir.glob("ruff-*.profraw")) - if not profiles or any(profile.stat().st_size == 0 for profile in profiles): - raise RuntimeError(f"No complete Ruff profiling data found in {profile_dir}") + +def merge_profiles( + profiler: Path, + profiles: list[Path], + destination: Path, + *, + environment: dict[str, str], +) -> None: + profile_size = sum(profile.stat().st_size for profile in profiles) with tempfile.NamedTemporaryFile( - dir=target_dir, prefix="ruff-", suffix=".profdata", delete=False + dir=destination.parent, prefix="ruff-", suffix=".profdata", delete=False ) as temporary_file: temporary_profile = Path(temporary_file.name) try: @@ -234,24 +297,12 @@ def main() -> None: ], environment=environment, ) - temporary_profile.replace(merged_profile) + temporary_profile.replace(destination) finally: temporary_profile.unlink(missing_ok=True) - print(f"Merged PGO profile: {merged_profile}", flush=True) - - if args.train_only: - return - - optimized_environment = environment | { - "CARGO_TARGET_DIR": str(target_dir), - "RUSTFLAGS": append_flags( - environment.get("RUSTFLAGS"), f"-Cprofile-use={merged_profile}" - ), - } - print("Building optimized release Ruff", flush=True) - run(cargo_command(target), environment=optimized_environment) print( - f"Optimized Ruff: {target_dir / target / 'release' / binary_name}", flush=True + f"Merged {len(profiles)} PGO profiles ({profile_size:,} bytes): {destination}", + flush=True, ) @@ -316,11 +367,25 @@ def ecosystem_python_files( "remote", "add", "origin", - f"https://github.com/{project.repository}.git", + project.url, ], environment=git_environment, ) + remote = subprocess.run( + [*git, "config", "--local", "--get", "remote.origin.url"], + cwd=REPOSITORY_ROOT, + env=git_environment, + check=True, + capture_output=True, + text=True, + ).stdout.strip() + if remote != project.url: + raise RuntimeError( + f"Unexpected origin for cached {project.name} checkout: " + f"expected {project.url}, got {remote}" + ) + run( [*git, "sparse-checkout", "set", "--cone", *project.source_directories], environment=git_environment, From 912fdb31df22e756e7268a5e22a09b6739754b76 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 7 Aug 2026 16:55:40 -0400 Subject: [PATCH 5/5] Share the ty PGO training corpus --- scripts/build_ruff_pgo.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/scripts/build_ruff_pgo.py b/scripts/build_ruff_pgo.py index ae5df882a96ae5..b6d8d25d4e11ab 100644 --- a/scripts/build_ruff_pgo.py +++ b/scripts/build_ruff_pgo.py @@ -88,17 +88,6 @@ def url(self) -> str: revision="b779108c7cec25c840c0f744fdf2a1550441e309", source_directories=("astropy/units",), ), - EcosystemProject( - name="prefect", - repository="PrefectHQ/prefect", - revision="db66b14dbaea18e726fc4ea0100fd194383c6c59", - source_directories=( - "src/prefect/server/models", - "src/prefect/concurrency", - "src/prefect/events", - "src/prefect/input", - ), - ), EcosystemProject( name="typeshed", repository="python/typeshed",