Skip to content

Commit f87b1f0

Browse files
committed
unix: use extended PGO training set
This will run all tests, ensuring maximal training coverage. As part of this, we had to annotate/ignore every failing test because test failures would otherwise fail the build.
1 parent ca37f1d commit f87b1f0

5 files changed

Lines changed: 315 additions & 17 deletions

File tree

cpython-unix/build-cpython.sh

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ PIP_WHEEL="${ROOT}/pip-${PIP_VERSION}-py3-none-any.whl"
5252
SETUPTOOLS_WHEEL="${ROOT}/setuptools-${SETUPTOOLS_VERSION}-py3-none-any.whl"
5353

5454
# Put critical config files in logs to aid debugging.
55-
for f in Setup.local Makefile.extra stdlib-test-annotations.json; do
55+
for f in Setup.local Makefile.extra stdlib-test-annotations.json profiling-training-ignores.txt; do
5656
echo "BEGIN $f"
5757
cat $f
5858
echo "END $f"
@@ -593,7 +593,27 @@ if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]; then
593593
fi
594594

595595
# Define the base PGO profiling task, which we'll extend below with ignores
596-
export PROFILE_TASK='-m test --pgo'
596+
#
597+
# --pgo-extended implies --pgo. And --pgo mode significantly changes the behavior
598+
# of the test harness. Notably, it disables richer reporting of test failures,
599+
# making it vastly more difficult to debug test failures. This behavior undermines
600+
# our ability to debug failures when running tests for PGO instrumentation.
601+
#
602+
# The only material downside to not using --pgo is some tests that self-skip
603+
# when run under PGO don't do so. When we audited for such tests in 2026-03,
604+
# all such tests had the reason "PGO isn't useful" or some such. There were
605+
# ~10 such tests. Since we run the entire test suite anyway, the inclusion of
606+
# such "worthless" tests isn't impactful. The test failure observability is
607+
# worth their loss.
608+
export PROFILE_TASK='-m test'
609+
610+
# Display test output on failure. This helps immensely with debugging PGO
611+
# failures.
612+
PROFILE_TASK="${PROFILE_TASK} -W"
613+
614+
# Force kill tests taking too long. This prevents deadlocks that can cause
615+
# CI jobs to run for potentially hours while doing nothing.
616+
PROFILE_TASK="${PROFILE_TASK} --timeout 300"
597617

598618
# Run tests in parallel to reduce wall time.
599619
#
@@ -608,29 +628,26 @@ export PROFILE_TASK='-m test --pgo'
608628
# and there will be no loss in profile quality.
609629
PROFILE_TASK="${PROFILE_TASK} -j ${NUM_CPUS}"
610630

611-
# On 3.14+ `test_strftime_y2k` fails when cross-compiling for `x86_64_v2` and `x86_64_v3` targets on
612-
# Linux, so we ignore it. See https://github.com/python/cpython/issues/128104
613-
if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" && -n "${CROSS_COMPILING}" && "${PYBUILD_PLATFORM}" != macos* ]]; then
614-
PROFILE_TASK="${PROFILE_TASK} --ignore test_strftime_y2k"
631+
IGNORE_TESTS=()
632+
633+
# Possible race condition.
634+
IGNORE_TESTS+=("test.test_audit.AuditTest.test_time_fail")
635+
636+
if [[ -n "${CPYTHON_FREETHREADED}" ]]; then
637+
IGNORE_TESTS+=("test.test_bytes.FreeThreadingTest.test_free_threading_bytearrayiter")
615638
fi
616639

617640
# On 3.14+ `test_json.test_recursion.TestCRecursion.test_highly_nested_objects_decoding` fails during
618641
# PGO due to RecursionError not being raised as expected. See https://github.com/python/cpython/issues/140125
619642
if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_14}" ]]; then
620-
PROFILE_TASK="${PROFILE_TASK} --ignore test_json"
643+
IGNORE_TESTS+=("test.test_json.test_recursion.TestCRecursion.test_highly_nested_objects_decoding")
621644
fi
622645

623-
# PGO optimized / BOLT instrumented binaries segfault in a test_bytes test. Skip it.
624-
if [[ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" && "${TARGET_TRIPLE}" == x86_64* ]]; then
625-
PROFILE_TASK="${PROFILE_TASK} --ignore test.test_bytes.BytesTest.test_from_format"
626-
fi
646+
PROFILE_TASK="${PROFILE_TASK} --ignorefile ${ROOT}/profiling-training-ignores.txt"
627647

628-
# Some tests aren't race friendly on freethreaded builds. Disable as a mitigation.
629-
if [[ -n "${CPYTHON_FREETHREADED}" ]]; then
630-
PROFILE_TASK="${PROFILE_TASK} --ignore test.test_bytes.FreeThreadingTest.test_free_threading_bytearrayiter"
631-
PROFILE_TASK="${PROFILE_TASK} --ignore test.test_hashlib.HashlibTestCase.test_threaded_hashing_fast"
632-
PROFILE_TASK="${PROFILE_TASK} --ignore test.test_itertools.TestBasicOps.test_count_threading"
633-
PROFILE_TASK="${PROFILE_TASK} --ignore test.test_itertools.TestBasicOps.test_count_with_step_threading"
648+
# Exclude whole modules from profiling based on stdlib test annotations.
649+
if [ -n "${PROFILING_EXCLUDE_MODULES}" ]; then
650+
PROFILE_TASK="${PROFILE_TASK} --exclude ${PROFILING_EXCLUDE_MODULES}"
634651
fi
635652

636653
# ./configure tries to auto-detect whether it can build 128-bit and 256-bit SIMD helpers for HACL,

cpython-unix/build.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,12 +839,28 @@ def build_cpython(
839839

840840
build_env.copy_file(fh.name, dest_name="stdlib-test-annotations.json")
841841

842+
# Install a file with tests to skip during profile training.
843+
# Also collect module excludes for the profiling run of the test harness.
844+
profiling_exclude_modules = []
845+
with tempfile.NamedTemporaryFile("w", encoding="utf-8") as fh:
846+
os.chmod(fh.name, 0o644)
847+
848+
for ann in test_annotations.annotations:
849+
if ann.profile_training_skip():
850+
fh.write(f"{ann.name}\n")
851+
if m := ann.profile_training_exclude_module():
852+
profiling_exclude_modules.append(m)
853+
854+
fh.flush()
855+
build_env.copy_file(fh.name, dest_name="profiling-training-ignores.txt")
856+
842857
env = {
843858
"PIP_VERSION": DOWNLOADS["pip"]["version"],
844859
"PYTHON_VERSION": python_version,
845860
"PYTHON_MAJMIN_VERSION": ".".join(python_version.split(".")[0:2]),
846861
"SETUPTOOLS_VERSION": DOWNLOADS["setuptools"]["version"],
847862
"TOOLCHAIN": "clang-%s" % host_platform,
863+
"PROFILING_EXCLUDE_MODULES": " ".join(profiling_exclude_modules),
848864
}
849865

850866
# Set environment variables allowing convenient testing for Python

pythonbuild/cpython.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,30 @@
197197
"required": ["name", "reason"],
198198
},
199199
},
200+
"profiling-excludes": {
201+
"type": "array",
202+
"items": {
203+
"type": "object",
204+
"properties": {
205+
"name": {"type": "string"},
206+
**STDLIB_TEST_ANNOTATION_COMMON_PROPERTIES,
207+
},
208+
"additionalProperties": False,
209+
"required": ["name", "reason"],
210+
},
211+
},
212+
"profiling-skips": {
213+
"type": "array",
214+
"items": {
215+
"type": "object",
216+
"properties": {
217+
"name": {"type": "string"},
218+
**STDLIB_TEST_ANNOTATION_COMMON_PROPERTIES,
219+
},
220+
"additionalProperties": False,
221+
"required": ["name", "reason"],
222+
},
223+
},
200224
},
201225
}
202226

@@ -817,6 +841,8 @@ def extension_modules_config(yaml_path: pathlib.Path):
817841
TEST_ANNOTATION_HARNESS_SKIP = "harness-skip"
818842
TEST_ANNOTATION_MODULE_EXCLUDE = "module-exclude"
819843
TEST_ANNOTATION_TEST_FAILURE = "test-failure"
844+
TEST_ANNOTATION_PROFILING_EXCLUDE = "profiling-exclude"
845+
TEST_ANNOTATION_PROFILING_SKIP = "profiling-skip"
820846

821847

822848
@dataclasses.dataclass
@@ -837,6 +863,17 @@ class TestAnnotation:
837863
# Whether to exclude loading the test module when running tests.
838864
exclude_testing: bool
839865

866+
def profile_training_skip(self) -> bool:
867+
"""Whether to ignore this test during PGO training."""
868+
return self.flavor in (
869+
TEST_ANNOTATION_TEST_FAILURE,
870+
TEST_ANNOTATION_PROFILING_SKIP,
871+
)
872+
873+
def profile_training_exclude_module(self) -> Optional[str]:
874+
"""Name of module to exclude from profiling."""
875+
return self.name if self.flavor == TEST_ANNOTATION_PROFILING_EXCLUDE else None
876+
840877

841878
@dataclasses.dataclass
842879
class TestAnnotations:
@@ -937,6 +974,12 @@ def stdlib_test_annotations(
937974
for entry in data["expected-failures"]:
938975
raw_entries.append((TEST_ANNOTATION_TEST_FAILURE, entry))
939976

977+
for entry in data["profiling-excludes"]:
978+
raw_entries.append((TEST_ANNOTATION_PROFILING_EXCLUDE, entry))
979+
980+
for entry in data["profiling-skips"]:
981+
raw_entries.append((TEST_ANNOTATION_PROFILING_SKIP, entry))
982+
940983
for flavor, entry in raw_entries:
941984
if a := filter_stdlib_test_entry(
942985
flavor,

pythonbuild/testdist.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
TEST_ANNOTATION_HARNESS_SKIP,
2020
TEST_ANNOTATION_MODULE_EXCLUDE,
2121
TEST_ANNOTATION_TEST_FAILURE,
22+
TEST_ANNOTATION_PROFILING_EXCLUDE,
23+
TEST_ANNOTATION_PROFILING_SKIP,
2224
TestAnnotation,
2325
meets_python_minimum_version,
2426
)
@@ -164,6 +166,15 @@ def run_stdlib_tests(
164166
if annotation.dont_verify:
165167
dont_verify.add(name)
166168

169+
elif flavor == "profiling-exclude":
170+
print(f"profiling exclude module ignored {name}")
171+
continue
172+
173+
elif flavor == "profiling-skip":
174+
print(f"profiling skip test ignored {name}")
175+
176+
continue
177+
167178
else:
168179
raise Exception(f"unhandled test annotation flavor: {flavor}")
169180

0 commit comments

Comments
 (0)