Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Oct 21, 2025

📄 15% (0.15x) speedup for gcd_recursive in src/math/computation.py

⏱️ Runtime : 24.2 microseconds 21.0 microseconds (best of 701 runs)

📝 Explanation and details

The optimization replaces recursion with iteration, converting the recursive GCD implementation into an iterative while loop. This eliminates Python's function call overhead, which is significant due to stack frame creation/destruction and parameter passing on each recursive call.

Key Changes:

  • Replaced if b == 0: return a with while b != 0:
  • Replaced return gcd_recursive(b, a % b) with a, b = b, a % b
  • Added final return a after the loop

Why This Is Faster:

  1. Eliminates function call overhead: Each recursive call creates a new stack frame with parameter binding and return handling. The iterative version reuses the same variables in a single stack frame.
  2. Reduces memory allocation: No stack buildup from recursive calls, preventing potential stack overflow for deep recursions.
  3. Better CPU cache locality: All operations happen in the same function scope with fewer memory accesses.

Performance Characteristics:

  • Shows 15-35% speedup on most test cases, particularly effective for inputs requiring many iterations (like consecutive Fibonacci numbers showing 29% improvement)
  • Zero speedup when b=0 initially (no loop iterations needed)
  • Best gains on cases with moderate recursion depth where call overhead dominates computation time
  • Maintains identical mathematical behavior while being more efficient for all non-trivial inputs

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 85 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 1 Passed
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest  # used for our unit tests
from src.math.computation import gcd_recursive

# unit tests

# ----------------------------
# 1. Basic Test Cases
# ----------------------------

def test_gcd_basic_positive_numbers():
    # Test with two positive integers
    codeflash_output = gcd_recursive(48, 18) # 292ns -> 250ns (16.8% faster)

def test_gcd_basic_one_is_multiple_of_other():
    # Test where one number is a multiple of the other
    codeflash_output = gcd_recursive(21, 7) # 208ns -> 167ns (24.6% faster)
    codeflash_output = gcd_recursive(7, 21) # 166ns -> 125ns (32.8% faster)

def test_gcd_basic_coprime_numbers():
    # Test with coprime numbers (GCD should be 1)
    codeflash_output = gcd_recursive(13, 28) # 333ns -> 250ns (33.2% faster)

def test_gcd_basic_equal_numbers():
    # Test where both numbers are equal (GCD should be the number itself)
    codeflash_output = gcd_recursive(15, 15) # 208ns -> 167ns (24.6% faster)

def test_gcd_basic_one_zero():
    # Test where one number is zero
    codeflash_output = gcd_recursive(0, 5) # 208ns -> 208ns (0.000% faster)
    codeflash_output = gcd_recursive(5, 0) # 83ns -> 83ns (0.000% faster)

def test_gcd_basic_both_zero():
    # Test where both numbers are zero (mathematically undefined, but function returns 0)
    codeflash_output = gcd_recursive(0, 0) # 125ns -> 125ns (0.000% faster)

# ----------------------------
# 2. Edge Test Cases
# ----------------------------

def test_gcd_negative_numbers():
    # Test with negative numbers (GCD should be positive)
    codeflash_output = gcd_recursive(-48, 18) # 250ns -> 250ns (0.000% faster)
    codeflash_output = gcd_recursive(48, -18) # 250ns -> 208ns (20.2% faster)
    codeflash_output = gcd_recursive(-48, -18) # 250ns -> 250ns (0.000% faster)

def test_gcd_negative_and_zero():
    # Test with negative and zero
    codeflash_output = gcd_recursive(-5, 0) # 125ns -> 125ns (0.000% faster)
    codeflash_output = gcd_recursive(0, -5) # 166ns -> 125ns (32.8% faster)

def test_gcd_one_and_large_number():
    # Test with 1 and a large number (GCD should be 1)
    codeflash_output = gcd_recursive(1, 999999) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(999999, 1) # 166ns -> 125ns (32.8% faster)

def test_gcd_prime_and_composite():
    # Test with a prime and a composite number
    codeflash_output = gcd_recursive(17, 34) # 250ns -> 208ns (20.2% faster)
    codeflash_output = gcd_recursive(34, 17) # 84ns -> 83ns (1.20% faster)

def test_gcd_min_int_values():
    # Test with Python's minimum integer values
    # In Python, ints are unbounded, but test with large negative values
    codeflash_output = gcd_recursive(-2147483648, 2147483647) # 542ns -> 541ns (0.185% faster)

def test_gcd_zero_and_zero():
    # Test with both arguments zero
    codeflash_output = gcd_recursive(0, 0) # 125ns -> 125ns (0.000% faster)

def test_gcd_large_negative_and_positive():
    # Test with a large negative and large positive number
    codeflash_output = gcd_recursive(-1000000000, 500000000) # 208ns -> 208ns (0.000% faster)

# ----------------------------
# 3. Large Scale Test Cases
# ----------------------------

def test_gcd_large_numbers():
    # Test with very large numbers
    a = 12345678901234567890
    b = 98765432109876543210
    # GCD should be 900000000090
    codeflash_output = gcd_recursive(a, b) # 417ns -> 458ns (8.95% slower)

def test_gcd_large_coprime_numbers():
    # Test with two large coprime numbers
    a = 9999999967  # prime
    b = 9999999961  # prime
    codeflash_output = gcd_recursive(a, b) # 458ns -> 375ns (22.1% faster)

def test_gcd_large_power_of_two():
    # Test with powers of two
    a = 2**500
    b = 2**300
    # GCD should be 2**300
    codeflash_output = gcd_recursive(a, b) # 500ns -> 458ns (9.17% faster)

def test_gcd_large_nontrivial():
    # Test with large numbers with a nontrivial GCD
    a = 12345678901234567890 * 17
    b = 12345678901234567890 * 23
    # GCD should be 12345678901234567890
    codeflash_output = gcd_recursive(a, b) # 667ns -> 666ns (0.150% faster)

def test_gcd_many_recursive_steps():
    # Test with numbers that require many recursive steps
    # Fibonacci numbers are good for this
    a = 832040  # F(30)
    b = 514229  # F(29)
    # GCD of consecutive Fibonacci numbers is 1
    codeflash_output = gcd_recursive(a, b) # 1.29μs -> 1.00μs (29.2% faster)

def test_gcd_large_negative_and_large_positive():
    # Test with a large negative and large positive number
    a = -999999999999999999
    b = 999999999999999999
    codeflash_output = gcd_recursive(a, b) # 333ns -> 292ns (14.0% faster)

# ----------------------------
# 4. Property-based and Miscellaneous Tests
# ----------------------------

@pytest.mark.parametrize("a,b", [
    (100, 10),
    (10, 100),
    (0, 100),
    (100, 0),
    (-100, 10),
    (10, -100),
    (-100, -10),
    (10, -10),
])
def test_gcd_sign_invariance(a, b):
    # GCD should always be non-negative
    codeflash_output = gcd_recursive(a, b); result = codeflash_output # 1.87μs -> 1.58μs (18.3% faster)

@pytest.mark.parametrize("a,b", [
    (123, 456),
    (456, 123),
    (-123, 456),
    (123, -456),
    (-123, -456),
])
def test_gcd_commutative(a, b):
    # GCD should be commutative: gcd(a, b) == gcd(b, a)
    codeflash_output = gcd_recursive(a, b) # 2.54μs -> 2.08μs (21.9% faster)

def test_gcd_recursive_property():
    # Test the recursive property: gcd(a, b) == gcd(b, a % b)
    a, b = 987654321, 123456789
    codeflash_output = gcd_recursive(a, b) # 291ns -> 250ns (16.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 src.math.computation import gcd_recursive

# unit tests

# 1. Basic Test Cases

def test_gcd_basic_coprime():
    # Coprime numbers (no common divisors except 1)
    codeflash_output = gcd_recursive(13, 17) # 333ns -> 291ns (14.4% faster)
    codeflash_output = gcd_recursive(101, 10) # 166ns -> 125ns (32.8% faster)

def test_gcd_basic_non_coprime():
    # Non-coprime numbers (common divisors)
    codeflash_output = gcd_recursive(12, 8) # 250ns -> 209ns (19.6% faster)
    codeflash_output = gcd_recursive(100, 10) # 125ns -> 83ns (50.6% faster)
    codeflash_output = gcd_recursive(81, 27) # 83ns -> 83ns (0.000% faster)

def test_gcd_basic_reverse_order():
    # Test symmetry (order of arguments should not matter)
    codeflash_output = gcd_recursive(8, 12) # 291ns -> 250ns (16.4% faster)
    codeflash_output = gcd_recursive(10, 100) # 125ns -> 83ns (50.6% faster)
    codeflash_output = gcd_recursive(27, 81) # 83ns -> 83ns (0.000% faster)

def test_gcd_basic_with_one():
    # GCD with 1 should always be 1
    codeflash_output = gcd_recursive(1, 999) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(999, 1) # 166ns -> 125ns (32.8% faster)

def test_gcd_basic_with_zero():
    # GCD with zero should be abs of the other number
    codeflash_output = gcd_recursive(0, 5) # 208ns -> 167ns (24.6% faster)
    codeflash_output = gcd_recursive(5, 0) # 83ns -> 83ns (0.000% faster)
    codeflash_output = gcd_recursive(0, 0) # 41ns -> 42ns (2.38% slower)

# 2. Edge Test Cases

def test_gcd_negative_numbers():
    # Negative inputs should behave as their absolute values
    codeflash_output = gcd_recursive(-12, 8) # 291ns -> 250ns (16.4% faster)
    codeflash_output = gcd_recursive(12, -8) # 208ns -> 125ns (66.4% faster)
    codeflash_output = gcd_recursive(-12, -8) # 166ns -> 125ns (32.8% faster)
    codeflash_output = gcd_recursive(-13, -17) # 250ns -> 208ns (20.2% faster)

def test_gcd_one_and_zero():
    # Edge cases with 1 and 0
    codeflash_output = gcd_recursive(0, 1) # 208ns -> 208ns (0.000% faster)
    codeflash_output = gcd_recursive(1, 0) # 83ns -> 83ns (0.000% faster)

def test_gcd_same_numbers():
    # GCD of a number with itself should be the number
    codeflash_output = gcd_recursive(7, 7) # 208ns -> 167ns (24.6% faster)
    codeflash_output = gcd_recursive(-7, -7) # 166ns -> 125ns (32.8% faster)
    codeflash_output = gcd_recursive(0, 0) # 83ns -> 42ns (97.6% faster)

def test_gcd_large_prime_and_small():
    # Large prime and small number
    codeflash_output = gcd_recursive(1000003, 2) # 250ns -> 250ns (0.000% faster)
    codeflash_output = gcd_recursive(2, 1000003) # 250ns -> 166ns (50.6% faster)

def test_gcd_large_prime_and_large_prime():
    # Two large distinct primes
    codeflash_output = gcd_recursive(1000003, 1000033) # 458ns -> 333ns (37.5% faster)

def test_gcd_large_prime_and_multiple():
    # Large prime and its multiple
    codeflash_output = gcd_recursive(1000003, 2000006) # 291ns -> 250ns (16.4% faster)

def test_gcd_zero_and_negative():
    # Zero and negative
    codeflash_output = gcd_recursive(0, -5) # 208ns -> 208ns (0.000% faster)
    codeflash_output = gcd_recursive(-5, 0) # 83ns -> 42ns (97.6% faster)

# 3. Large Scale Test Cases

def test_gcd_large_numbers():
    # Very large numbers
    a = 12345678901234567890
    b = 98765432109876543210
    # GCD should be 10
    codeflash_output = gcd_recursive(a, b) # 500ns -> 500ns (0.000% faster)

def test_gcd_large_numbers_with_common_factor():
    # Both numbers have a large common factor
    a = 2**20 * 3**15 * 5**10
    b = 2**15 * 3**10 * 5**5
    # GCD should be 2**15 * 3**10 * 5**5
    expected = 2**15 * 3**10 * 5**5
    codeflash_output = gcd_recursive(a, b) # 292ns -> 292ns (0.000% faster)

def test_gcd_large_numbers_coprime():
    # Large coprime numbers
    a = 9999999967  # prime
    b = 9999999943  # prime
    codeflash_output = gcd_recursive(a, b) # 458ns -> 458ns (0.000% faster)

def test_gcd_large_numbers_with_one():
    # Large number and one
    a = 10**18
    b = 1
    codeflash_output = gcd_recursive(a, b) # 250ns -> 208ns (20.2% faster)

def test_gcd_large_numbers_with_zero():
    # Large number and zero
    a = 10**18
    b = 0
    codeflash_output = gcd_recursive(a, b) # 125ns -> 125ns (0.000% faster)

def test_gcd_performance_reasonable_depth():
    # Should not exceed recursion limit for reasonable input sizes
    # 1000 and 999 are coprime, so recursion depth is manageable
    codeflash_output = gcd_recursive(1000, 999) # 292ns -> 250ns (16.8% faster)

# 4. Additional Edge Cases

def test_gcd_minimum_integers():
    # Minimum possible integer values
    codeflash_output = gcd_recursive(-2147483648, 2147483647) # 541ns -> 541ns (0.000% faster)

def test_gcd_maximum_integers():
    # Maximum possible integer values
    codeflash_output = gcd_recursive(2147483647, 2147483646) # 375ns -> 333ns (12.6% faster)

def test_gcd_zero_and_zero():
    # Both zero
    codeflash_output = gcd_recursive(0, 0) # 125ns -> 125ns (0.000% faster)

def test_gcd_large_negative_numbers():
    # Large negative numbers
    a = -12345678901234567890
    b = -98765432109876543210
    codeflash_output = gcd_recursive(a, b) # 500ns -> 458ns (9.17% faster)

# 5. Symmetry and Commutativity

@pytest.mark.parametrize("a,b", [
    (12, 8),
    (100, 10),
    (81, 27),
    (13, 17),
    (0, 5),
    (-12, 8),
    (12345678901234567890, 98765432109876543210),
])
def test_gcd_commutativity(a, b):
    # GCD(a, b) == GCD(b, a)
    codeflash_output = gcd_recursive(a, b) # 2.00μs -> 1.79μs (11.6% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from src.math.computation import gcd_recursive

def test_gcd_recursive():
    gcd_recursive(21, -76)
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic_3vjj4ioc/tmpuucwg31g/test_concolic_coverage.py::test_gcd_recursive 625ns 458ns 36.5%✅

To edit these changes git checkout codeflash/optimize-gcd_recursive-mh13gh00 and push.

Codeflash

The optimization replaces recursion with iteration, converting the recursive GCD implementation into an iterative while loop. This eliminates Python's function call overhead, which is significant due to stack frame creation/destruction and parameter passing on each recursive call.

**Key Changes:**
- Replaced `if b == 0: return a` with `while b != 0:`
- Replaced `return gcd_recursive(b, a % b)` with `a, b = b, a % b`
- Added final `return a` after the loop

**Why This Is Faster:**
1. **Eliminates function call overhead**: Each recursive call creates a new stack frame with parameter binding and return handling. The iterative version reuses the same variables in a single stack frame.
2. **Reduces memory allocation**: No stack buildup from recursive calls, preventing potential stack overflow for deep recursions.
3. **Better CPU cache locality**: All operations happen in the same function scope with fewer memory accesses.

**Performance Characteristics:**
- Shows 15-35% speedup on most test cases, particularly effective for inputs requiring many iterations (like consecutive Fibonacci numbers showing 29% improvement)
- Zero speedup when `b=0` initially (no loop iterations needed)
- Best gains on cases with moderate recursion depth where call overhead dominates computation time
- Maintains identical mathematical behavior while being more efficient for all non-trivial inputs
@codeflash-ai codeflash-ai bot requested a review from Saga4 October 21, 2025 21:46
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 21, 2025
@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-gcd_recursive-mh13gh00 branch October 21, 2025 23:05
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant