Skip to content

⚡️ Speed up method StringUtils.indexOf by 157%#4

Open
codeflash-ai[bot] wants to merge 1 commit intomainfrom
codeflash/optimize-StringUtils.indexOf-mnn7a4yp
Open

⚡️ Speed up method StringUtils.indexOf by 157%#4
codeflash-ai[bot] wants to merge 1 commit intomainfrom
codeflash/optimize-StringUtils.indexOf-mnn7a4yp

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai bot commented Apr 6, 2026

📄 157% (1.57x) speedup for StringUtils.indexOf in rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java

⏱️ Runtime : 3.00 milliseconds 1.17 milliseconds (best of 139 runs)

📝 Explanation and details

The optimization hoists text.length() into a local variable len and caches the predicate reference as t, eliminating repeated virtual method calls inside the hot loop. The original profiler shows the loop condition (i < text.length()) consumed 74.1% of total time at ~1596 ns per hit, while the optimized version reduces per-hit cost to ~318 ns by querying length once upfront. Additionally, extracting text.charAt(i) into a local char c before the predicate test avoids redundant character retrieval, cutting the overall runtime from 3.00 ms to 1.17 ms (157% speedup) with no behavioral changes.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 23 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage Coverage data not available
🌀 Click to see Generated Regression Tests
package org.openrewrite.internal;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.util.function.Predicate;

import static org.junit.jupiter.api.Assertions.*;

import org.openrewrite.internal.StringUtils;

public class StringUtilsTest {
    private StringUtils instance;

    @BeforeEach
    void setUp() throws Exception {
        // StringUtils has a private constructor; create an instance reflectively to satisfy the requirement.
        Constructor<StringUtils> ctor = StringUtils.class.getDeclaredConstructor();
        ctor.setAccessible(true);
        instance = ctor.newInstance();
    }

    @Test
    void testBasicSearch_ReturnsCorrectIndex() {
        int idx = StringUtils.indexOf("abcdef", 0, ch -> ch == 'c');
    }

    @Test
    void testFromIndexGreaterThanMatch_ReturnsMinusOne() {
        int idx = StringUtils.indexOf("abcdef", 3, ch -> ch == 'c');
    }

    @Test
    void testEmptyString_ReturnsMinusOne() {
        int idx = StringUtils.indexOf("", 0, ch -> true);
    }

    @Test
    void testFromIndexEqualsLength_ReturnsMinusOne() {
        String s = "abc";
        int idx = StringUtils.indexOf(s, s.length(), ch -> true);
    }

    @Test
    void testFromIndexBeyondLength_ReturnsMinusOne() {
        String s = "abc";
        int idx = StringUtils.indexOf(s, 10, ch -> true);
    }

    @Test
    void testNegativeFromIndex_ThrowsStringIndexOutOfBoundsException() {
        try { StringUtils.indexOf("abc", -1, ch -> true); } catch (StringIndexOutOfBoundsException ignored) {}
    }

    @Test
    void testNullText_ThrowsNullPointerException() {
        try { StringUtils.indexOf(null, 0, ch -> true); } catch (NullPointerException ignored) {}
    }

    @Test
    void testNullPredicate_ThrowsNullPointerException() {
        try { StringUtils.indexOf("abc", 0, null); } catch (NullPointerException ignored) {}
    }

    @Test
    void testFromIndexPointsToMatch_ReturnsSameIndex() {
        int idx = StringUtils.indexOf("xyz", 1, ch -> ch == 'y');
    }

    @Test
    void testLargeInput_FindLastCharacter() {
        // Build a large string of 'a' with a single 'b' at the end to verify behavior on big inputs.
        int size = 200_000;
        StringBuilder sb = new StringBuilder(size);
        for (int i = 0; i < size - 1; i++) {
            sb.append('a');
        }
        sb.append('b');
        String large = sb.toString();

        int idx = StringUtils.indexOf(large, 0, ch -> ch == 'b');
    }

    @Test
    void testSurrogatePairHighSurrogateDetection_ReturnsHighSurrogateIndex() {
        // "A" + GRINNING FACE (surrogate pair) + "B"
        String s = "A\uD83D\uDE00B";
        // The high surrogate is at index 1
        int idx = StringUtils.indexOf(s, 0, Character::isHighSurrogate);
    }
}
package org.openrewrite.internal;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.util.function.Predicate;

import static org.junit.jupiter.api.Assertions.*;
import org.openrewrite.internal.StringUtils;

public class StringUtilsTest_2 {
    private StringUtils instance;

    @BeforeEach
    void setUp() throws Exception {
        // StringUtils has a private constructor; create an instance via reflection as requested.
        Constructor<StringUtils> ctor = StringUtils.class.getDeclaredConstructor();
        ctor.setAccessible(true);
        instance = ctor.newInstance();
    }

    @Test
    void testTypicalInput_FindsFirstMatch() {
        String text = "hello world";
        int idx = StringUtils.indexOf(text, 0, c -> c == 'o');
    }

    @Test
    void testStartFromNonZero_SkipsEarlierMatches() {
        String text = "hello world";
        int idx = StringUtils.indexOf(text, 5, c -> c == 'o');
    }

    @Test
    void testNoMatch_ReturnsMinusOne() {
        String text = "abcdef";
        int idx = StringUtils.indexOf(text, 0, c -> c == 'z');
    }

    @Test
    void testEmptyText_ReturnsMinusOne() {
        String text = "";
        int idx = StringUtils.indexOf(text, 0, c -> true);
    }

    @Test
    void testNullText_ThrowsNullPointerException() {
        Predicate<Character> any = c -> true;
        try { StringUtils.indexOf(null, 0, any); } catch (NullPointerException ignored) {}
    }

    @Test
    void testNullPredicateWithNonEmptyText_ThrowsNullPointerException() {
        try { StringUtils.indexOf("abc", 0, null); } catch (NullPointerException ignored) {}
    }

    @Test
    void testNullPredicateWithEmptyText_NoExceptionAndReturnsMinusOne() {
        // When text is empty, the predicate is never invoked and null predicate should not cause an exception.
        int idx = StringUtils.indexOf("", 0, null);
    }

    @Test
    void testNegativeFromIndex_ThrowsStringIndexOutOfBoundsException() {
        try { StringUtils.indexOf("abc", -1, c -> c == 'a'); } catch (StringIndexOutOfBoundsException ignored) {}
    }

    @Test
    void testFromIndexBeyondLength_ReturnsMinusOne() {
        String text = "abc";
        int idx = StringUtils.indexOf(text, 5, c -> true);
    }

    @Test
    void testMultipleMatches_ReturnsFirstAfterStart() {
        String text = "aaabaaa";
        int idx = StringUtils.indexOf(text, 3, c -> c == 'a');
    }

    @Test
    void testStartIndexAtLength_ReturnsMinusOne() {
        String text = "abc";
        int idx = StringUtils.indexOf(text, text.length(), c -> true);
    }

    @Test
    void testLargeInput_FindMatchNearEnd() {
        // Build a large string with many 'a' and a single 'b' at the end to verify correctness on large input.
        int size = 100_000;
        StringBuilder sb = new StringBuilder(size + 1);
        for (int i = 0; i < size; i++) {
            sb.append('a');
        }
        sb.append('b');
        String large = sb.toString();

        int idx = StringUtils.indexOf(large, 0, c -> c == 'b');
    }
}

To edit these changes git checkout codeflash/optimize-StringUtils.indexOf-mnn7a4yp and push.

Codeflash Static Badge

The optimization hoists `text.length()` into a local variable `len` and caches the predicate reference as `t`, eliminating repeated virtual method calls inside the hot loop. The original profiler shows the loop condition (`i < text.length()`) consumed 74.1% of total time at ~1596 ns per hit, while the optimized version reduces per-hit cost to ~318 ns by querying length once upfront. Additionally, extracting `text.charAt(i)` into a local `char c` before the predicate test avoids redundant character retrieval, cutting the overall runtime from 3.00 ms to 1.17 ms (157% speedup) with no behavioral changes.
@codeflash-ai codeflash-ai bot requested a review from HeshamHM28 April 6, 2026 13:02
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants