From 9c8315dad2d2bba7427093937ffb9dded3281bb8 Mon Sep 17 00:00:00 2001 From: Thomas Wuerthinger Date: Wed, 15 Apr 2026 21:12:45 +0200 Subject: [PATCH 1/2] Split build-time and runtime cached call targets --- .../oracle/graal/python/PythonLanguage.java | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index 1522e51998..506dee0dbb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -48,6 +48,7 @@ import java.util.concurrent.Semaphore; import java.util.logging.Level; +import org.graalvm.collections.EconomicMap; import org.graalvm.home.Version; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.options.OptionDescriptors; @@ -311,12 +312,15 @@ public boolean isSingleContext() { public final Assumption noInteropTypeRegisteredAssumption = Truffle.getRuntime().createAssumption("No class for interop registered"); /** - * A thread-safe map to retrieve (and cache) singleton instances of call targets, e.g., for - * Arithmetic operations, wrappers, named cext functions, etc. This reduces the number of call - * targets and allows AST sharing across contexts. The key in this map is either a single value - * or a list of values. + * Call targets that are populated during native-image build time and then only read at image + * runtime. Using {@link EconomicMap} avoids embedding a large number of + * {@link java.util.concurrent.ConcurrentHashMap.Node} objects into the image heap. */ - private final ConcurrentHashMap cachedCallTargets = new ConcurrentHashMap<>(); + private final EconomicMap imageBuildtimeCachedCallTargets = EconomicMap.create(); + /** + * Call targets added after image startup, or all cached call targets when running on the JVM. + */ + private final ConcurrentHashMap runtimeCachedCallTargets = new ConcurrentHashMap<>(); @CompilationFinal(dimensions = 1) private final RootCallTarget[] builtinSlotsCallTargets; @@ -1157,7 +1161,7 @@ public RootCallTarget createCachedPropAccessCallTarget(Function rootNodeFunction, Object key, boolean cacheInSingleContext) { CompilerAsserts.neverPartOfCompilation(); if (cacheInSingleContext || !singleContext) { - return cachedCallTargets.computeIfAbsent(key, k -> PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this))); + return getOrCreateCachedCallTarget(rootNodeFunction, key); } else { return PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this)); } @@ -1167,6 +1171,28 @@ private RootCallTarget createCachedCallTargetUnsafe(Function rootNodeFunction, Object key) { + if (ImageInfo.inImageRuntimeCode()) { + RootCallTarget preinitialized = imageBuildtimeCachedCallTargets.get(key); + if (preinitialized != null) { + return preinitialized; + } + return runtimeCachedCallTargets.computeIfAbsent(key, k -> PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this))); + } + if (ImageInfo.inImageBuildtimeCode()) { + synchronized (imageBuildtimeCachedCallTargets) { + RootCallTarget cached = imageBuildtimeCachedCallTargets.get(key); + if (cached == null) { + cached = PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this)); + imageBuildtimeCachedCallTargets.put(key, cached); + } + return cached; + } + } + return runtimeCachedCallTargets.computeIfAbsent(key, k -> PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this))); + } + @Override protected void exitContext(PythonContext context, ExitMode exitMode, int exitCode) { if (context.getCApiContext() != null) { From 5ea0c514168526408972ab9e6158f7747cea1533 Mon Sep 17 00:00:00 2001 From: Thomas Wuerthinger Date: Fri, 17 Apr 2026 11:26:11 +0200 Subject: [PATCH 2/2] Refine cached call target initialization --- .../src/com/oracle/graal/python/PythonLanguage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index 506dee0dbb..19bd3d0c2e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -316,7 +316,7 @@ public boolean isSingleContext() { * runtime. Using {@link EconomicMap} avoids embedding a large number of * {@link java.util.concurrent.ConcurrentHashMap.Node} objects into the image heap. */ - private final EconomicMap imageBuildtimeCachedCallTargets = EconomicMap.create(); + private final EconomicMap imageBuildtimeCachedCallTargets = ImageInfo.inImageCode() ? EconomicMap.create() : null; /** * Call targets added after image startup, or all cached call targets when running on the JVM. */ @@ -1171,8 +1171,8 @@ private RootCallTarget createCachedCallTargetUnsafe(Function rootNodeFunction, Object key) { + CompilerAsserts.neverPartOfCompilation(); if (ImageInfo.inImageRuntimeCode()) { RootCallTarget preinitialized = imageBuildtimeCachedCallTargets.get(key); if (preinitialized != null) {