From 54998fe7667e66064b575974026617edfa79a370 Mon Sep 17 00:00:00 2001 From: Ivo Horak Date: Mon, 29 Sep 2025 14:47:54 +0200 Subject: [PATCH 1/4] Add lazy init of calltargets for builtins --- .../CachedLazyCalltargetSupplier.java | 78 +++++++++++++++++++ .../graal/python/builtins/PythonBuiltins.java | 21 ++++- .../objects/function/PBuiltinFunction.java | 28 +++++-- .../graal/python/runtime/PythonOptions.java | 3 + .../graal/python/runtime/object/PFactory.java | 15 +++- 5 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java new file mode 100644 index 0000000000..304d7dfbb0 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.builtins; + +import com.oracle.graal.python.util.Supplier; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.RootCallTarget; + +public class CachedLazyCalltargetSupplier { + + private volatile RootCallTarget callTarget = null; + private Supplier supplier; + + + public CachedLazyCalltargetSupplier(Supplier supplier) { + this.supplier = supplier; + } + + public RootCallTarget get() { + CompilerAsserts.neverPartOfCompilation("Must not be part of the PE"); + + // avoids two volatile reads + var alreadySetCallTarget = this.callTarget; + if (alreadySetCallTarget != null) { + return alreadySetCallTarget; + } + + synchronized (this) { + if (callTarget == null) { + callTarget = supplier.get(); + supplier = null; + } + return callTarget; + } + } + + public RootCallTarget getIfExists() { + return callTarget; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java index 551463c831..05072c67d7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java @@ -41,11 +41,13 @@ import com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.ExecBuiltin; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; +import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.BiConsumer; import com.oracle.truffle.api.RootCallTarget; @@ -93,11 +95,22 @@ public void initialize(Python3Core core) { } TruffleString tsName = toTruffleStringUncached(builtin.name()); PythonLanguage language = core.getLanguage(); - RootCallTarget callTarget = language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), factory.getNodeClass(), - builtin.name()); + boolean lazyCallTargets = core.getContext().getOption(PythonOptions.BuiltinLazyCallTargets); + PBuiltinFunction function; + if (lazyCallTargets) { + Signature signature = BuiltinFunctionRootNode.createSignature(factory, builtin, declaresExplicitSelf, false); + int flags = PBuiltinFunction.getFlags(builtin, signature); + var callTargetSupplier = new CachedLazyCalltargetSupplier(() -> language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, signature, builtin, factory, declaresExplicitSelf, null), factory.getNodeClass(), + builtin.name())); + function = PFactory.createBuiltinFunction(language, tsName, numDefaults(builtin),signature, flags, callTargetSupplier); + } else { + RootCallTarget callTarget = language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), factory.getNodeClass(), + builtin.name()); + int flags = PBuiltinFunction.getFlags(builtin, callTarget); + function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, callTarget); + } + Object builtinDoc = builtin.doc().isEmpty() ? PNone.NONE : toTruffleStringUncached(builtin.doc()); - int flags = PBuiltinFunction.getFlags(builtin, callTarget); - PBuiltinFunction function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, callTarget); function.setAttribute(T___DOC__, builtinDoc); BoundBuiltinCallable callable = function; if (builtin.isGetter() || builtin.isSetter()) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java index 23d9c60e5c..d826d71d3d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java @@ -33,6 +33,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.BoundBuiltinCallable; import com.oracle.graal.python.annotations.Builtin; +import com.oracle.graal.python.builtins.CachedLazyCalltargetSupplier; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; @@ -49,6 +50,7 @@ import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; @@ -75,7 +77,8 @@ public final class PBuiltinFunction extends PythonBuiltinObject implements Bound private final PString name; private final TruffleString qualname; private final Object enclosingType; - private final RootCallTarget callTarget; + @CompilationFinal private RootCallTarget callTarget; + private final CachedLazyCalltargetSupplier callTargetSupplier; private final Signature signature; private final int flags; private final TpSlot slot; @@ -83,7 +86,7 @@ public final class PBuiltinFunction extends PythonBuiltinObject implements Bound @CompilationFinal(dimensions = 1) private final Object[] defaults; @CompilationFinal(dimensions = 1) private final PKeyword[] kwDefaults; - public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootCallTarget callTarget, + public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, RootCallTarget callTarget, CachedLazyCalltargetSupplier callTargetSupplier, TpSlot slot, PExternalFunctionWrapper slotWrapper) { super(cls, shape); this.name = PythonUtils.toPString(name); @@ -94,16 +97,27 @@ public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString n } this.enclosingType = enclosingType; this.callTarget = callTarget; - this.signature = ((PRootNode) callTarget.getRootNode()).getSignature(); + this.signature = signature; this.flags = flags; this.defaults = defaults; this.kwDefaults = kwDefaults != null ? kwDefaults : generateKwDefaults(signature); this.slot = slot; this.slotWrapper = slotWrapper; + this.callTargetSupplier = callTargetSupplier; + + /* If the call target supplier has already been run, then don't wait until the first time the InternalMethod is + * asked for the call target, because can cause deoptimization in getCallTarget(). */ + if (callTarget == null && callTargetSupplier != null) { + this.callTarget = callTargetSupplier.getIfExists(); + } } public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootCallTarget callTarget) { - this(cls, shape, name, enclosingType, defaults, kwDefaults, flags, callTarget, null, null); + this(cls, shape, name, enclosingType, defaults, kwDefaults, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, callTarget, null, null, null); + } + + public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, CachedLazyCalltargetSupplier callTargetSupplier) { + this(cls, shape, name, enclosingType, defaults, kwDefaults, signature, flags, null, callTargetSupplier, null, null); } public static PKeyword[] generateKwDefaults(Signature signature) { @@ -129,7 +143,7 @@ public static Object[] generateDefaults(int numDefaults) { } public RootNode getFunctionRootNode() { - return callTarget.getRootNode(); + return getCallTarget().getRootNode(); } /** @@ -211,6 +225,10 @@ public Signature getSignature() { } public RootCallTarget getCallTarget() { + if (callTarget == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + callTarget = callTargetSupplier.get(); + } return callTarget; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java index e9d0a03de9..d31e462662 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java @@ -389,6 +389,9 @@ public static void checkBytecodeDSLEnv() { @EngineOption @Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "If true, uses native storage strategy for primitive types") // public static final OptionKey UseNativePrimitiveStorageStrategy = new OptionKey<>(false); + @Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "If true, CallTargets for builtins are initialized lazily") // + public static final OptionKey BuiltinLazyCallTargets = new OptionKey<>(true); + @Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Print warnings when using experimental features at runtime.", stability = OptionStability.STABLE) // public static final OptionKey WarnExperimentalFeatures = new OptionKey<>(true); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java index ef6c73b0ee..1e728f1189 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java @@ -37,6 +37,7 @@ import javax.net.ssl.SSLEngine; import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.CachedLazyCalltargetSupplier; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins.PosixFileHandle; import com.oracle.graal.python.builtins.modules.bz2.BZ2Object; @@ -239,6 +240,7 @@ import com.oracle.graal.python.builtins.objects.typing.PTypeVar; import com.oracle.graal.python.builtins.objects.typing.PTypeVarTuple; import com.oracle.graal.python.compiler.BytecodeCodeUnit; +import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; @@ -543,6 +545,11 @@ public static PFunction createFunction(PythonLanguage language, TruffleString na return trace(language, new PFunction(language, name, qualname, code, globals, defaultValues, kwDefaultValues, closure, codeStableAssumption)); } + public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, int numDefaults, Signature signature, int flags, CachedLazyCalltargetSupplier callTargetSupplier) { + return trace(language, new PBuiltinFunction(PythonBuiltinClassType.PBuiltinFunction, PythonBuiltinClassType.PBuiltinFunction.getInstanceShape(language), name, null, + PBuiltinFunction.generateDefaults(numDefaults), null, signature, flags, callTargetSupplier)); + } + public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, Object type, int numDefaults, int flags, RootCallTarget callTarget) { return trace(language, new PBuiltinFunction(PythonBuiltinClassType.PBuiltinFunction, PythonBuiltinClassType.PBuiltinFunction.getInstanceShape(language), name, type, PBuiltinFunction.generateDefaults(numDefaults), null, flags, callTarget)); @@ -556,13 +563,13 @@ public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, Tr public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language, TruffleString name, Object type, int numDefaults, int flags, RootCallTarget callTarget, TpSlot slot, PExternalFunctionWrapper slotWrapper) { return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, - PBuiltinFunction.generateDefaults(numDefaults), null, flags, callTarget, slot, slotWrapper)); + PBuiltinFunction.generateDefaults(numDefaults), null, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, callTarget, null, slot, slotWrapper)); } public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language, TruffleString name, Object type, Object[] defaults, PKeyword[] kw, int flags, RootCallTarget callTarget, TpSlot slot, PExternalFunctionWrapper slotWrapper) { - return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, defaults, kw, flags, - callTarget, slot, slotWrapper)); + return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, defaults, kw, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, + callTarget, null, slot, slotWrapper)); } public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object type, Object[] defaults, PKeyword[] kwdefaults, RootCallTarget callTarget, TpSlot slot) { @@ -574,7 +581,7 @@ public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object ty public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, PBuiltinFunction function, Object klass) { PythonBuiltinClassType type = (PythonBuiltinClassType) function.getPythonClass(); return trace(language, new PBuiltinFunction(type, type.getInstanceShape(language), function.getName(), klass, - function.getDefaults(), function.getKwDefaults(), function.getFlags(), function.getCallTarget(), + function.getDefaults(), function.getKwDefaults(), function.getSignature(), function.getFlags(), function.getCallTarget(), null, function.getSlot(), function.getSlotWrapper())); } From cc6b74f33aa827d45d409c791ee547ed87220c76 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Wed, 1 Oct 2025 08:53:43 +0200 Subject: [PATCH 2/4] Make call targets for builtin methods always lazy, without option --- .../CachedLazyCalltargetSupplier.java | 1 - .../graal/python/builtins/PythonBuiltins.java | 22 +++++-------------- .../objects/function/PBuiltinFunction.java | 15 ++++++++----- .../graal/python/runtime/PythonOptions.java | 3 --- .../graal/python/runtime/object/PFactory.java | 11 ++++++---- 5 files changed, 23 insertions(+), 29 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java index 304d7dfbb0..126985193c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java @@ -49,7 +49,6 @@ public class CachedLazyCalltargetSupplier { private volatile RootCallTarget callTarget = null; private Supplier supplier; - public CachedLazyCalltargetSupplier(Supplier supplier) { this.supplier = supplier; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java index 05072c67d7..d11c0fc8dd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java @@ -47,10 +47,8 @@ import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; -import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.BiConsumer; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.strings.TruffleString; @@ -95,21 +93,13 @@ public void initialize(Python3Core core) { } TruffleString tsName = toTruffleStringUncached(builtin.name()); PythonLanguage language = core.getLanguage(); - boolean lazyCallTargets = core.getContext().getOption(PythonOptions.BuiltinLazyCallTargets); PBuiltinFunction function; - if (lazyCallTargets) { - Signature signature = BuiltinFunctionRootNode.createSignature(factory, builtin, declaresExplicitSelf, false); - int flags = PBuiltinFunction.getFlags(builtin, signature); - var callTargetSupplier = new CachedLazyCalltargetSupplier(() -> language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, signature, builtin, factory, declaresExplicitSelf, null), factory.getNodeClass(), - builtin.name())); - function = PFactory.createBuiltinFunction(language, tsName, numDefaults(builtin),signature, flags, callTargetSupplier); - } else { - RootCallTarget callTarget = language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), factory.getNodeClass(), - builtin.name()); - int flags = PBuiltinFunction.getFlags(builtin, callTarget); - function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, callTarget); - } - + Signature signature = BuiltinFunctionRootNode.createSignature(factory, builtin, declaresExplicitSelf, false); + int flags = PBuiltinFunction.getFlags(builtin, signature); + var callTargetSupplier = new CachedLazyCalltargetSupplier( + () -> language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, signature, builtin, factory, declaresExplicitSelf, null), factory.getNodeClass(), + builtin.name())); + function = PFactory.createBuiltinFunction(language, tsName, numDefaults(builtin), signature, flags, callTargetSupplier); Object builtinDoc = builtin.doc().isEmpty() ? PNone.NONE : toTruffleStringUncached(builtin.doc()); function.setAttribute(T___DOC__, builtinDoc); BoundBuiltinCallable callable = function; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java index d826d71d3d..f0f8817da1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java @@ -86,7 +86,8 @@ public final class PBuiltinFunction extends PythonBuiltinObject implements Bound @CompilationFinal(dimensions = 1) private final Object[] defaults; @CompilationFinal(dimensions = 1) private final PKeyword[] kwDefaults; - public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, RootCallTarget callTarget, CachedLazyCalltargetSupplier callTargetSupplier, + public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, + RootCallTarget callTarget, CachedLazyCalltargetSupplier callTargetSupplier, TpSlot slot, PExternalFunctionWrapper slotWrapper) { super(cls, shape); this.name = PythonUtils.toPString(name); @@ -105,8 +106,11 @@ public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString n this.slotWrapper = slotWrapper; this.callTargetSupplier = callTargetSupplier; - /* If the call target supplier has already been run, then don't wait until the first time the InternalMethod is - * asked for the call target, because can cause deoptimization in getCallTarget(). */ + /* + * If the call target supplier has already been run, then don't wait until the first time + * the InternalMethod is asked for the call target, because can cause deoptimization in + * getCallTarget(). + */ if (callTarget == null && callTargetSupplier != null) { this.callTarget = callTargetSupplier.getIfExists(); } @@ -116,7 +120,8 @@ public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString n this(cls, shape, name, enclosingType, defaults, kwDefaults, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, callTarget, null, null, null); } - public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, CachedLazyCalltargetSupplier callTargetSupplier) { + public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, + CachedLazyCalltargetSupplier callTargetSupplier) { this(cls, shape, name, enclosingType, defaults, kwDefaults, signature, flags, null, callTargetSupplier, null, null); } @@ -225,7 +230,7 @@ public Signature getSignature() { } public RootCallTarget getCallTarget() { - if (callTarget == null) { + if (callTarget == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); callTarget = callTargetSupplier.get(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java index d31e462662..e9d0a03de9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java @@ -389,9 +389,6 @@ public static void checkBytecodeDSLEnv() { @EngineOption @Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "If true, uses native storage strategy for primitive types") // public static final OptionKey UseNativePrimitiveStorageStrategy = new OptionKey<>(false); - @Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "If true, CallTargets for builtins are initialized lazily") // - public static final OptionKey BuiltinLazyCallTargets = new OptionKey<>(true); - @Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Print warnings when using experimental features at runtime.", stability = OptionStability.STABLE) // public static final OptionKey WarnExperimentalFeatures = new OptionKey<>(true); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java index 1e728f1189..55838694dc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java @@ -545,9 +545,10 @@ public static PFunction createFunction(PythonLanguage language, TruffleString na return trace(language, new PFunction(language, name, qualname, code, globals, defaultValues, kwDefaultValues, closure, codeStableAssumption)); } - public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, int numDefaults, Signature signature, int flags, CachedLazyCalltargetSupplier callTargetSupplier) { + public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, int numDefaults, Signature signature, int flags, + CachedLazyCalltargetSupplier callTargetSupplier) { return trace(language, new PBuiltinFunction(PythonBuiltinClassType.PBuiltinFunction, PythonBuiltinClassType.PBuiltinFunction.getInstanceShape(language), name, null, - PBuiltinFunction.generateDefaults(numDefaults), null, signature, flags, callTargetSupplier)); + PBuiltinFunction.generateDefaults(numDefaults), null, signature, flags, callTargetSupplier)); } public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, Object type, int numDefaults, int flags, RootCallTarget callTarget) { @@ -568,8 +569,10 @@ public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language, public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language, TruffleString name, Object type, Object[] defaults, PKeyword[] kw, int flags, RootCallTarget callTarget, TpSlot slot, PExternalFunctionWrapper slotWrapper) { - return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, defaults, kw, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, - callTarget, null, slot, slotWrapper)); + return trace(language, + new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, defaults, kw, + ((PRootNode) callTarget.getRootNode()).getSignature(), flags, + callTarget, null, slot, slotWrapper)); } public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object type, Object[] defaults, PKeyword[] kwdefaults, RootCallTarget callTarget, TpSlot slot) { From 55c1690cb8b42ccdd60a5133b182fe7d38f0fd03 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Thu, 2 Oct 2025 10:51:00 +0200 Subject: [PATCH 3/4] Add a micro benchmark for startup --- .../python/micro/startup.py | 61 +++++++++++++++++++ mx.graalpython/mx_graalpython_bench_param.py | 1 + 2 files changed, 62 insertions(+) create mode 100644 graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py new file mode 100644 index 0000000000..7ab6f6f070 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py @@ -0,0 +1,61 @@ +# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +import subprocess, sys + + +def __benchmark__(num=1000000): + while num > 0: + num -= 1 + orig_vm_argv = sys.orig_argv + for i, arg in enumerate(orig_vm_argv): + if arg.endswith('.py'): + orig_vm_argv = orig_vm_argv[:i] + break + subprocess.check_call([ + *orig_vm_argv, + "-I", # isolate from environment + "-S", # do not import site + "-Wignore", # ignore all warnings + "-B", # do not attempt to write pyc files + "-u", # do not add buffering wrappers around output streams + "-c", + "1" + ]) diff --git a/mx.graalpython/mx_graalpython_bench_param.py b/mx.graalpython/mx_graalpython_bench_param.py index cdc908376b..c56d8022f4 100644 --- a/mx.graalpython/mx_graalpython_bench_param.py +++ b/mx.graalpython/mx_graalpython_bench_param.py @@ -186,6 +186,7 @@ 'c-issubtype-monorphic': ITER_5 + ['200000'], 'c-call-method': ITER_5 + ['50000'], 'c-call-method-int-float': ITER_5 + ['500000'], + 'startup': ITER_5 + ['50'], } def _pickling_benchmarks(module='pickle'): From 17fefcffce89a0514501065ceb0968cd2b8b3a96 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Mon, 6 Oct 2025 20:26:54 +0200 Subject: [PATCH 4/4] Be a bit more lenient with heap growth --- .../src/com/oracle/graal/python/test/advanced/LeakTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java index 6d6acc8cf6..2bef8eaf9b 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java @@ -316,7 +316,7 @@ protected void launch(Builder contextBuilder) { } long currentSize = getJavaHeapSize(false); System.out.printf("Heap size after all repetitions: %,d\n", currentSize); - if (currentSize > initialSize * 1.1) { + if (currentSize > initialSize * 1.15) { System.err.printf("Heap size grew too much after repeated context creations and invocations. From %,d bytes to %,d bytes.\n", initialSize, currentSize); if (keepDump) { dumpHeap(ManagementFactory.getPlatformMBeanServer(), true);