Skip to content

Commit 8f9b4d2

Browse files
committed
[GR-27248] Performance improvements in str.__getitem__ with slice.
PullRequest: graalpython/1370
2 parents 96f975e + 9fa9fd6 commit 8f9b4d2

File tree

2 files changed

+72
-37
lines changed

2 files changed

+72
-37
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
import com.oracle.truffle.api.dsl.TypeSystemReference;
156156
import com.oracle.truffle.api.frame.VirtualFrame;
157157
import com.oracle.truffle.api.library.CachedLibrary;
158+
import com.oracle.truffle.api.nodes.Node;
158159
import com.oracle.truffle.api.profiles.BranchProfile;
159160
import com.oracle.truffle.api.profiles.ConditionProfile;
160161
import com.oracle.truffle.api.profiles.LoopConditionProfile;
@@ -2249,42 +2250,82 @@ protected String make(String self, int width, String fill) {
22492250

22502251
}
22512252

2253+
public abstract static class StrGetItemNodeWithSlice extends Node {
2254+
2255+
public abstract String execute(String value, SliceInfo info);
2256+
2257+
static boolean isEmptySlice(SliceInfo s) {
2258+
int step = s.step;
2259+
int start = s.start;
2260+
int stop = s.stop;
2261+
return (step >= 0 && stop <= start) || (step <= 0 && stop >= start);
2262+
}
2263+
2264+
static boolean isSimpleSlice(SliceInfo s) {
2265+
return s.step == 1 && s.stop > s.start;
2266+
}
2267+
2268+
@Specialization(guards = "isSimpleSlice(slice)")
2269+
static String doStepOneStopGtStart(String value, SliceInfo slice) {
2270+
return getSubString(value, slice.start, slice.stop);
2271+
}
2272+
2273+
@Specialization(guards = "isEmptySlice(slice)")
2274+
static String doEmptySlice(@SuppressWarnings("unused") String value, @SuppressWarnings("unused") SliceInfo slice) {
2275+
return "";
2276+
}
2277+
2278+
@Specialization(guards = {"step == slice.step", "!isSimpleSlice(slice)", "!isEmptySlice(slice)"}, limit = "1")
2279+
static String doGenericCachedStep(String value, SliceInfo slice,
2280+
@Cached("slice.step") int step,
2281+
@Shared("loop") @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
2282+
@Shared("len") @Cached LenOfRangeNode sliceLen) {
2283+
int len = sliceLen.len(slice);
2284+
int start = slice.start;
2285+
char[] newChars = new char[len];
2286+
int j = 0;
2287+
loopProfile.profileCounted(len);
2288+
for (int i = start; loopProfile.inject(j < len); i += step) {
2289+
newChars[j++] = value.charAt(i);
2290+
}
2291+
return PythonUtils.newString(newChars);
2292+
}
2293+
2294+
@Specialization(replaces = "doGenericCachedStep", guards = {"!isSimpleSlice(slice)", "!isEmptySlice(slice)"})
2295+
static String doGeneric(String value, SliceInfo slice,
2296+
@Shared("loop") @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
2297+
@Shared("len") @Cached LenOfRangeNode sliceLen) {
2298+
return doGenericCachedStep(value, slice, slice.step, loopProfile, sliceLen);
2299+
}
2300+
2301+
@TruffleBoundary(allowInlining = true)
2302+
private static String getSubString(String origin, int start, int stop) {
2303+
return origin.substring(start, stop);
2304+
}
2305+
}
2306+
22522307
@Builtin(name = __GETITEM__, minNumOfPositionalArgs = 2)
22532308
@GenerateNodeFactory
22542309
@TypeSystemReference(PythonArithmeticTypes.class)
22552310
public abstract static class StrGetItemNode extends PythonBinaryBuiltinNode {
22562311

2257-
@Specialization
2258-
public String doString(String primary, PSlice slice,
2312+
@Specialization(guards = "isString(primary)")
2313+
public String doString(Object primary, PSlice slice,
2314+
@Cached CastToJavaStringNode castToJavaString,
22592315
@Cached CoerceToIntSlice sliceCast,
22602316
@Cached ComputeIndices compute,
2261-
@Cached LenOfRangeNode sliceLen) {
2262-
SliceInfo info = compute.execute(sliceCast.execute(slice), primary.length());
2263-
final int sliceLength = sliceLen.len(info);
2264-
final int start = info.start;
2265-
int stop = info.stop;
2266-
int step = info.step;
2267-
2268-
if (step > 0 && stop < start) {
2269-
stop = start;
2270-
}
2271-
if (step == 1) {
2272-
return getSubString(primary, start, stop);
2273-
} else {
2274-
char[] newChars = new char[sliceLength];
2275-
int j = 0;
2276-
for (int i = start; j < sliceLength; i += step) {
2277-
newChars[j++] = primary.charAt(i);
2278-
}
2279-
2280-
return PythonUtils.newString(newChars);
2281-
}
2317+
@Cached StrGetItemNodeWithSlice getItemNodeWithSlice) {
2318+
String str = castToJavaString.execute(primary);
2319+
SliceInfo info = compute.execute(sliceCast.execute(slice), str.length());
2320+
return getItemNodeWithSlice.execute(str, info);
22822321
}
22832322

2284-
@Specialization(limit = "getCallSiteInlineCacheMaxDepth()")
2285-
public String doString(VirtualFrame frame, String primary, Object idx,
2323+
@Specialization(guards = {"!isPSlice(idx)", "isString(primary)"}, limit = "getCallSiteInlineCacheMaxDepth()")
2324+
public String doString(VirtualFrame frame, Object primary, Object idx,
2325+
@Cached CastToJavaStringNode castToJavaString,
22862326
@Cached("createBinaryProfile()") ConditionProfile hasFrame,
22872327
@CachedLibrary("idx") PythonObjectLibrary lib) {
2328+
String str = castToJavaString.execute(primary);
22882329
int index;
22892330
if (hasFrame.profile(frame != null)) {
22902331
index = lib.asSizeWithState(idx, PArguments.getThreadState(frame));
@@ -2293,9 +2334,9 @@ public String doString(VirtualFrame frame, String primary, Object idx,
22932334
}
22942335
try {
22952336
if (index < 0) {
2296-
index += primary.length();
2337+
index += str.length();
22972338
}
2298-
return charAtToString(primary, index);
2339+
return charAtToString(str, index);
22992340
} catch (StringIndexOutOfBoundsException | ArithmeticException e) {
23002341
throw raise(IndexError, ErrorMessages.STRING_INDEX_OUT_OF_RANGE);
23012342
}
@@ -2307,17 +2348,10 @@ Object doGeneric(Object self, Object other) {
23072348
return PNotImplemented.NOT_IMPLEMENTED;
23082349
}
23092350

2310-
@TruffleBoundary
2311-
private static String getSubString(String origin, int start, int stop) {
2312-
char[] chars = new char[stop - start];
2313-
origin.getChars(start, stop, chars, 0);
2314-
return new String(chars);
2315-
}
2316-
23172351
@TruffleBoundary
23182352
private static String charAtToString(String primary, int index) {
2319-
char charactor = primary.charAt(index);
2320-
return new String(new char[]{charactor});
2353+
char character = primary.charAt(index);
2354+
return new String(new char[]{character});
23212355
}
23222356
}
23232357

mx.graalpython/mx_graalpython.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ def run_python_unittests(python_binary, args=None, paths=None, aot_compatible=Tr
598598
return mx.run([python_binary] + args, nonZeroIsFatal=True, env=env)
599599

600600

601-
def run_hpy_unittests(python_binary, args=[]):
601+
def run_hpy_unittests(python_binary, args=None):
602+
args = [] if args is None else args
602603
with tempfile.TemporaryDirectory(prefix='hpy-test-site-') as d:
603604
env = os.environ.copy()
604605
prefix = str(d)

0 commit comments

Comments
 (0)