Skip to content

Conversation

@codeflash-ai
Copy link

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

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

⏱️ Runtime : 402 microseconds 349 microseconds (best of 135 runs)

📝 Explanation and details

The optimized code replaces recursive function calls with an iterative while loop, eliminating Python's function call overhead. This is the key performance improvement.

What changed:

  • Converted from recursive calls (gcd_recursive(b, a % b)) to iterative loop (while b: a, b = b, a % b)
  • Eliminated the base case check (if b == 0) since the while loop naturally terminates when b becomes 0

Why it's faster:

  • Eliminates function call overhead: Each recursive call in Python involves stack frame creation, parameter passing, and return value handling. The line profiler shows the original recursive call (line with return gcd_recursive(b, a % b)) took 15.7% of total time.
  • Reduces memory pressure: Iterative approach uses O(1) stack space vs O(log n) for recursion
  • Better CPU cache utilization: Loop stays in the same memory location rather than jumping between stack frames

Performance gains by test case type:

  • Best improvements (25-55% faster): Cases requiring multiple iterations like gcd_recursive(12, 18) and gcd_recursive(13, 17) where recursion overhead compounds
  • Modest improvements (15-25% faster): Most standard cases benefit from avoiding the initial function call setup
  • Minimal impact: Simple cases like gcd_recursive(7, 0) that terminate quickly show less improvement since there's less recursion to eliminate

The algorithm complexity remains O(log min(a,b)) but with significantly lower constant factors due to removing Python's recursive call mechanics.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 2118 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 src.math.computation import gcd_recursive

# unit tests

# --- Basic Test Cases ---

def test_gcd_basic_coprime():
    # Coprime numbers: GCD should be 1
    codeflash_output = gcd_recursive(13, 17) # 625ns -> 500ns (25.0% faster)
    codeflash_output = gcd_recursive(29, 12) # 334ns -> 292ns (14.4% faster)

def test_gcd_basic_non_coprime():
    # Numbers with common divisors
    codeflash_output = gcd_recursive(12, 18) # 583ns -> 375ns (55.5% faster)
    codeflash_output = gcd_recursive(100, 80) # 250ns -> 250ns (0.000% faster)
    codeflash_output = gcd_recursive(81, 27) # 167ns -> 125ns (33.6% faster)

def test_gcd_basic_one_is_zero():
    # One argument is zero: should return abs of the other
    codeflash_output = gcd_recursive(0, 5) # 500ns -> 417ns (19.9% faster)
    codeflash_output = gcd_recursive(7, 0) # 125ns -> 167ns (25.1% slower)

def test_gcd_basic_both_are_zero():
    # Both arguments zero: mathematically undefined, but function should return 0
    codeflash_output = gcd_recursive(0, 0) # 250ns -> 209ns (19.6% faster)

def test_gcd_basic_one_is_one():
    # GCD with 1: should always be 1
    codeflash_output = gcd_recursive(1, 999) # 542ns -> 500ns (8.40% faster)
    codeflash_output = gcd_recursive(999, 1) # 208ns -> 167ns (24.6% faster)

def test_gcd_basic_equal_numbers():
    # GCD of two equal numbers is the number itself
    codeflash_output = gcd_recursive(42, 42) # 458ns -> 375ns (22.1% faster)
    codeflash_output = gcd_recursive(1000, 1000) # 208ns -> 250ns (16.8% slower)

# --- Edge Test Cases ---

def test_gcd_negative_numbers():
    # GCD should be positive even if inputs are negative
    codeflash_output = gcd_recursive(-12, 18) # 583ns -> 417ns (39.8% faster)
    codeflash_output = gcd_recursive(12, -18) # 333ns -> 333ns (0.000% faster)
    codeflash_output = gcd_recursive(-12, -18) # 416ns -> 333ns (24.9% faster)

def test_gcd_negative_and_zero():
    # Negative and zero: should return abs of the nonzero number
    codeflash_output = gcd_recursive(-7, 0) # 291ns -> 291ns (0.000% faster)
    codeflash_output = gcd_recursive(0, -7) # 292ns -> 292ns (0.000% faster)

def test_gcd_large_prime_and_one():
    # Large prime and 1: should always be 1
    codeflash_output = gcd_recursive(982451653, 1) # 417ns -> 416ns (0.240% faster)
    codeflash_output = gcd_recursive(1, 982451653) # 250ns -> 292ns (14.4% slower)

def test_gcd_negative_and_one():
    # Negative and 1: should always be 1
    codeflash_output = gcd_recursive(-1, 1) # 458ns -> 375ns (22.1% faster)
    codeflash_output = gcd_recursive(1, -1) # 167ns -> 167ns (0.000% faster)

def test_gcd_large_and_zero():
    # Large number and zero: should return abs of the large number
    codeflash_output = gcd_recursive(12345678901234567890, 0) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(0, 12345678901234567890) # 375ns -> 250ns (50.0% faster)

def test_gcd_zero_and_zero():
    # Both zero: should return 0 (by convention)
    codeflash_output = gcd_recursive(0, 0) # 250ns -> 291ns (14.1% slower)

def test_gcd_one_negative_and_one_positive():
    # One negative, one positive, coprime
    codeflash_output = gcd_recursive(-13, 17) # 583ns -> 542ns (7.56% faster)
    codeflash_output = gcd_recursive(13, -17) # 333ns -> 292ns (14.0% faster)

def test_gcd_negative_equal_numbers():
    # Both negative and equal
    codeflash_output = gcd_recursive(-42, -42) # 417ns -> 417ns (0.000% faster)

# --- Large Scale Test Cases ---

def test_gcd_large_numbers():
    # Large numbers with a known GCD
    a = 12345678901234567890
    b = 98765432109876543210
    # GCD is 900000000090
    codeflash_output = gcd_recursive(a, b) # 1.17μs -> 1.00μs (16.6% faster)

def test_gcd_large_coprime():
    # Large coprime numbers
    a = 982451653  # prime
    b = 57885161   # prime
    codeflash_output = gcd_recursive(a, b) # 1.42μs -> 1.12μs (25.9% faster)

def test_gcd_large_equal_numbers():
    # Large equal numbers
    a = 10**18
    codeflash_output = gcd_recursive(a, a) # 625ns -> 666ns (6.16% slower)

def test_gcd_large_one_is_much_bigger():
    # One number much larger than the other
    a = 10**18
    b = 10**9
    codeflash_output = gcd_recursive(a, b) # 459ns -> 375ns (22.4% faster)

def test_gcd_large_prime_and_composite():
    # Large prime and large composite with known GCD
    prime = 9999999967
    composite = prime * 12345
    codeflash_output = gcd_recursive(prime, composite) # 791ns -> 666ns (18.8% faster)

def test_gcd_large_many_steps():
    # Numbers that require many recursive calls
    # Use two consecutive Fibonacci numbers (GCD should be 1)
    fib_30 = 832040
    fib_31 = 1346269
    codeflash_output = gcd_recursive(fib_30, fib_31) # 2.00μs -> 1.58μs (26.3% faster)

def test_gcd_large_near_max_int():
    # Near Python's max int (arbitrary precision, but still test very large numbers)
    a = 2**100
    b = 2**80
    codeflash_output = gcd_recursive(a, b) # 625ns -> 584ns (7.02% faster)

# --- Property-based checks (sanity for mutation testing) ---

@pytest.mark.parametrize("a,b", [
    (0, 0), (0, 1), (1, 0), (1, 1), (12, 18), (18, 12), (100, 80), (80, 100),
    (982451653, 57885161), (-12, 18), (12, -18), (-12, -18),
    (12345678901234567890, 98765432109876543210),
])
def test_gcd_commutativity(a, b):
    # GCD(a, b) == GCD(b, a)
    codeflash_output = gcd_recursive(a, b) # 7.46μs -> 6.50μs (14.7% faster)

@pytest.mark.parametrize("a,b,c", [
    (12, 18, 6), (81, 27, 27), (100, 80, 20), (13, 17, 1),
])
def test_gcd_divides_inputs(a, b, c):
    # GCD should divide both inputs
    codeflash_output = gcd_recursive(a, b); gcd = codeflash_output # 2.12μs -> 1.79μs (18.6% faster)

def test_gcd_mutation_resistance():
    # GCD should be idempotent: GCD(a, GCD(a, b)) == GCD(a, b)
    a, b = 123456, 789012
    codeflash_output = gcd_recursive(a, b); g = codeflash_output # 1.12μs -> 958ns (17.4% faster)
    codeflash_output = gcd_recursive(a, g) # 208ns -> 208ns (0.000% faster)
    codeflash_output = gcd_recursive(g, a) # 208ns -> 208ns (0.000% faster)
    codeflash_output = gcd_recursive(g, b) # 208ns -> 167ns (24.6% 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

# Basic Test Cases
def test_gcd_basic_coprime():
    # Coprime numbers: GCD should be 1
    codeflash_output = gcd_recursive(13, 17) # 625ns -> 500ns (25.0% faster)
    codeflash_output = gcd_recursive(101, 10) # 250ns -> 208ns (20.2% faster)

def test_gcd_basic_non_coprime():
    # Non-coprime numbers: GCD should be their common divisor
    codeflash_output = gcd_recursive(12, 18) # 583ns -> 417ns (39.8% faster)
    codeflash_output = gcd_recursive(100, 80) # 209ns -> 208ns (0.481% faster)
    codeflash_output = gcd_recursive(27, 36) # 250ns -> 250ns (0.000% faster)

def test_gcd_basic_one_zero():
    # One argument is zero, GCD should be the absolute value of the other
    codeflash_output = gcd_recursive(0, 5) # 417ns -> 458ns (8.95% slower)
    codeflash_output = gcd_recursive(7, 0) # 167ns -> 125ns (33.6% faster)
    codeflash_output = gcd_recursive(0, 0) # 125ns -> 125ns (0.000% faster)

def test_gcd_basic_equal_numbers():
    # Both numbers equal: GCD is the number itself
    codeflash_output = gcd_recursive(7, 7) # 458ns -> 375ns (22.1% faster)
    codeflash_output = gcd_recursive(1000, 1000) # 208ns -> 250ns (16.8% slower)

def test_gcd_basic_one_is_one():
    # One argument is 1: GCD should be 1
    codeflash_output = gcd_recursive(1, 999) # 541ns -> 459ns (17.9% faster)
    codeflash_output = gcd_recursive(999, 1) # 208ns -> 208ns (0.000% faster)

# Edge Test Cases
def test_gcd_negative_numbers():
    # Negative numbers: GCD should always be positive
    codeflash_output = gcd_recursive(-12, 18) # 500ns -> 416ns (20.2% faster)
    codeflash_output = gcd_recursive(12, -18) # 333ns -> 292ns (14.0% faster)
    codeflash_output = gcd_recursive(-12, -18) # 334ns -> 334ns (0.000% faster)
    codeflash_output = gcd_recursive(-7, 0) # 125ns -> 125ns (0.000% faster)
    codeflash_output = gcd_recursive(0, -7) # 167ns -> 167ns (0.000% faster)

def test_gcd_large_prime_and_small():
    # Large prime and small number: GCD should be 1
    codeflash_output = gcd_recursive(999983, 2) # 500ns -> 458ns (9.17% faster)
    codeflash_output = gcd_recursive(2, 999983) # 333ns -> 291ns (14.4% faster)

def test_gcd_one_argument_much_larger():
    # One argument much larger than the other
    codeflash_output = gcd_recursive(1000000, 3) # 541ns -> 417ns (29.7% faster)
    codeflash_output = gcd_recursive(3, 1000000) # 333ns -> 250ns (33.2% faster)
    codeflash_output = gcd_recursive(123456789, 98765432) # 417ns -> 375ns (11.2% faster)

def test_gcd_zero_and_zero():
    # Both arguments zero: GCD is mathematically undefined, but function returns 0
    codeflash_output = gcd_recursive(0, 0) # 250ns -> 250ns (0.000% faster)

def test_gcd_edge_max_int():
    # Edge case: very large integers
    max_int = 2**63 - 1
    codeflash_output = gcd_recursive(max_int, max_int) # 708ns -> 709ns (0.141% slower)
    codeflash_output = gcd_recursive(max_int, 1) # 333ns -> 250ns (33.2% faster)
    codeflash_output = gcd_recursive(0, max_int) # 208ns -> 208ns (0.000% faster)

def test_gcd_edge_min_int():
    # Edge case: very small (negative) integers
    min_int = -2**63
    codeflash_output = gcd_recursive(min_int, min_int) # 625ns -> 583ns (7.20% faster)
    codeflash_output = gcd_recursive(min_int, 1) # 292ns -> 250ns (16.8% faster)
    codeflash_output = gcd_recursive(0, min_int) # 208ns -> 208ns (0.000% faster)

def test_gcd_edge_one_is_zero():
    # Edge: one argument is zero, other is negative
    codeflash_output = gcd_recursive(0, -100) # 500ns -> 416ns (20.2% faster)
    codeflash_output = gcd_recursive(-100, 0) # 167ns -> 167ns (0.000% faster)

# Large Scale Test Cases
def test_gcd_large_numbers():
    # Large numbers with known GCD
    codeflash_output = gcd_recursive(12345678901234567890, 98765432109876543210) # 792ns -> 708ns (11.9% faster)
    codeflash_output = gcd_recursive(10**18, 10**9) # 292ns -> 167ns (74.9% faster)
    codeflash_output = gcd_recursive(10**18 + 1, 10**9 + 1) # 417ns -> 375ns (11.2% faster)

def test_gcd_large_numbers_coprime():
    # Large coprime numbers
    codeflash_output = gcd_recursive(9999999967, 9999999961) # 875ns -> 792ns (10.5% faster)

def test_gcd_large_numbers_non_coprime():
    # Large numbers with large GCD
    codeflash_output = gcd_recursive(9999999967 * 12345, 9999999967 * 67890) # 1.04μs -> 833ns (25.0% faster)

def test_gcd_large_scale_loop():
    # Test many pairs of numbers to check scalability
    for i in range(1, 1001):  # 1000 pairs
        codeflash_output = gcd_recursive(i, i+1) # 212μs -> 175μs (21.2% faster)
        codeflash_output = gcd_recursive(i*2, i)  # Multiples

def test_gcd_large_scale_random_pairs():
    # Test random pairs for robustness (deterministic)
    pairs = [
        (123456, 789012),
        (987654, 321098),
        (555555, 111111),
        (1000000000, 999999999),
        (999999999, 1000000000)
    ]
    expected = [
        12,
        2,
        111111,
        1,
        1
    ]
    for (a, b), exp in zip(pairs, expected):
        codeflash_output = gcd_recursive(a, b) # 2.67μs -> 2.42μs (10.3% faster)

# Edge case: very small numbers
def test_gcd_small_numbers():
    codeflash_output = gcd_recursive(2, 2) # 500ns -> 417ns (19.9% faster)
    codeflash_output = gcd_recursive(2, 1) # 167ns -> 125ns (33.6% faster)
    codeflash_output = gcd_recursive(1, 2) # 250ns -> 208ns (20.2% faster)
    codeflash_output = gcd_recursive(0, 1) # 208ns -> 167ns (24.6% faster)
    codeflash_output = gcd_recursive(1, 0) # 125ns -> 167ns (25.1% slower)

# Edge case: negative and zero
def test_gcd_negative_and_zero():
    codeflash_output = gcd_recursive(-1, 0) # 291ns -> 208ns (39.9% faster)
    codeflash_output = gcd_recursive(0, -1) # 375ns -> 292ns (28.4% faster)
    codeflash_output = gcd_recursive(-1, 1) # 208ns -> 167ns (24.6% faster)
    codeflash_output = gcd_recursive(1, -1) # 167ns -> 125ns (33.6% faster)

# Edge case: both negative
def test_gcd_both_negative():
    codeflash_output = gcd_recursive(-10, -5) # 416ns -> 417ns (0.240% slower)
    codeflash_output = gcd_recursive(-5, -10) # 292ns -> 250ns (16.8% faster)

# Edge case: one negative, one positive
def test_gcd_one_negative_one_positive():
    codeflash_output = gcd_recursive(-10, 5) # 458ns -> 375ns (22.1% faster)
    codeflash_output = gcd_recursive(10, -5) # 208ns -> 208ns (0.000% faster)

# Edge case: zero and zero
def test_gcd_zero_zero():
    codeflash_output = gcd_recursive(0, 0) # 250ns -> 250ns (0.000% faster)

# Edge case: large difference in magnitude
def test_gcd_large_difference():
    codeflash_output = gcd_recursive(1, 10**12) # 709ns -> 584ns (21.4% faster)
    codeflash_output = gcd_recursive(10**12, 1) # 250ns -> 167ns (49.7% faster)

# Edge case: both arguments are powers of two
def test_gcd_powers_of_two():
    codeflash_output = gcd_recursive(2**10, 2**5) # 458ns -> 416ns (10.1% faster)
    codeflash_output = gcd_recursive(2**20, 2**15) # 250ns -> 208ns (20.2% faster)

# Edge case: both arguments are multiples of a common prime
def test_gcd_common_prime_multiple():
    codeflash_output = gcd_recursive(17*23, 17*29) # 834ns -> 667ns (25.0% faster)
    codeflash_output = gcd_recursive(23*29, 23*31) # 417ns -> 292ns (42.8% faster)

# Edge case: one argument is negative, the other is zero
def test_gcd_negative_and_zero_again():
    codeflash_output = gcd_recursive(-100, 0) # 250ns -> 250ns (0.000% faster)
    codeflash_output = gcd_recursive(0, -100) # 375ns -> 250ns (50.0% 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)

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

Codeflash

The optimized code replaces recursive function calls with an iterative `while` loop, eliminating Python's function call overhead. This is the key performance improvement.

**What changed:**
- Converted from recursive calls (`gcd_recursive(b, a % b)`) to iterative loop (`while b: a, b = b, a % b`)
- Eliminated the base case check (`if b == 0`) since the while loop naturally terminates when `b` becomes 0

**Why it's faster:**
- **Eliminates function call overhead**: Each recursive call in Python involves stack frame creation, parameter passing, and return value handling. The line profiler shows the original recursive call (line with `return gcd_recursive(b, a % b)`) took 15.7% of total time.
- **Reduces memory pressure**: Iterative approach uses O(1) stack space vs O(log n) for recursion
- **Better CPU cache utilization**: Loop stays in the same memory location rather than jumping between stack frames

**Performance gains by test case type:**
- **Best improvements** (25-55% faster): Cases requiring multiple iterations like `gcd_recursive(12, 18)` and `gcd_recursive(13, 17)` where recursion overhead compounds
- **Modest improvements** (15-25% faster): Most standard cases benefit from avoiding the initial function call setup
- **Minimal impact**: Simple cases like `gcd_recursive(7, 0)` that terminate quickly show less improvement since there's less recursion to eliminate

The algorithm complexity remains O(log min(a,b)) but with significantly lower constant factors due to removing Python's recursive call mechanics.
@codeflash-ai codeflash-ai bot requested a review from KRRT7 October 29, 2025 06:39
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 29, 2025
@KRRT7 KRRT7 closed this Nov 8, 2025
@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-gcd_recursive-mhbmk9nx branch November 8, 2025 10:10
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.

2 participants