Skip to content

Commit 89619cb

Browse files
committed
Fix GraalPy PGO profile selection
1 parent 97c4e1d commit 89619cb

1 file changed

Lines changed: 100 additions & 31 deletions

File tree

mx.graalpython/mx_graalpython.py

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,60 @@ def _normalize_branch_name(branch):
306306
return branch
307307

308308

309+
def _graalpython_commit_is_ancestor_of_branch(commit, branch):
310+
"""Return True if the GraalPy commit is already reachable from the branch."""
311+
# Check the remote-tracking branch, not the local branch or HEAD. Regular branch-run
312+
# workspaces may put a synthetic merge commit on a local branch, while origin/<branch>
313+
# still represents the target branch state used to decide whether the GraalPy commit has
314+
# really landed.
315+
return _graalpython_merge_base_with_branch(commit, branch) == commit
316+
317+
318+
def _graalpython_merge_base_with_branch(commit, branch):
319+
if not commit or not branch or not SUITE.vc:
320+
return None
321+
merge_base = SUITE.vc.git_command(
322+
SUITE.dir,
323+
["merge-base", commit, f"origin/{branch}"],
324+
abortOnError=False,
325+
)
326+
return merge_base.strip() if merge_base else None
327+
328+
329+
def _graalpython_target_branch_profile_candidates(commit, branch, max_age_days):
330+
"""Return target-branch ancestor commits that may have CI-generated PGO profiles."""
331+
merge_base = _graalpython_merge_base_with_branch(commit, branch)
332+
if not merge_base:
333+
return []
334+
335+
commit_ts = SUITE.vc.git_command(
336+
SUITE.dir,
337+
["show", "-s", "--format=%ct", merge_base],
338+
abortOnError=False,
339+
)
340+
if not commit_ts:
341+
return []
342+
try:
343+
since_ts = int(commit_ts.strip()) - max_age_days * 24 * 60 * 60
344+
except ValueError:
345+
mx.warn(f"Cannot parse commit timestamp for GraalPy PGO profile search: {commit_ts!r}")
346+
return []
347+
348+
candidates = SUITE.vc.git_command(
349+
SUITE.dir,
350+
["rev-list", "--first-parent", f"--since=@{since_ts}", merge_base],
351+
abortOnError=False,
352+
)
353+
return candidates.splitlines() if candidates else []
354+
355+
356+
def _graalpython_pgo_profile_exists(path):
357+
try:
358+
return os.path.isfile(path or "") and os.path.getsize(path) > 0
359+
except OSError:
360+
return False
361+
362+
309363
def _graalpython_target_import_commit(target_branch):
310364
"""Return the GraalPy commit imported by the target branch's VM suite.
311365
@@ -458,55 +512,70 @@ def libpythonvm_build_args():
458512
profile_source_commit = source_commit
459513
profile_source_branch = source_branch
460514
profile_source_reason = "current GraalPy commit"
515+
profile_source_candidates = [(profile_source_commit, profile_source_branch, profile_source_reason)]
461516
override = os.environ.get("GRAALPY_PGO_PROFILE_SOURCE_COMMIT")
462517
if override:
463518
profile_source_commit = override.strip()
464519
if not re.match(r"^[0-9a-f]{40}$", profile_source_commit):
465520
mx.abort(f"GRAALPY_PGO_PROFILE_SOURCE_COMMIT must be a 40-character lowercase git commit, got: {override}")
466521
profile_source_reason = "GRAALPY_PGO_PROFILE_SOURCE_COMMIT"
522+
profile_source_candidates = [(profile_source_commit, profile_source_branch, profile_source_reason)]
467523
elif (
468524
target_branch
469525
and source_branch
470526
and source_branch != target_branch
471527
and not (source_branch == MAIN_BRANCH or source_branch.startswith(("release/", "cpu/")))
528+
and not _graalpython_commit_is_ancestor_of_branch(source_commit, target_branch)
472529
):
473-
# Feature branch merge commits can import a fresh GraalPy revision that
474-
# has no released-product profile yet. Use the target branch's imported
475-
# GraalPy commit so product-ee consumes the profile that already belongs
476-
# to the baseline product launcher.
530+
# Feature branch commits usually have no released-product profile yet,
531+
# but use one if it was explicitly generated. Otherwise prefer the
532+
# nearest available target-branch ancestor profile, then fall back to
533+
# the VM suite's target import for compatibility with older CI workspaces.
534+
profile_search_days = 14
535+
target_candidates = _graalpython_target_branch_profile_candidates(
536+
source_commit, target_branch, profile_search_days
537+
)
477538
target_import_commit = _graalpython_target_import_commit(target_branch)
478-
if target_import_commit:
479-
profile_source_commit = target_import_commit
480-
profile_source_branch = target_branch
481-
profile_source_reason = f"target branch import from {target_branch}"
482-
elif require_profile:
483-
mx.abort("\n".join([
484-
"Could not resolve the GraalPy PGO profile source commit from the target branch import.",
485-
f"GraalPy source commit: {source_commit}",
486-
f"Source branch: {source_branch or '<unknown>'}",
487-
f"Target branch: {target_branch or '<unknown>'}",
488-
"Expected to read vm/mx.vm/suite.py from the target branch and find the graalpython import version.",
489-
"Set GRAALPY_PGO_PROFILE_SOURCE_COMMIT=<40-character-commit> to override this lookup for debugging.",
490-
]))
491-
else:
492-
mx.warn("Falling back to the current GraalPy commit for PGO profile lookup because the target branch import could not be resolved")
539+
profile_source_candidates = [
540+
(source_commit, source_branch, "current GraalPy branch commit"),
541+
] + [
542+
(commit, target_branch, f"target branch ancestor from {target_branch}")
543+
for commit in target_candidates
544+
]
545+
if (
546+
target_import_commit
547+
and target_import_commit not in {commit for commit, _, _ in profile_source_candidates}
548+
):
549+
profile_source_candidates.append(
550+
(target_import_commit, target_branch, f"target branch import from {target_branch}")
551+
)
493552
artifact_name = f"{GRAALPY_PGO_PROFILE_ARTIFACT_GROUP}/{GRAALPY_PGO_PROFILE_ARTIFACT_PREFIX}{profile_source_commit}"
494553
mx.log(f"GraalPy source commit for PGO profile lookup: {source_commit}")
495-
mx.log(f"GraalPy PGO profile source commit: {profile_source_commit} ({profile_source_reason})")
496554

497555
if script := os.environ.get("ARTIFACT_DOWNLOAD_SCRIPT"):
498556
# This is always available in the GraalPy CI
499557
profile = f"cached_profile.iprof.gz"
500-
run(
501-
[
502-
sys.executable,
503-
script,
504-
artifact_name,
505-
profile,
506-
],
507-
nonZeroIsFatal=False,
508-
)
558+
for candidate_commit, candidate_branch, candidate_reason in profile_source_candidates:
559+
profile_source_commit = candidate_commit
560+
profile_source_branch = candidate_branch
561+
profile_source_reason = candidate_reason
562+
artifact_name = f"{GRAALPY_PGO_PROFILE_ARTIFACT_GROUP}/{GRAALPY_PGO_PROFILE_ARTIFACT_PREFIX}{profile_source_commit}"
563+
mx.log(f"GraalPy PGO profile source commit: {profile_source_commit} ({profile_source_reason})")
564+
if os.path.exists(profile):
565+
os.remove(profile)
566+
run(
567+
[
568+
sys.executable,
569+
script,
570+
artifact_name,
571+
profile,
572+
],
573+
nonZeroIsFatal=False,
574+
)
575+
if _graalpython_pgo_profile_exists(profile):
576+
break
509577
elif not require_profile:
578+
mx.log(f"GraalPy PGO profile source commit: {profile_source_commit} ({profile_source_reason})")
510579
# Locally, we try to get a reasonable profile
511580
get_profile = mx.command_function('python-get-latest-profile', fatalIfMissing=False)
512581
if get_profile:
@@ -522,7 +591,7 @@ def libpythonvm_build_args():
522591
except BaseException:
523592
pass
524593

525-
profile_missing = not profile or not os.path.isfile(profile)
594+
profile_missing = not _graalpython_pgo_profile_exists(profile)
526595
if require_profile and profile_missing:
527596
mx.abort("\n".join([
528597
"GRAALPY_REQUIRE_PGO_PROFILE is set, but no CI generated GraalPy PGO profile was found.",
@@ -550,7 +619,7 @@ def libpythonvm_build_args():
550619
mx.warn("PGO profile must exist for benchmarking and release, creating one now...")
551620
profile = graalpy_native_pgo_build_and_test()
552621

553-
if os.path.isfile(profile or ""):
622+
if _graalpython_pgo_profile_exists(profile):
554623
print(invert(f"Automatically chose PGO profile {profile}. To disable this, set GRAALPY_PGO_PROFILE to an empty string'", blinking=True), file=sys.stderr)
555624
build_args += [
556625
f"--pgo={profile}",

0 commit comments

Comments
 (0)