Skip to content

[GR-61337] Properly use sampled method profiles for inlining decisions #11896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion sdk/mx.sdk/mx_sdk_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,27 @@ def run_launcher(self, cmd, args, cwd):
return code, out, dims


# The uncompressed file from perf script is ~25x bigger than the compressed file
ADJUSTED_ITERS_FOR_PERF = {
"renaissance": {
"finagle-chirper": 10, # Default: 90 iters, 7GB compressed perf file
"fj-kmeans": 15, # Default: 30 iters, 520MB compressed perf file
"scala-stm-bench7": 20, # Default: 60 iters, 840MB compressed perf file
"philosophers": 10, # Default: 30 iters, 900MB compressed perf file
"akka-uct": 5, # Default: 24 iters, 2GB compressed perf file
"finagle-http": 5 # Default: 12 iters, 1.3GB compressed perf file
},
"dacapo": {
"sunflow": 15 # Default: 49 iters, 1.2GB compressed perf file
}
}

NUM_ITERS_FLAG_NAME = {
"renaissance": "-r",
"dacapo": "-n"
}


class NativeImageBenchmarkConfig:
def __init__(self, vm: NativeImageVM, bm_suite: BenchmarkSuite | NativeImageBenchmarkMixin, args):
self.bm_suite = bm_suite
Expand All @@ -308,6 +329,11 @@ def __init__(self, vm: NativeImageVM, bm_suite: BenchmarkSuite | NativeImageBenc
not (vm.safepoint_sampler or vm.pgo_sampler_only or vm.pgo_use_perf))
self.extra_agent_profile_run_args = bm_suite.extra_agent_profile_run_arg(self.benchmark_name, args,
list(image_run_args))
if vm.pgo_use_perf and self.benchmark_suite_name in ADJUSTED_ITERS_FOR_PERF and self.benchmark_name in ADJUSTED_ITERS_FOR_PERF[self.benchmark_suite_name]:
# For some benches the number of iters in the instrument-run stage is too much for perf, the generated files are too large.
desired_iters = ADJUSTED_ITERS_FOR_PERF[self.benchmark_suite_name][self.benchmark_name]
mx.log(f"Adjusting number of iters for instrument-run stage to {desired_iters}")
self.extra_profile_run_args = adjust_arg_with_number(NUM_ITERS_FLAG_NAME[self.benchmark_suite_name], desired_iters, self.extra_profile_run_args)
self.params = ['extra-image-build-argument', 'extra-jvm-arg', 'extra-run-arg', 'extra-agent-run-arg',
'extra-profile-run-arg',
'extra-agent-profile-run-arg', 'benchmark-output-dir', 'stages', 'skip-agent-assertions']
Expand Down Expand Up @@ -1560,7 +1586,7 @@ def run_stage_instrument_run(self):
image_run_cmd += self.config.extra_jvm_args
image_run_cmd += self.config.extra_profile_run_args
if self.pgo_use_perf:
image_run_cmd = ['perf', 'record', '-o', f'{self.config.perf_data_path}', '--call-graph', 'fp,2048', '--freq=999'] + image_run_cmd
image_run_cmd = ['perf', 'record', '-o', f'{self.config.perf_data_path}', '--call-graph', 'fp', '--freq=999'] + image_run_cmd

with self.get_stage_runner() as s:
if self.pgo_use_perf:
Expand Down Expand Up @@ -3733,6 +3759,15 @@ def _strip_arg_with_number_gen(_strip_arg, _args):
result = _strip_arg_with_number_gen(strip_arg, result)
return list(result)

def adjust_arg_with_number(arg_name, new_value: int, user_args):
"""
Sets the argument value of `arg_name` in `user_args` with `new_value`.
If `arg_name` is already present in `user_args`, the value will be replaced.
If `arg_name` is not already present, the argument and corresponding value will be added.
"""

return [arg_name, str(new_value)] + strip_args_with_number(arg_name, user_args)

class PerfInvokeProfileCollectionStrategy(Enum):
"""
The strategy for extracting virtual invoke method profiles from perf sampling data.
Expand Down
12 changes: 6 additions & 6 deletions substratevm/mx.substratevm/mx_substratevm_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,13 @@ def renaissance_additional_lib(self, lib):
def extra_agent_run_arg(self, benchmark, args, image_run_args):
user_args = super(RenaissanceNativeImageBenchmarkSuite, self).extra_agent_run_arg(benchmark, args, image_run_args)
# remove -r X argument from image run args
return ['-r', '1'] + mx_sdk_benchmark.strip_args_with_number('-r', user_args)
return mx_sdk_benchmark.adjust_arg_with_number('-r', 1, user_args)

def extra_profile_run_arg(self, benchmark, args, image_run_args, should_strip_run_args):
user_args = super(RenaissanceNativeImageBenchmarkSuite, self).extra_profile_run_arg(benchmark, args, image_run_args, should_strip_run_args)
# remove -r X argument from image run args
if should_strip_run_args:
extra_profile_run_args = ['-r', '1'] + mx_sdk_benchmark.strip_args_with_number('-r', user_args)
extra_profile_run_args = mx_sdk_benchmark.adjust_arg_with_number('-r', 1, user_args)
else:
extra_profile_run_args = user_args

Expand Down Expand Up @@ -1185,7 +1185,7 @@ def run(self, benchmarks, bmSuiteArgs) -> mx_benchmark.DataPoints:
def extra_agent_run_arg(self, benchmark, args, image_run_args):
user_args = super(DaCapoNativeImageBenchmarkSuite, self).extra_agent_run_arg(benchmark, args, image_run_args)
# remove -n X argument from image run args
return ['-n', '1'] + mx_sdk_benchmark.strip_args_with_number('-n', user_args)
return mx_sdk_benchmark.adjust_arg_with_number('-n', 1, user_args)

def extra_profile_run_arg(self, benchmark, args, image_run_args, should_strip_run_args):
self.fixDataLocation()
Expand All @@ -1197,7 +1197,7 @@ def extra_profile_run_arg(self, benchmark, args, image_run_args, should_strip_ru

# remove -n X argument from image run args
if should_strip_run_args:
return ['-n', '1'] + mx_sdk_benchmark.strip_args_with_number('-n', user_args)
return mx_sdk_benchmark.adjust_arg_with_number('-n', 1, user_args)
else:
return user_args

Expand Down Expand Up @@ -1342,13 +1342,13 @@ def run(self, benchmarks, bmSuiteArgs) -> mx_benchmark.DataPoints:
def extra_agent_run_arg(self, benchmark, args, image_run_args):
user_args = super(ScalaDaCapoNativeImageBenchmarkSuite, self).extra_agent_run_arg(benchmark, args, image_run_args)
# remove -n X argument from image run args
return mx_sdk_benchmark.strip_args_with_number('-n', user_args) + ['-n', '1']
return mx_sdk_benchmark.adjust_arg_with_number('-n', 1, user_args)

def extra_profile_run_arg(self, benchmark, args, image_run_args, should_strip_run_args):
user_args = super(ScalaDaCapoNativeImageBenchmarkSuite, self).extra_profile_run_arg(benchmark, args, image_run_args, should_strip_run_args)
# remove -n X argument from image run args if the flag is true.
if should_strip_run_args:
return mx_sdk_benchmark.strip_args_with_number('-n', user_args) + ['-n', '1']
return mx_sdk_benchmark.adjust_arg_with_number('-n', 1, user_args)
else:
return user_args

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@
public final class SubstrateMethodCallTargetNode extends MethodCallTargetNode {
public static final NodeClass<SubstrateMethodCallTargetNode> TYPE = NodeClass.create(SubstrateMethodCallTargetNode.class);

/**
* Method profile inferred from a static type profile.
*/
private JavaMethodProfile staticMethodProfile;
/**
* Either the static method profile, or a dynamic method profile inferred from, for example,
* sampling data.
*/
private JavaMethodProfile methodProfile;

/*
Expand All @@ -59,27 +67,68 @@ public SubstrateMethodCallTargetNode(InvokeKind invokeKind, ResolvedJavaMethod t
super(TYPE, invokeKind, targetMethod, arguments, returnStamp, typeProfile);
}

public void setProfiles(JavaTypeProfile typeProfile, JavaMethodProfile methodProfile) {
this.typeProfile = typeProfile;
this.methodProfile = methodProfile;
this.staticTypeProfile = typeProfile;
public void setProfiles(JavaTypeProfile dynamicTypeProfile, JavaTypeProfile staticTypeProfile, JavaMethodProfile dynamicMethodProfile, JavaMethodProfile staticMethodProfile) {
this.typeProfile = dynamicTypeProfile;
this.methodProfile = dynamicMethodProfile;
this.staticTypeProfile = staticTypeProfile;
this.staticMethodProfile = staticMethodProfile;
}

public void setProfiles(JavaTypeProfile typeProfile, JavaMethodProfile methodProfile, JavaTypeProfile staticTypeProfile) {
public void setDynamicProfiles(JavaTypeProfile typeProfile, JavaMethodProfile methodProfile) {
this.typeProfile = typeProfile;
this.methodProfile = methodProfile;
this.staticTypeProfile = staticTypeProfile;
}

/**
* Returns a dynamic method profile if one exists. Otherwise, returns a static method profile if
* one exists. If neither exist, returns null.
*/
public JavaMethodProfile getMethodProfile() {
return methodProfile;
}

/**
* Returns a static type profile if one exists. Otherwise, returns null.
*/
public JavaTypeProfile getStaticTypeProfile() {
return staticTypeProfile;
}

/**
* Returns a static method profile if one exists. Otherwise, returns null.
*/
public JavaMethodProfile getStaticMethodProfile() {
return staticMethodProfile;
}

/**
* Set a dynamic method profile.
*/
public void setJavaMethodProfile(JavaMethodProfile profile) {
methodProfile = profile;
}

/**
* Returns true iff {@code this} has a dynamic type profile. A dynamic type profile can be
* gathered by, for example, instrumentation. A static type profile (as produced by the static
* analysis) does not count as a dynamic profile.
*/
public boolean hasDynamicTypeProfile() {
if (typeProfile == null) {
return false;
}
return !typeProfile.equals(staticTypeProfile);
}

/**
* Returns true iff {@code this} has a dynamic method profile. A dynamic method profile can be
* gathered by, for example, instrumentation or perf samples. A static method profile (as
* produced by the static analysis) does not count as a dynamic profile.
*/
public boolean hasDynamicMethodProfile() {
if (methodProfile == null) {
return false;
}
return !methodProfile.equals(staticMethodProfile);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@
import com.oracle.svm.hosted.code.SubstrateCompilationDirectives;
import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport;
import com.oracle.svm.hosted.meta.HostedType;

import com.oracle.svm.hosted.phases.DynamicAccessDetectionPhase;
import com.oracle.svm.hosted.phases.AnalyzeJavaHomeAccessPhase;
import com.oracle.svm.hosted.phases.DynamicAccessDetectionPhase;

import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.nodes.ConstantNode;
Expand Down Expand Up @@ -149,13 +148,14 @@ protected FixedNode createUnreachable(StructuredGraph graph, CoreProviders provi
@Override
protected void setInvokeProfiles(Invoke invoke, JavaTypeProfile typeProfile, JavaMethodProfile methodProfile) {
if (needsProfiles(invoke.asNode().graph())) {
((SubstrateMethodCallTargetNode) invoke.callTarget()).setProfiles(typeProfile, methodProfile);
((SubstrateMethodCallTargetNode) invoke.callTarget()).setProfiles(typeProfile, typeProfile, methodProfile, methodProfile);
}
}

protected void setInvokeProfiles(Invoke invoke, JavaTypeProfile typeProfile, JavaMethodProfile methodProfile, JavaTypeProfile staticTypeProfile) {
protected void setInvokeProfiles(Invoke invoke, JavaTypeProfile typeProfile, JavaMethodProfile methodProfile, JavaTypeProfile staticTypeProfile, JavaMethodProfile staticMethodProfile) {
if (needsProfiles(invoke.asNode().graph())) {
((SubstrateMethodCallTargetNode) invoke.callTarget()).setProfiles(typeProfile, methodProfile, staticTypeProfile);
SubstrateMethodCallTargetNode substrateCallTarget = (SubstrateMethodCallTargetNode) invoke.callTarget();
substrateCallTarget.setProfiles(typeProfile, staticTypeProfile, methodProfile, staticMethodProfile);
}
}

Expand Down
Loading