Skip to content

⚡️ Speed up function quicksort by 2,139%#304

Closed
codeflash-ai[bot] wants to merge 1 commit intoexperimentalfrom
codeflash/optimize-quicksort-mmwkwxnw
Closed

⚡️ Speed up function quicksort by 2,139%#304
codeflash-ai[bot] wants to merge 1 commit intoexperimentalfrom
codeflash/optimize-quicksort-mmwkwxnw

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai bot commented Mar 18, 2026

📄 2,139% (21.39x) speedup for quicksort in src/algorithms/dynamic_programming.py

⏱️ Runtime : 5.20 milliseconds 232 microseconds (best of 250 runs)

📝 Explanation and details

The optimized version replaces the recursive quicksort implementation with Python's built-in sorted() (Timsort), which eliminates three full array scans to partition elements (left/middle/right list comprehensions accounting for ~50% of original runtime) and recursive list concatenations. Line profiler shows the original spent 27% of time in the recursive return statement alone, while the optimized version completes in a single sorted() call that benefits from cache-friendly memory access and C-level optimizations. The 21× speedup is most pronounced on larger inputs (e.g., 1000-element test improved from 557 µs to 2.79 µs) where repeated partitioning overhead compounds, with no correctness trade-offs across all test cases.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 252 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 1 Passed
📊 Tests Coverage 100.0%
🌀 Click to see Generated Regression Tests
import random
from collections import Counter
from copy import deepcopy

# imports
import pytest  # used for our unit tests

# import the function under test from the provided module path
from src.algorithms.dynamic_programming import quicksort


def test_quicksort_basic_unsorted_list():
    # Simple unsorted list of distinct integers
    arr = [3, 1, 4, 1, 5, 9, 2, 6, 5]
    # Call quicksort and compare to Python's built-in sorted result
    result = quicksort(arr)  # 3.29μs -> 375ns (778% faster)
    assert result == sorted(arr)  # result should be a sorted list of the same values
    # Ensure original list is not modified by the function
    assert arr == [3, 1, 4, 1, 5, 9, 2, 6, 5]


def test_quicksort_already_sorted_and_reverse():
    # Already sorted list should remain sorted
    sorted_arr = [0, 1, 2, 3, 4, 5]
    assert quicksort(sorted_arr) == sorted_arr  # 1.96μs -> 250ns (683% faster)
    # Reverse-sorted list should become sorted
    rev = [5, 4, 3, 2, 1, 0]
    assert quicksort(rev) == sorted(rev)  # 1.54μs -> 167ns (823% faster)


def test_quicksort_empty_and_singleton():
    # Empty list returns empty list
    empty = []
    assert quicksort(empty) == []  # 166ns -> 125ns (32.8% faster)
    # Single element list returns same single-element list
    single = [42]
    assert quicksort(single) == [42]  # 83ns -> 83ns (0.000% faster)


def test_quicksort_all_equal_elements_and_identity_of_result():
    # All elements equal: result should preserve the values and order of equal elements
    arr = [7] * 10
    result = quicksort(arr)  # 1.38μs -> 292ns (371% faster)
    # Values should be the same as original
    assert result == arr
    # The function returns a new list object (not the exact same object reference)
    assert result is not arr


def test_quicksort_negative_and_float_values():
    # Mix of negative integers and floats to ensure function handles numeric types consistently
    arr = [3.5, -2, 0, -2.0, 7, 3, -1.1]
    result = quicksort(arr)  # 3.67μs -> 583ns (529% faster)
    # Python's sorted handles mixed ints/floats; quicksort should produce the same ordering
    assert result == sorted(arr)


def test_quicksort_incomparable_types_raises_type_error():
    # Mixing types that can't be compared (int and string) should raise a TypeError
    arr = [1, "a"]
    with pytest.raises(TypeError):
        quicksort(arr)  # 833ns -> 667ns (24.9% faster)


def test_quicksort_preserves_multiset_counts():
    # Ensure the multiset of elements is preserved (same element counts)
    arr = [5, 3, 3, 7, 5, 3, 9]
    res = quicksort(arr)  # 2.29μs -> 333ns (588% faster)
    assert Counter(res) == Counter(arr)
    # And the result is sorted
    assert res == sorted(arr)


def test_quicksort_large_random_deterministic():
    # Deterministic large random test: seed the RNG so test is reproducible
    random.seed(0)
    arr = [random.randint(-10000, 10000) for _ in range(1000)]  # 1000 elements
    # Take a deep copy to assert the original is not mutated later
    original = deepcopy(arr)
    result = quicksort(arr)  # 801μs -> 36.5μs (2095% faster)
    # Should match Python's sorted()
    assert result == sorted(original)
    # Original must remain unchanged
    assert arr == original
    # Element counts must match (extra safety)
    assert Counter(result) == Counter(original)


def test_quicksort_large_with_many_duplicates():
    # Large list with many duplicates to exercise middle-list handling efficiently
    random.seed(1)
    choices = [0, 1, 2, 3, 4]  # limited set to force many duplicates
    arr = [random.choice(choices) for _ in range(1000)]
    result = quicksort(arr)  # 115μs -> 29.0μs (299% faster)
    assert result == sorted(arr)
    # Verify counts of each value match
    assert Counter(result) == Counter(arr)


def test_quicksort_many_small_iterations_stability_of_behavior():
    # Run many small cases to ensure deterministic behavior over repeated calls
    random.seed(2)
    for n in range(1, 200):  # 199 small random lists (keeps runtime reasonable)
        arr = [random.randint(-50, 50) for _ in range(random.randint(0, 30))]
        # Use deepcopy to later ensure arr is unchanged
        original = deepcopy(arr)
        res = quicksort(arr)  # 1.23ms -> 67.2μs (1729% faster)
        assert res == sorted(original)
        assert arr == original


def test_quicksort_return_type_and_non_mutation_for_various_inputs():
    # Check return is a list and input is not mutated across various scenarios
    cases = [
        [],
        [10],
        [2, 1],
        [1, 2, 3, 4, 5],
        [5, 4, 3, 2, 1],
        [0, 0, 0, 0],
    ]
    for arr in cases:
        arr_copy = deepcopy(arr)
        res = quicksort(arr)  # 4.41μs -> 791ns (458% faster)
        assert isinstance(res, list)  # return type must be list
        assert res == sorted(arr_copy)  # sorted result
        assert arr == arr_copy  # input unchanged
import pytest
from src.algorithms.dynamic_programming import quicksort


def test_empty_list():
    """Verify that an empty list returns an empty list."""
    result = quicksort([])  # 166ns -> 166ns (0.000% faster)
    assert result == []


def test_single_element():
    """Verify that a single-element list returns itself."""
    result = quicksort([42])  # 125ns -> 125ns (0.000% faster)
    assert result == [42]


def test_two_elements_sorted():
    """Verify sorting of two elements already in order."""
    result = quicksort([1, 2])  # 833ns -> 291ns (186% faster)
    assert result == [1, 2]


def test_two_elements_unsorted():
    """Verify sorting of two elements in reverse order."""
    result = quicksort([2, 1])  # 791ns -> 291ns (172% faster)
    assert result == [1, 2]


def test_small_list_unsorted():
    """Verify sorting of a small unsorted list."""
    result = quicksort([3, 1, 2])  # 1.29μs -> 292ns (342% faster)
    assert result == [1, 2, 3]


def test_small_list_sorted():
    """Verify that an already sorted list remains sorted."""
    result = quicksort([1, 2, 3, 4, 5])  # 1.83μs -> 292ns (528% faster)
    assert result == [1, 2, 3, 4, 5]


def test_small_list_reverse_sorted():
    """Verify sorting of a reverse-sorted list."""
    result = quicksort([5, 4, 3, 2, 1])  # 1.79μs -> 292ns (514% faster)
    assert result == [1, 2, 3, 4, 5]


def test_list_with_duplicate_elements():
    """Verify that duplicate elements are preserved in sorted order."""
    result = quicksort([3, 1, 3, 2, 1])  # 1.92μs -> 333ns (476% faster)
    assert result == [1, 1, 2, 3, 3]


def test_list_with_all_duplicates():
    """Verify that a list of all identical elements sorts correctly."""
    result = quicksort([5, 5, 5, 5])  # 958ns -> 291ns (229% faster)
    assert result == [5, 5, 5, 5]


def test_list_with_negative_numbers():
    """Verify sorting with negative integers."""
    result = quicksort([3, -1, -5, 2, 0])  # 2.00μs -> 292ns (585% faster)
    assert result == [-5, -1, 0, 2, 3]


def test_list_with_mixed_sign_numbers():
    """Verify sorting with both positive and negative numbers."""
    result = quicksort([-10, 5, -3, 8, 0, -1])  # 2.42μs -> 333ns (626% faster)
    assert result == [-10, -3, -1, 0, 5, 8]


def test_list_with_zero():
    """Verify that zero is sorted correctly among other numbers."""
    result = quicksort([3, 0, -2, 1])  # 1.79μs -> 292ns (514% faster)
    assert result == [-2, 0, 1, 3]


def test_medium_list():
    """Verify sorting of a medium-sized list."""
    input_list = [9, 3, 7, 1, 5, 4, 8, 2, 6]
    result = quicksort(input_list)  # 3.54μs -> 375ns (845% faster)
    assert result == [1, 2, 3, 4, 5, 6, 7, 8, 9]


def test_list_with_large_numbers():
    """Verify sorting with very large integers."""
    result = quicksort([1000000, 999, 1000001, 1])  # 1.71μs -> 292ns (485% faster)
    assert result == [1, 999, 1000000, 1000001]


def test_list_with_negative_large_numbers():
    """Verify sorting with very large negative integers."""
    result = quicksort([-1000000, -1, -1000001, 0])  # 1.67μs -> 333ns (401% faster)
    assert result == [-1000001, -1000000, -1, 0]


def test_two_elements_both_zero():
    """Verify sorting when both elements are zero."""
    result = quicksort([0, 0])  # 833ns -> 250ns (233% faster)
    assert result == [0, 0]


def test_three_elements_all_same():
    """Verify sorting when all three elements are identical."""
    result = quicksort([7, 7, 7])  # 916ns -> 291ns (215% faster)
    assert result == [7, 7, 7]


def test_alternating_values():
    """Verify sorting with alternating high and low values."""
    result = quicksort([10, 1, 10, 1, 10, 1])  # 1.54μs -> 333ns (363% faster)
    assert result == [1, 1, 1, 10, 10, 10]


def test_almost_sorted_list():
    """Verify sorting of a nearly-sorted list with one element out of place."""
    result = quicksort([1, 2, 3, 5, 4])  # 1.83μs -> 291ns (530% faster)
    assert result == [1, 2, 3, 4, 5]


def test_pivot_is_smallest():
    """Verify sorting when the pivot (middle element) is the smallest."""
    result = quicksort([5, 1, 4, 3, 2])  # 1.88μs -> 292ns (542% faster)
    assert result == [1, 2, 3, 4, 5]


def test_pivot_is_largest():
    """Verify sorting when the pivot (middle element) is the largest."""
    result = quicksort([1, 5, 2, 3, 4])  # 1.88μs -> 333ns (463% faster)
    assert result == [1, 2, 3, 4, 5]


def test_single_large_value():
    """Verify sorting when one element dominates."""
    result = quicksort([1, 1, 1, 1, 100, 1])  # 1.17μs -> 292ns (299% faster)
    assert result == [1, 1, 1, 1, 1, 100]


def test_list_preserves_stability():
    """Verify that equal elements maintain relative order."""
    # Quicksort may not guarantee stability, but we verify all duplicates are present
    result = quicksort([3, 3, 3, 1, 1, 2])  # 1.67μs -> 333ns (400% faster)
    # Count occurrences to verify all elements are present
    assert result.count(1) == 2
    assert result.count(2) == 1
    assert result.count(3) == 3
    # Verify sorted order
    assert result == [1, 1, 2, 3, 3, 3]


def test_large_list_100_elements():
    """Verify sorting a large list of 100 elements."""
    input_list = list(range(100, 0, -1))  # [100, 99, ..., 1]
    result = quicksort(input_list)  # 45.9μs -> 625ns (7240% faster)
    expected = list(range(1, 101))  # [1, 2, ..., 100]
    assert result == expected


def test_large_list_500_elements():
    """Verify sorting a list of 500 elements in random order."""
    # Create a list of 500 elements in descending order
    input_list = list(range(500, 0, -1))
    result = quicksort(input_list)  # 258μs -> 1.58μs (16188% faster)
    expected = list(range(1, 501))
    assert result == expected


def test_large_list_1000_elements():
    """Verify sorting a list of 1000 elements."""
    # Create a list of 1000 elements in descending order
    input_list = list(range(1000, 0, -1))
    result = quicksort(input_list)  # 557μs -> 2.79μs (19865% faster)
    expected = list(range(1, 1001))
    assert result == expected
    # Verify length is preserved
    assert len(result) == 1000


def test_large_list_with_many_duplicates():
    """Verify sorting a large list with many repeated values."""
    # Create a list with repeated values
    input_list = [5, 3, 5, 1, 3, 5, 2, 1, 3] * 100  # 900 elements
    result = quicksort(input_list)  # 95.9μs -> 15.9μs (502% faster)
    # Verify counts
    assert result.count(1) == 200
    assert result.count(2) == 100
    assert result.count(3) == 300
    assert result.count(5) == 300
    # Verify sorted order
    assert result == sorted(input_list)


def test_large_list_all_negatives():
    """Verify sorting a large list of all negative numbers."""
    input_list = list(range(-500, 0))  # [-500, -499, ..., -1]
    # Shuffle to randomize
    import random

    shuffled = input_list.copy()
    random.shuffle(shuffled)
    result = quicksort(shuffled)  # 363μs -> 13.8μs (2537% faster)
    assert result == sorted(input_list)


def test_large_list_mixed_positive_negative():
    """Verify sorting a large list with mixed positive and negative numbers."""
    input_list = list(range(-250, 250))  # [-250, ..., -1, 0, 1, ..., 249]
    import random

    shuffled = input_list.copy()
    random.shuffle(shuffled)
    result = quicksort(shuffled)  # 375μs -> 14.1μs (2565% faster)
    assert result == sorted(input_list)
    assert len(result) == 500


def test_large_list_length_preserved():
    """Verify that the output length matches input length for large lists."""
    input_list = [7, 2, 9, 1, 5, 3, 8, 4, 6] * 100  # 900 elements
    result = quicksort(input_list)  # 137μs -> 20.9μs (560% faster)
    assert len(result) == len(input_list)


def test_large_list_matches_sorted_builtin():
    """Verify large list results match Python's built-in sorted function."""
    import random

    input_list = [random.randint(-1000, 1000) for _ in range(500)]
    result = quicksort(input_list)  # 343μs -> 13.9μs (2367% faster)
    expected = sorted(input_list)
    assert result == expected


def test_sequential_list_large():
    """Verify sorting a large sequential list maintains correctness."""
    input_list = list(range(1, 501))  # [1, 2, ..., 500] already sorted
    result = quicksort(input_list)  # 256μs -> 1.38μs (18582% faster)
    assert result == input_list


def test_reverse_sequential_list_large():
    """Verify sorting a large reverse-sequential list."""
    input_list = list(range(1000, 0, -1))  # [1000, 999, ..., 1]
    result = quicksort(input_list)  # 553μs -> 2.75μs (20039% faster)
    assert result == list(range(1, 1001))


def test_output_is_sorted():
    """Verify that the output is always in ascending order."""
    result = quicksort([9, 3, 7, 1, 5])  # 1.96μs -> 292ns (571% faster)
    # Check that each element is <= the next element
    for i in range(len(result) - 1):
        assert result[i] <= result[i + 1]


def test_output_contains_all_input_elements():
    """Verify that all input elements appear in the output."""
    input_list = [3, 1, 4, 1, 5, 9, 2, 6]
    result = quicksort(input_list)  # 3.25μs -> 375ns (767% faster)
    # Check that sorted result contains the same elements
    assert sorted(input_list) == result


def test_no_elements_lost_or_added():
    """Verify that the count of each element is preserved."""
    input_list = [5, 2, 8, 2, 5, 1, 2]
    result = quicksort(input_list)  # 2.04μs -> 333ns (513% faster)
    # Count each unique value
    for value in set(input_list):
        assert input_list.count(value) == result.count(value)
from src.algorithms.dynamic_programming import quicksort


def test_quicksort():
    quicksort([30055, 30056, 30057])
🔎 Click to see Concolic Coverage Tests

To edit these changes git checkout codeflash/optimize-quicksort-mmwkwxnw and push.

Codeflash Static Badge

The optimized version replaces the recursive quicksort implementation with Python's built-in `sorted()` (Timsort), which eliminates three full array scans to partition elements (left/middle/right list comprehensions accounting for ~50% of original runtime) and recursive list concatenations. Line profiler shows the original spent 27% of time in the recursive return statement alone, while the optimized version completes in a single `sorted()` call that benefits from cache-friendly memory access and C-level optimizations. The 21× speedup is most pronounced on larger inputs (e.g., 1000-element test improved from 557 µs to 2.79 µs) where repeated partitioning overhead compounds, with no correctness trade-offs across all test cases.
@codeflash-ai codeflash-ai bot requested a review from aseembits93 March 18, 2026 21:54
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Mar 18, 2026
@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-quicksort-mmwkwxnw branch March 18, 2026 22:49
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