Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Dec 17, 2025

📄 5% (0.05x) speedup for removesuffix in keras/src/utils/python_utils.py

⏱️ Runtime : 69.5 microseconds 66.1 microseconds (best of 224 runs)

📝 Explanation and details

The optimization eliminates a redundant len(suffix) call by computing it once and storing the result. In the original code, len(suffix) was called twice when a suffix exists and matches: once in the conditional check and again when slicing the string. The optimized version calculates suffix_len = len(suffix) upfront and reuses this value.

The restructured control flow also avoids calling x.endswith(suffix) when the suffix is empty (length 0), since an empty suffix would never match anyway. This provides a small performance benefit for empty suffix cases.

Based on the test results, the optimization shows consistent improvements across most scenarios:

  • Best gains (10-15% faster) occur with longer suffixes that actually match and get removed, where the double len() call elimination has the most impact
  • Modest gains (3-8% faster) for typical suffix removal cases
  • Minimal impact on edge cases like empty suffixes or non-matching cases

Given the function references, this optimization is particularly valuable since removesuffix is called within layer building operations in Keras, specifically for processing argument names with _shape suffixes. In neural network frameworks, layer building can happen frequently during model construction, making even small optimizations worthwhile in the aggregate.

The 5% overall speedup may seem modest, but for a utility function that's likely called many times during model setup and potentially in training loops, this optimization provides measurable performance benefits without any change to the API or behavior.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 109 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest  # used for our unit tests
from keras.src.utils.python_utils import removesuffix

# unit tests

# =======================
# BASIC TEST CASES
# =======================

def test_basic_remove_existing_suffix():
    # Removes a simple suffix
    codeflash_output = removesuffix("foobar", "bar") # 912ns -> 878ns (3.87% faster)
    # Suffix at end only is removed
    codeflash_output = removesuffix("testcase", "case") # 432ns -> 373ns (15.8% faster)
    # Suffix is the entire string
    codeflash_output = removesuffix("hello", "hello") # 332ns -> 309ns (7.44% faster)
    # Suffix is a single character
    codeflash_output = removesuffix("abc", "c") # 320ns -> 326ns (1.84% slower)
    # Suffix is not present
    codeflash_output = removesuffix("foobar", "baz") # 272ns -> 264ns (3.03% faster)

def test_basic_empty_suffix():
    # Suffix is empty string, should return input unchanged
    codeflash_output = removesuffix("foobar", "") # 473ns -> 461ns (2.60% faster)
    codeflash_output = removesuffix("", "") # 247ns -> 219ns (12.8% faster)
    codeflash_output = removesuffix("a", "") # 165ns -> 173ns (4.62% slower)

def test_basic_empty_string():
    # String is empty, suffix is non-empty
    codeflash_output = removesuffix("", "foo") # 660ns -> 655ns (0.763% faster)
    # Both string and suffix empty
    codeflash_output = removesuffix("", "") # 276ns -> 272ns (1.47% faster)

def test_basic_suffix_not_at_end():
    # Suffix is present but not at the end
    codeflash_output = removesuffix("barfoo", "foo") # 901ns -> 847ns (6.38% faster)
    codeflash_output = removesuffix("foofoobar", "foo") # 373ns -> 361ns (3.32% faster)
    codeflash_output = removesuffix("foobarfoo", "bar") # 220ns -> 221ns (0.452% slower)

# =======================
# EDGE TEST CASES
# =======================

def test_edge_suffix_longer_than_string():
    # Suffix longer than string: should return string unchanged
    codeflash_output = removesuffix("foo", "foobar") # 652ns -> 601ns (8.49% faster)
    codeflash_output = removesuffix("", "foobar") # 295ns -> 297ns (0.673% slower)

def test_edge_suffix_is_whitespace():
    # Suffix is whitespace
    codeflash_output = removesuffix("hello ", " ") # 945ns -> 900ns (5.00% faster)
    codeflash_output = removesuffix("hello", " ") # 341ns -> 371ns (8.09% slower)
    codeflash_output = removesuffix(" ", " ") # 381ns -> 337ns (13.1% faster)

def test_edge_suffix_is_special_characters():
    # Suffix is special characters
    codeflash_output = removesuffix("abc$%^", "$%^") # 911ns -> 844ns (7.94% faster)
    codeflash_output = removesuffix("abc$%^", "^") # 446ns -> 435ns (2.53% faster)
    codeflash_output = removesuffix("abc$%^", "&") # 280ns -> 282ns (0.709% slower)

def test_edge_suffix_is_unicode():
    # Suffix is unicode
    codeflash_output = removesuffix("café", "é") # 1.10μs -> 1.02μs (7.71% faster)
    codeflash_output = removesuffix("добрый", "й") # 731ns -> 716ns (2.09% faster)
    codeflash_output = removesuffix("你好世界", "世界") # 423ns -> 419ns (0.955% faster)

def test_edge_suffix_multiple_occurrences():
    # Suffix occurs multiple times, only the last is removed
    codeflash_output = removesuffix("testtest", "test") # 906ns -> 845ns (7.22% faster)
    codeflash_output = removesuffix("testtesttest", "test") # 459ns -> 393ns (16.8% faster)
    codeflash_output = removesuffix("aaaaa", "a") # 320ns -> 318ns (0.629% faster)

def test_edge_suffix_is_substring_of_suffix():
    # Suffix is a substring of the end of the string, but not a full match
    codeflash_output = removesuffix("testing", "ingg") # 649ns -> 639ns (1.56% faster)
    codeflash_output = removesuffix("testingg", "ingg") # 584ns -> 551ns (5.99% faster)

def test_edge_suffix_is_none_or_non_str():
    # Suffix is None or non-str: should raise TypeError
    with pytest.raises(TypeError):
        removesuffix("foobar", None)
    with pytest.raises(TypeError):
        removesuffix("foobar", 123)
    with pytest.raises(TypeError):
        removesuffix(123, "bar")
    with pytest.raises(TypeError):
        removesuffix(None, "bar")

def test_edge_suffix_is_empty_and_string_is_empty():
    # Both empty
    codeflash_output = removesuffix("", "") # 630ns -> 607ns (3.79% faster)

def test_edge_suffix_is_same_as_string():
    # Suffix is exactly the string
    codeflash_output = removesuffix("abc", "abc") # 1.08μs -> 1.00μs (8.29% faster)

def test_edge_suffix_is_partial_match_at_end():
    # Only part of suffix matches at end
    codeflash_output = removesuffix("foobar", "ar") # 997ns -> 974ns (2.36% faster)
    codeflash_output = removesuffix("foobar", "bar") # 395ns -> 393ns (0.509% faster)
    codeflash_output = removesuffix("foobar", "foo") # 312ns -> 295ns (5.76% faster)

def test_edge_suffix_is_overlapping():
    # Suffix overlaps with itself in string
    codeflash_output = removesuffix("aaaaaa", "aaa") # 929ns -> 900ns (3.22% faster)
    codeflash_output = removesuffix("aaaaaa", "aaaa") # 353ns -> 325ns (8.62% faster)

# =======================
# LARGE SCALE TEST CASES
# =======================

def test_large_scale_long_string_suffix():
    # Large string, suffix at end
    long_str = "a" * 1000 + "END"
    codeflash_output = removesuffix(long_str, "END") # 1.30μs -> 1.19μs (8.82% faster)
    # Suffix not present
    codeflash_output = removesuffix(long_str, "XYZ") # 300ns -> 330ns (9.09% slower)

def test_large_scale_long_suffix():
    # Suffix almost as long as string
    s = "x" * 999 + "y"
    suffix = "x" * 999 + "y"
    codeflash_output = removesuffix(s, suffix) # 1.30μs -> 1.13μs (14.6% faster)
    # Suffix longer than string
    codeflash_output = removesuffix("short", "x" * 1000) # 332ns -> 347ns (4.32% slower)

def test_large_scale_repeated_pattern():
    # String is repeated pattern, suffix is one repetition
    s = "abc" * 333 + "xyz"
    codeflash_output = removesuffix(s, "xyz") # 1.20μs -> 1.20μs (0.250% faster)
    # Suffix is multiple repetitions
    s2 = "abc" * 333 + "xyzxyz"
    codeflash_output = removesuffix(s2, "xyzxyz") # 679ns -> 591ns (14.9% faster)

def test_large_scale_suffix_at_start_not_end():
    # Suffix at start, not end
    s = "START" + "a" * 995
    codeflash_output = removesuffix(s, "START") # 736ns -> 714ns (3.08% faster)

def test_large_scale_empty_suffix_on_long_string():
    # Empty suffix, long string
    s = "x" * 1000
    codeflash_output = removesuffix(s, "") # 465ns -> 472ns (1.48% slower)

def test_large_scale_all_same_char():
    # String and suffix are all same character
    s = "z" * 1000
    suffix = "z" * 500
    codeflash_output = removesuffix(s, suffix) # 1.37μs -> 1.19μs (15.7% faster)
    # Suffix is not present (different char)
    codeflash_output = removesuffix(s, "y" * 500) # 378ns -> 383ns (1.31% slower)

def test_large_scale_unicode():
    # Large string with unicode suffix
    s = "α" * 999 + "β"
    suffix = "β"
    codeflash_output = removesuffix(s, suffix) # 1.68μs -> 1.58μs (6.01% faster)

def test_large_scale_multiple_suffixes():
    # Suffix occurs multiple times, only last is removed
    s = "foo" * 333 + "bar"
    codeflash_output = removesuffix(s, "bar") # 1.19μs -> 1.16μs (2.41% faster)
    s2 = "foo" * 333 + "barbar"
    codeflash_output = removesuffix(s2, "barbar") # 726ns -> 640ns (13.4% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
import pytest  # used for our unit tests
from keras.src.utils.python_utils import removesuffix

# unit tests

# --------------------- BASIC TEST CASES ---------------------

def test_removesuffix_basic_removal():
    # Suffix is present at the end
    codeflash_output = removesuffix("foobar", "bar") # 935ns -> 847ns (10.4% faster)
    # Suffix is not present
    codeflash_output = removesuffix("foobar", "baz") # 387ns -> 385ns (0.519% faster)
    # Suffix is empty
    codeflash_output = removesuffix("foobar", "") # 223ns -> 226ns (1.33% slower)
    # Suffix is the entire string
    codeflash_output = removesuffix("foobar", "foobar") # 454ns -> 410ns (10.7% faster)
    # Suffix is a single character at the end
    codeflash_output = removesuffix("hello!", "!") # 353ns -> 345ns (2.32% faster)
    # Suffix is a single character not at the end
    codeflash_output = removesuffix("hello!", "?") # 236ns -> 245ns (3.67% slower)

def test_removesuffix_basic_unicode():
    # Suffix is a unicode character at the end
    codeflash_output = removesuffix("café", "é") # 1.06μs -> 1.01μs (5.67% faster)
    # Suffix is a unicode string
    codeflash_output = removesuffix("добрыйдень", "день") # 770ns -> 705ns (9.22% faster)
    # Suffix is not present (unicode)
    codeflash_output = removesuffix("добрыйдень", "ночи") # 340ns -> 304ns (11.8% faster)

def test_removesuffix_basic_empty_string():
    # Both string and suffix are empty
    codeflash_output = removesuffix("", "") # 490ns -> 461ns (6.29% faster)
    # String is empty, suffix is non-empty
    codeflash_output = removesuffix("", "abc") # 468ns -> 475ns (1.47% slower)

# --------------------- EDGE TEST CASES ---------------------

def test_removesuffix_suffix_longer_than_string():
    # Suffix is longer than the string
    codeflash_output = removesuffix("foo", "foobar") # 657ns -> 626ns (4.95% faster)
    # Suffix is much longer than the string
    codeflash_output = removesuffix("a", "longsuffix") # 310ns -> 301ns (2.99% faster)

def test_removesuffix_suffix_is_substring_but_not_suffix():
    # Suffix occurs in the string, but not at the end
    codeflash_output = removesuffix("banana", "an") # 661ns -> 635ns (4.09% faster)
    # Suffix is a substring, but not at the end
    codeflash_output = removesuffix("mississippi", "iss") # 313ns -> 325ns (3.69% slower)

def test_removesuffix_suffix_is_empty():
    # Suffix is empty, should return original string
    codeflash_output = removesuffix("abc", "") # 487ns -> 454ns (7.27% faster)
    codeflash_output = removesuffix("", "") # 253ns -> 229ns (10.5% faster)

def test_removesuffix_suffix_is_space():
    # Suffix is a space at the end
    codeflash_output = removesuffix("hello ", " ") # 964ns -> 874ns (10.3% faster)
    # Suffix is a space not at the end
    codeflash_output = removesuffix("hello world", " ") # 345ns -> 394ns (12.4% slower)

def test_removesuffix_string_and_suffix_are_identical():
    # String and suffix are identical
    codeflash_output = removesuffix("abc", "abc") # 868ns -> 899ns (3.45% slower)
    # String and suffix are identical, but suffix is repeated
    codeflash_output = removesuffix("abcabc", "abcabc") # 495ns -> 509ns (2.75% slower)

def test_removesuffix_suffix_is_multiple_characters():
    # Suffix is multiple characters at the end
    codeflash_output = removesuffix("testing123", "123") # 914ns -> 860ns (6.28% faster)
    # Suffix is multiple characters not at the end
    codeflash_output = removesuffix("testing123", "test") # 354ns -> 383ns (7.57% slower)

def test_removesuffix_suffix_is_special_characters():
    # Suffix is special character at the end
    codeflash_output = removesuffix("hello!", "!") # 914ns -> 861ns (6.16% faster)
    # Suffix is special character not at the end
    codeflash_output = removesuffix("hello!", "?") # 374ns -> 382ns (2.09% slower)

def test_removesuffix_suffix_is_newline():
    # Suffix is newline at the end
    codeflash_output = removesuffix("hello\n", "\n") # 880ns -> 858ns (2.56% faster)
    # Suffix is newline not at the end
    codeflash_output = removesuffix("hello\nworld", "\n") # 359ns -> 384ns (6.51% slower)

def test_removesuffix_suffix_is_null_char():
    # Suffix is null character at the end
    codeflash_output = removesuffix("foo\0", "\0") # 865ns -> 814ns (6.27% faster)
    # Suffix is null character not at the end
    codeflash_output = removesuffix("foo\0bar", "\0") # 367ns -> 353ns (3.97% faster)

def test_removesuffix_suffix_is_partial_overlap():
    # Suffix partially overlaps with end of string
    codeflash_output = removesuffix("foobarbaz", "barbaz") # 935ns -> 935ns (0.000% faster)
    # Suffix partially overlaps but not at end
    codeflash_output = removesuffix("foobarbaz", "bazbar") # 371ns -> 362ns (2.49% faster)

def test_removesuffix_suffix_is_repeated():
    # Suffix is repeated at the end
    codeflash_output = removesuffix("abcabcabc", "abc") # 918ns -> 876ns (4.79% faster)
    # Suffix is repeated but only last occurrence is removed
    codeflash_output = removesuffix("abcabcabc", "abcabc") # 471ns -> 474ns (0.633% slower)

def test_removesuffix_suffix_is_case_sensitive():
    # Suffix is same letters, different case
    codeflash_output = removesuffix("foobar", "BAR") # 637ns -> 653ns (2.45% slower)
    # Suffix matches only if case matches
    codeflash_output = removesuffix("foobar", "bar") # 638ns -> 572ns (11.5% faster)

# --------------------- LARGE SCALE TEST CASES ---------------------

def test_removesuffix_large_string_and_suffix():
    # Large string, suffix present
    s = "a" * 999 + "END"
    codeflash_output = removesuffix(s, "END") # 1.31μs -> 1.21μs (8.38% faster)
    # Large string, suffix not present
    s = "b" * 1000
    codeflash_output = removesuffix(s, "END") # 400ns -> 371ns (7.82% faster)
    # Large suffix, exactly matches end
    s = "x" * 500 + "y" * 500
    suffix = "y" * 500
    codeflash_output = removesuffix(s, suffix) # 924ns -> 687ns (34.5% faster)

def test_removesuffix_large_suffix_longer_than_string():
    # Suffix longer than string, should not remove
    s = "short"
    suffix = "a" * 1000
    codeflash_output = removesuffix(s, suffix) # 733ns -> 659ns (11.2% faster)

def test_removesuffix_large_suffix_partial_match():
    # Suffix partially matches end of string, but not full
    s = "a" * 998 + "bc"
    suffix = "abc"
    codeflash_output = removesuffix(s, suffix) # 1.22μs -> 1.18μs (3.14% faster)

def test_removesuffix_large_string_suffix_is_whole_string():
    # Suffix is the whole string, should return empty string
    s = "z" * 1000
    codeflash_output = removesuffix(s, s) # 1.19μs -> 1.08μs (9.40% faster)

def test_removesuffix_large_string_suffix_is_empty():
    # Suffix is empty, should return original string
    s = "x" * 1000
    codeflash_output = removesuffix(s, "") # 480ns -> 495ns (3.03% slower)

def test_removesuffix_large_string_suffix_is_single_char():
    # Suffix is single character at the end
    s = "a" * 999 + "z"
    codeflash_output = removesuffix(s, "z") # 1.27μs -> 1.24μs (2.50% faster)

def test_removesuffix_large_string_suffix_not_present():
    # Suffix is not present
    s = "a" * 1000
    codeflash_output = removesuffix(s, "b") # 671ns -> 642ns (4.52% faster)

def test_removesuffix_large_string_suffix_is_special_char():
    # Suffix is special character at the end of large string
    s = "x" * 999 + "!"
    codeflash_output = removesuffix(s, "!") # 1.32μs -> 1.17μs (13.3% faster)

# --------------------- ADDITIONAL EDGE CASES ---------------------

def test_removesuffix_suffix_is_whitespace():
    # Suffix is whitespace at the end
    codeflash_output = removesuffix("foo ", " ") # 952ns -> 857ns (11.1% faster)
    # Suffix is tab at the end
    codeflash_output = removesuffix("foo\t", "\t") # 405ns -> 363ns (11.6% faster)
    # Suffix is multiple whitespace
    codeflash_output = removesuffix("foo   ", "   ") # 302ns -> 327ns (7.65% slower)

def test_removesuffix_suffix_is_multiple_null_chars():
    # Suffix is multiple null chars at the end
    s = "abc\0\0"
    codeflash_output = removesuffix(s, "\0\0") # 895ns -> 854ns (4.80% faster)

def test_removesuffix_suffix_is_non_ascii():
    # Suffix is non-ascii at the end
    s = "helloπ"
    codeflash_output = removesuffix(s, "π") # 1.22μs -> 1.11μs (10.1% faster)
    # Suffix is emoji at the end
    s = "goodbye🙂"
    codeflash_output = removesuffix(s, "🙂") # 721ns -> 770ns (6.36% slower)

def test_removesuffix_suffix_is_combining_char():
    # Suffix is combining character at the end
    s = "e\u0301"  # é as e + combining acute
    codeflash_output = removesuffix(s, "\u0301") # 964ns -> 959ns (0.521% faster)
    # Suffix is combining character not at the end
    s = "e\u0301e"
    codeflash_output = removesuffix(s, "\u0301") # 414ns -> 399ns (3.76% faster)

def test_removesuffix_suffix_is_number():
    # Suffix is number at the end
    s = "item42"
    codeflash_output = removesuffix(s, "42") # 929ns -> 832ns (11.7% faster)
    # Suffix is number not at the end
    s = "42item"
    codeflash_output = removesuffix(s, "42") # 381ns -> 399ns (4.51% slower)

def test_removesuffix_suffix_is_multiple_types():
    # Suffix is mix of letters, numbers, and symbols
    s = "foo123!@#"
    codeflash_output = removesuffix(s, "123!@#") # 967ns -> 972ns (0.514% slower)
    # Suffix is mix but not at end
    s = "foo123!@#bar"
    codeflash_output = removesuffix(s, "123!@#") # 366ns -> 398ns (8.04% slower)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-removesuffix-mja6gylj and push.

Codeflash Static Badge

The optimization eliminates a redundant `len(suffix)` call by computing it once and storing the result. In the original code, `len(suffix)` was called twice when a suffix exists and matches: once in the conditional check and again when slicing the string. The optimized version calculates `suffix_len = len(suffix)` upfront and reuses this value.

The restructured control flow also avoids calling `x.endswith(suffix)` when the suffix is empty (length 0), since an empty suffix would never match anyway. This provides a small performance benefit for empty suffix cases.

Based on the test results, the optimization shows consistent improvements across most scenarios:
- **Best gains** (10-15% faster) occur with longer suffixes that actually match and get removed, where the double `len()` call elimination has the most impact
- **Modest gains** (3-8% faster) for typical suffix removal cases
- **Minimal impact** on edge cases like empty suffixes or non-matching cases

Given the function references, this optimization is particularly valuable since `removesuffix` is called within layer building operations in Keras, specifically for processing argument names with `_shape` suffixes. In neural network frameworks, layer building can happen frequently during model construction, making even small optimizations worthwhile in the aggregate.

The 5% overall speedup may seem modest, but for a utility function that's likely called many times during model setup and potentially in training loops, this optimization provides measurable performance benefits without any change to the API or behavior.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 December 17, 2025 15:40
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Dec 17, 2025
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.

1 participant