From 86558ac01e7399db01e12b59a3b8389228becfdd Mon Sep 17 00:00:00 2001 From: stepan Date: Fri, 2 Oct 2026 10:26:51 +0200 Subject: [PATCH 1/3] Use ArrayBuilder for whitespace byte splits --- .../objects/bytes/BytesCommonBuiltins.java | 79 +++++++------------ .../graal/python/util/ArrayBuilder.java | 8 ++ 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java index 6f619b72be..558e063b3d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java @@ -50,10 +50,7 @@ import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; import java.util.List; import com.oracle.graal.python.PythonLanguage; @@ -124,6 +121,7 @@ import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; +import com.oracle.graal.python.util.ArrayBuilder; import com.oracle.graal.python.util.OverflowException; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -142,6 +140,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @@ -1353,7 +1352,7 @@ static byte[] replaceWithEmptySub(byte[] bytes, int len, byte[] replacementBytes @TruffleBoundary(allowInlining = true) static byte[] replace(byte[] bytes, int len, byte[] sub, byte[] replacementBytes, int count) { int i, j, pos, maxcount = count, subLen = sub.length, repLen = replacementBytes.length; - List list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); int resultLen = 0; i = 0; @@ -1378,9 +1377,8 @@ static byte[] replace(byte[] bytes, int len, byte[] sub, byte[] replacementBytes i = 0; byte[] result = new byte[resultLen]; - Iterator it = iterator(list); - while (hasNext(it)) { - byte[] b = next(it); + for (int k = 0; k < list.size(); k++) { + byte[] b = list.get(k); PythonUtils.arraycopy(b, 0, result, i, b.length); i += b.length; } @@ -1537,11 +1535,11 @@ abstract static class AbstractSplitNode extends PythonTernaryClinicBuiltinNode { protected static final byte[] WHITESPACE = new byte[]{' '}; - protected abstract List splitWhitespace(byte[] bytes, int size, int maxsplit); + protected abstract ArrayBuilder splitWhitespace(byte[] bytes, int size, int maxsplit); - protected abstract List splitSingle(byte[] bytes, int size, byte sep, int maxsplit); + protected abstract ArrayBuilder splitSingle(byte[] bytes, int size, byte sep, int maxsplit); - protected abstract List splitDelimiter(byte[] bytes, int size, byte[] sep, int maxsplit); + protected abstract ArrayBuilder splitDelimiter(byte[] bytes, int size, byte[] sep, int maxsplit); protected static boolean isEmptySep(byte[] sep) { return sep.length == 0; @@ -1605,13 +1603,13 @@ static PList error(Object bytes, byte[] sep, int maxsplit, throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.ValueError, ErrorMessages.EMPTY_SEPARATOR); } - private static PList getBytesResult(List bytes, ListNodes.AppendNode appendNode, Object self, Node inliningTarget, BytesNodes.CreateBytesNode createBytesNode, - PythonLanguage language) { + private static PList getBytesResult(ArrayBuilder bytes, ListNodes.AppendNode appendNode, Object self, Node inliningTarget, + BytesNodes.CreateBytesNode createBytesNode, PythonLanguage language) { PList result = PFactory.createList(language); - Iterator it = iterator(bytes); - while (hasNext(it)) { - appendNode.execute(result, createBytesNode.execute(inliningTarget, self, next(it))); + for (int i = 0; i < bytes.size(); i++) { + appendNode.execute(result, createBytesNode.execute(inliningTarget, self, bytes.get(i))); } + LoopNode.reportLoopCount(inliningTarget, bytes.size()); return result; } } @@ -1633,9 +1631,9 @@ protected ArgumentClinicProvider getArgumentClinic() { @Override @TruffleBoundary - protected List splitWhitespace(byte[] bytes, int len, int maxsplit) { + protected ArrayBuilder splitWhitespace(byte[] bytes, int len, int maxsplit) { int i, j, maxcount = maxsplit; - List list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); i = 0; while (maxcount-- > 0) { @@ -1667,9 +1665,9 @@ protected List splitWhitespace(byte[] bytes, int len, int maxsplit) { } @Override - protected List splitSingle(byte[] bytes, int len, byte sep, int maxsplit) { + protected ArrayBuilder splitSingle(byte[] bytes, int len, byte sep, int maxsplit) { int i, j, maxcount = maxsplit; - List list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); i = j = 0; while ((j < len) && (maxcount-- > 0)) { @@ -1689,9 +1687,9 @@ protected List splitSingle(byte[] bytes, int len, byte sep, int maxsplit } @Override - protected List splitDelimiter(byte[] bytes, int len, byte[] sep, int maxsplit) { + protected ArrayBuilder splitDelimiter(byte[] bytes, int len, byte[] sep, int maxsplit) { int i, j, pos, maxcount = maxsplit, sepLen = sep.length; - List list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); i = 0; while (maxcount-- > 0) { @@ -1725,15 +1723,10 @@ protected ArgumentClinicProvider getArgumentClinic() { return BytesCommonBuiltinsClinicProviders.RSplitNodeClinicProviderGen.INSTANCE; } - @TruffleBoundary - private static void reverseList(ArrayList list) { - Collections.reverse(list); - } - @Override - protected List splitWhitespace(byte[] bytes, int len, int maxsplit) { + protected ArrayBuilder splitWhitespace(byte[] bytes, int len, int maxsplit) { int i, j, maxcount = maxsplit; - ArrayList list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); i = len - 1; while (maxcount-- > 0) { @@ -1761,14 +1754,14 @@ protected List splitWhitespace(byte[] bytes, int len, int maxsplit) { list.add(copyOfRange(bytes, 0, i + 1)); } } - reverseList(list); + list.reverse(); return list; } @Override - protected List splitSingle(byte[] bytes, int len, byte sep, int maxsplit) { + protected ArrayBuilder splitSingle(byte[] bytes, int len, byte sep, int maxsplit) { int i, j, maxcount = maxsplit; - ArrayList list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); i = j = len - 1; while ((i >= 0) && (maxcount-- > 0)) { @@ -1783,14 +1776,14 @@ protected List splitSingle(byte[] bytes, int len, byte sep, int maxsplit if (j >= -1) { list.add(copyOfRange(bytes, 0, j + 1)); } - reverseList(list); + list.reverse(); return list; } @Override - protected List splitDelimiter(byte[] bytes, int len, byte[] sep, int maxsplit) { + protected ArrayBuilder splitDelimiter(byte[] bytes, int len, byte[] sep, int maxsplit) { int j, pos, maxcount = maxsplit, sepLen = sep.length; - ArrayList list = new ArrayList<>(); + ArrayBuilder list = new ArrayBuilder<>(); if (sepLen == 1) { return splitSingle(bytes, len, sep[0], maxcount); @@ -1806,9 +1799,8 @@ protected List splitDelimiter(byte[] bytes, int len, byte[] sep, int max j = pos; } list.add(copyOfRange(bytes, 0, j)); - reverseList(list); + list.reverse(); return list; - } } @@ -2281,21 +2273,6 @@ static byte[] copyOfRange(byte[] bytes, int from, int to) { return PythonUtils.arrayCopyOfRange(bytes, from, to); } - @TruffleBoundary(allowInlining = true) - static Iterator iterator(List bytes) { - return bytes.iterator(); - } - - @TruffleBoundary(allowInlining = true) - static byte[] next(Iterator it) { - return it.next(); - } - - @TruffleBoundary(allowInlining = true) - static boolean hasNext(Iterator it) { - return it.hasNext(); - } - @Builtin(name = J___GETNEWARGS__, minNumOfPositionalArgs = 1) @GenerateNodeFactory public abstract static class GetNewargsNode extends PythonUnaryBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/ArrayBuilder.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/ArrayBuilder.java index 149e5a1606..58d170bcae 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/ArrayBuilder.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/ArrayBuilder.java @@ -95,6 +95,14 @@ public T peek() { return (T) data[size - 1]; } + public void reverse() { + for (int left = 0, right = size - 1; left < right; left++, right--) { + Object item = data[left]; + data[left] = data[right]; + data[right] = item; + } + } + public boolean containsIdentical(Object o) { for (int i = 0; i < size; i++) { if (data[i] == o) { From 1c85ef3a9764727c4f8db4e58d5b5116f0385ce1 Mon Sep 17 00:00:00 2001 From: stepan Date: Fri, 2 Oct 2026 10:31:27 +0200 Subject: [PATCH 2/3] Use ArrayBuilder for frame locals snapshots --- .../builtins/objects/frame/FrameLocalsProxyBuiltins.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameLocalsProxyBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameLocalsProxyBuiltins.java index 69a92ef1cb..10dd053c17 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameLocalsProxyBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameLocalsProxyBuiltins.java @@ -40,7 +40,6 @@ */ package com.oracle.graal.python.builtins.objects.frame; -import java.util.ArrayList; import java.util.List; import com.oracle.graal.python.PythonLanguage; @@ -82,6 +81,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.util.ArrayBuilder; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.bytecode.BytecodeFrame; import com.oracle.truffle.api.dsl.Bind; @@ -90,6 +90,7 @@ import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -190,7 +191,7 @@ private static Object[] snapshotEntries(PFrameLocalsProxy self, PythonLanguage l PDict snapshot = snapshot(self, language, inliningTarget, setItem); HashingStorage storage = snapshot.getDictStorage(); HashingStorageIterator iterator = getIterator.execute(inliningTarget, storage); - List result = new ArrayList<>(); + ArrayBuilder result = new ArrayBuilder<>(); while (iteratorNext.execute(inliningTarget, storage, iterator)) { Object key = iteratorKey.execute(inliningTarget, storage, iterator); Object value = iteratorValue.execute(inliningTarget, storage, iterator); @@ -200,7 +201,8 @@ private static Object[] snapshotEntries(PFrameLocalsProxy self, PythonLanguage l case ITEMS -> PFactory.createTuple(language, new Object[]{key, value}); }); } - return result.toArray(); + LoopNode.reportLoopCount(inliningTarget, result.size()); + return result.toObjectArray(); } @Slot(value = SlotKind.mp_subscript, isComplex = true) From 8e38f9590015fcfb5d3ec0c2ae968c27f6d4580d Mon Sep 17 00:00:00 2001 From: stepan Date: Fri, 2 Oct 2026 10:34:53 +0200 Subject: [PATCH 3/3] Use ArrayBuilder for string split results --- .../python/builtins/objects/str/StringUtils.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java index 298da48ed4..a88f049a89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java @@ -43,15 +43,14 @@ import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; import java.util.Locale; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacter; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacterCategory; import org.graalvm.shadowed.com.ibm.icu.lang.UProperty; +import com.oracle.graal.python.util.ArrayBuilder; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -60,6 +59,7 @@ import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleStringBuilder; @@ -343,24 +343,20 @@ public static TruffleString[] split(TruffleString s, TruffleString sep, TruffleS if (selfLen == sepLen && eqNode.execute(s, sep, TS_ENCODING)) { return PythonUtils.EMPTY_TRUFFLESTRING_ARRAY; } - List l = new ArrayList<>(); + ArrayBuilder l = new ArrayBuilder<>(); while (lastEnd < selfLen) { int nextIndex = indexOfStringNode.execute(s, sep, lastEnd, selfLen, TS_ENCODING); if (nextIndex < 0) { break; } - add(l, substringNode.execute(s, lastEnd, nextIndex - lastEnd, TS_ENCODING, false)); + l.add(substringNode.execute(s, lastEnd, nextIndex - lastEnd, TS_ENCODING, false)); lastEnd = nextIndex + sepLen; } - add(l, substringNode.execute(s, lastEnd, selfLen - lastEnd, TS_ENCODING, false)); + LoopNode.reportLoopCount(substringNode, l.size()); + l.add(substringNode.execute(s, lastEnd, selfLen - lastEnd, TS_ENCODING, false)); return l.toArray(new TruffleString[l.size()]); } - @TruffleBoundary - private static void add(List l, TruffleString s) { - l.add(s); - } - @TruffleBoundary public static TruffleString cat(TruffleString arg1, TruffleString arg2) { return arg1.concatUncached(arg2, TS_ENCODING, false);