Skip to content

Commit 1a921ab

Browse files
committed
[GR-76225] Best effort retrieval of GraalPy product PGO profiles.
PullRequest: graalpython/4613
2 parents 60d6363 + 89619cb commit 1a921ab

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
@@ -307,6 +307,60 @@ def _normalize_branch_name(branch):
307307
return branch
308308

309309

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

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

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

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

0 commit comments

Comments
 (0)