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/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); 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..126985193c --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/CachedLazyCalltargetSupplier.java @@ -0,0 +1,77 @@ +/* + * 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..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 @@ -41,6 +41,7 @@ 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; @@ -48,7 +49,6 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; 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; @@ -93,11 +93,14 @@ 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()); + PBuiltinFunction function; + 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()); - 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..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 @@ -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,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, 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 +98,31 @@ 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 +148,7 @@ public static Object[] generateDefaults(int numDefaults) { } public RootNode getFunctionRootNode() { - return callTarget.getRootNode(); + return getCallTarget().getRootNode(); } /** @@ -211,6 +230,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/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java index ef6c73b0ee..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 @@ -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,12 @@ 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 +564,15 @@ 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 +584,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())); } 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'):