Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 13, 2025

📄 27% (0.27x) speedup for zaif.parse_balance in python/ccxt/async_support/zaif.py

⏱️ Runtime : 42.0 milliseconds 33.0 milliseconds (best of 66 runs)

📝 Explanation and details

The optimized code achieves a 27% speedup through several key optimizations targeting the hot paths identified in the profiling data:

Primary Optimizations:

  1. Inline dictionary access in safe_string and safe_value: Instead of calling Exchange.key_exists() (which adds function call overhead), the optimized version directly uses isinstance(dictionary, dict) checks and dictionary.get() or key in dictionary operations. This eliminates ~52ms of function call overhead seen in the original profiling.

  2. Direct dictionary iteration in safe_balance: Replaced the inefficient codes = list(balances.keys()) followed by for i in range(0, len(codes)): code = codes[i] pattern with direct for code, entry in balances.items(). This eliminates list allocation and indexing overhead, reducing iteration cost from O(n) list creation + O(n) indexing to O(n) direct iteration.

  3. Optimized dictionary comprehension for omitting keys: Replaced the self.omit() function call with an inline dictionary comprehension {k: v for k, v in balance.items() if k not in to_omit} using a set for O(1) membership testing.

  4. Simplified loop in parse_balance: Changed from for i in range(0, len(currencyIds)): currencyId = currencyIds[i] to direct for currencyId, balance in funds.items(), eliminating list creation and index-based access.

  5. Conditional optimization: Combined the deposit check if deposit is not None and currencyId in deposit to short-circuit evaluation, avoiding unnecessary membership tests when deposit is None.

Performance Impact:

  • The optimizations are particularly effective for large-scale test cases (28-30% speedup with 500-1000 currencies)
  • Edge cases with empty/minimal data show even larger improvements (40-58% speedup) due to reduced setup overhead
  • Basic cases with few currencies still benefit significantly (11-16% speedup)

Why These Work:
These optimizations target Python's performance characteristics: reducing function call overhead, minimizing dictionary lookups, eliminating unnecessary list allocations, and using more efficient iteration patterns. The improvements compound when processing many currencies, making this especially valuable for exchanges handling large numbers of trading pairs.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 135 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import pytest
from ccxt.async_support.zaif import zaif

# ----------------- UNIT TESTS -----------------

@pytest.fixture
def zaif_instance():
    return zaif()

# 1. Basic Test Cases

def test_single_currency_basic(zaif_instance):
    # Basic: Single currency, funds and deposit match
    response = {
        "return": {
            "funds": {"btc": "1.5"},
            "deposit": {"btc": "1.5"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 32.0μs -> 28.2μs (13.3% faster)

def test_multiple_currencies_basic(zaif_instance):
    # Basic: Multiple currencies, all present in deposit
    response = {
        "return": {
            "funds": {"btc": "1.2", "eth": "3.4"},
            "deposit": {"btc": "1.2", "eth": "3.4"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 40.5μs -> 36.5μs (11.1% faster)

def test_deposit_greater_than_funds(zaif_instance):
    # Basic: deposit > funds (implies used funds)
    response = {
        "return": {
            "funds": {"btc": "1.0"},
            "deposit": {"btc": "1.5"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.6μs -> 27.1μs (12.7% faster)

def test_deposit_less_than_funds(zaif_instance):
    # Basic: deposit < funds (should not happen, but test anyway)
    response = {
        "return": {
            "funds": {"btc": "2.0"},
            "deposit": {"btc": "1.5"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 31.7μs -> 28.3μs (11.9% faster)

def test_no_deposit_field(zaif_instance):
    # Basic: No deposit field, total=free from funds
    response = {
        "return": {
            "funds": {"btc": "0.5"}
            # no deposit
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 29.8μs -> 26.8μs (11.1% faster)

def test_zero_balances(zaif_instance):
    # Basic: Zero balances
    response = {
        "return": {
            "funds": {"btc": "0.0"},
            "deposit": {"btc": "0.0"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.7μs -> 27.1μs (13.2% faster)

def test_currency_code_mapping(zaif_instance):
    # Basic: Test currency code mapping (e.g. xbt->BTC, bcc->BCH)
    response = {
        "return": {
            "funds": {"xbt": "1.0", "bcc": "2.0", "bchsv": "3.0"},
            "deposit": {"xbt": "1.0", "bcc": "2.0", "bchsv": "3.0"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 48.5μs -> 42.9μs (13.0% faster)

# 2. Edge Test Cases

def test_empty_response(zaif_instance):
    # Edge: Completely empty response
    response = {}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.40μs -> 4.77μs (55.1% faster)

def test_missing_return_key(zaif_instance):
    # Edge: Response with no 'return' key
    response = {"foo": "bar"}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.48μs -> 4.89μs (53.0% faster)

def test_funds_empty(zaif_instance):
    # Edge: 'funds' is empty dict
    response = {"return": {"funds": {}}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.75μs -> 5.16μs (50.2% faster)

def test_funds_none(zaif_instance):
    # Edge: 'funds' is None
    response = {"return": {"funds": None}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.78μs -> 5.12μs (51.8% faster)

def test_deposit_none(zaif_instance):
    # Edge: 'deposit' is None, funds present
    response = {"return": {"funds": {"btc": "1.0"}, "deposit": None}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 31.2μs -> 27.7μs (12.8% faster)

def test_funds_with_non_string_values(zaif_instance):
    # Edge: 'funds' has int/float values
    response = {"return": {"funds": {"btc": 2, "eth": 3.5}}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 43.3μs -> 38.1μs (13.6% faster)

def test_deposit_partial(zaif_instance):
    # Edge: deposit only for one currency
    response = {
        "return": {
            "funds": {"btc": "1.0", "eth": "2.0"},
            "deposit": {"btc": "1.5"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 40.9μs -> 36.4μs (12.4% faster)

def test_funds_and_deposit_different_currencies(zaif_instance):
    # Edge: deposit has a currency not in funds
    response = {
        "return": {
            "funds": {"btc": "1.0"},
            "deposit": {"btc": "1.0", "eth": "2.0"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.6μs -> 26.9μs (13.8% faster)

def test_funds_with_none_value(zaif_instance):
    # Edge: funds contains a currency with None value
    response = {
        "return": {
            "funds": {"btc": None, "eth": "2.0"},
            "deposit": {"btc": "1.0", "eth": "2.0"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 37.1μs -> 32.4μs (14.6% faster)

def test_funds_with_zero_and_negative(zaif_instance):
    # Edge: negative and zero values
    response = {
        "return": {
            "funds": {"btc": "0", "eth": "-1.5"},
            "deposit": {"btc": "0", "eth": "-1.5"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 42.1μs -> 37.1μs (13.5% faster)

def test_currency_id_case_insensitivity(zaif_instance):
    # Edge: currency ids in mixed case
    response = {
        "return": {
            "funds": {"BtC": "1.0", "eTh": "2.0"},
            "deposit": {"BtC": "1.0", "eTh": "2.0"}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 40.9μs -> 36.3μs (12.6% faster)

# 3. Large Scale Test Cases

def test_large_number_of_currencies(zaif_instance):
    # Large scale: 500 currencies
    funds = {f"cur{i}": str(i) for i in range(500)}
    deposit = {f"cur{i}": str(i + 1) for i in range(500)}
    response = {"return": {"funds": funds, "deposit": deposit}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 3.46ms -> 2.66ms (30.0% faster)
    for i in range(500):
        code = f"CUR{i}".upper()

def test_large_balances_values(zaif_instance):
    # Large scale: very large balances
    big = 1e18
    response = {
        "return": {
            "funds": {"btc": str(big)},
            "deposit": {"btc": str(big + 1)}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 31.4μs -> 27.6μs (13.4% faster)

def test_large_scale_sparse_deposit(zaif_instance):
    # Large scale: 1000 currencies, only 10 in deposit
    funds = {f"cur{i}": str(i) for i in range(1000)}
    deposit = {f"cur{i}": str(i + 1) for i in range(0, 1000, 100)}
    response = {"return": {"funds": funds, "deposit": deposit}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 6.64ms -> 5.17ms (28.5% faster)
    for i in range(1000):
        code = f"CUR{i}".upper()
        if i % 100 == 0:
            pass
        else:
            pass

def test_large_scale_empty_funds(zaif_instance):
    # Large scale: empty funds, but deposit has many keys (should not add any)
    deposit = {f"cur{i}": str(i) for i in range(500)}
    response = {"return": {"funds": {}, "deposit": deposit}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.58μs -> 5.40μs (40.3% faster)

def test_large_scale_all_zero(zaif_instance):
    # Large scale: 1000 currencies, all zero
    funds = {f"cur{i}": "0" for i in range(1000)}
    deposit = {f"cur{i}": "0" for i in range(1000)}
    response = {"return": {"funds": funds, "deposit": deposit}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 6.89ms -> 5.35ms (28.9% faster)
    for i in range(1000):
        code = f"CUR{i}".upper()
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
import pytest
from ccxt.async_support.zaif import zaif

# --- Unit Tests ---

@pytest.fixture
def zaif_instance():
    return zaif()

# 1. Basic Test Cases

def test_basic_single_currency(zaif_instance):
    # Basic: Single currency, only funds, no deposit
    response = {
        'return': {
            'funds': {'btc': '0.12345678'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 31.3μs -> 27.3μs (14.8% faster)

def test_basic_multiple_currencies(zaif_instance):
    # Basic: Multiple currencies, only funds
    response = {
        'return': {
            'funds': {'btc': '0.1', 'jpy': '5000', 'xem': '1000'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 48.9μs -> 41.9μs (16.9% faster)

def test_basic_with_deposit(zaif_instance):
    # Basic: Funds and deposit, deposit overrides total
    response = {
        'return': {
            'funds': {'btc': '0.1', 'jpy': '5000'},
            'deposit': {'btc': '0.15', 'jpy': '6000'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 45.0μs -> 40.5μs (11.1% faster)

def test_basic_with_info_and_timestamp(zaif_instance):
    # Basic: Info and timestamp should be preserved
    response = {
        'return': {
            'funds': {'btc': '0.1'},
        },
        'extra': 'data'
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.1μs -> 26.6μs (13.3% faster)

# 2. Edge Test Cases

def test_edge_empty_funds(zaif_instance):
    # Edge: Empty funds dict
    response = {
        'return': {
            'funds': {}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.80μs -> 5.15μs (51.4% faster)

def test_edge_missing_funds_key(zaif_instance):
    # Edge: No 'funds' key
    response = {
        'return': {}
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.83μs -> 5.08μs (54.1% faster)

def test_edge_missing_return_key(zaif_instance):
    # Edge: No 'return' key
    response = {}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.63μs -> 4.83μs (58.1% faster)

def test_edge_deposit_partial_override(zaif_instance):
    # Edge: deposit only for some currencies
    response = {
        'return': {
            'funds': {'btc': '0.1', 'jpy': '5000'},
            'deposit': {'btc': '0.15'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 44.2μs -> 39.6μs (11.7% faster)

def test_edge_zero_and_negative_balances(zaif_instance):
    # Edge: Zero and negative balances
    response = {
        'return': {
            'funds': {'btc': '0', 'jpy': '-1000'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 40.8μs -> 35.5μs (15.0% faster)

def test_edge_non_string_balances(zaif_instance):
    # Edge: Balances as numbers (not strings)
    response = {
        'return': {
            'funds': {'btc': 0.5, 'jpy': 1000}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 44.3μs -> 39.1μs (13.4% faster)

def test_edge_currency_code_mapping(zaif_instance):
    # Edge: Test common currency code mapping
    response = {
        'return': {
            'funds': {'xbt': '1', 'bcc': '2', 'bchsv': '3'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 47.0μs -> 40.8μs (15.2% faster)

def test_edge_large_float_precision(zaif_instance):
    # Edge: Large float precision
    response = {
        'return': {
            'funds': {'btc': '0.12345678901234567890'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 33.0μs -> 28.9μs (14.0% faster)

def test_edge_unused_and_used_calculation(zaif_instance):
    # Edge: Used and total calculation
    # Simulate safe_balance logic: if free and used are present, total = free + used
    # We'll manually add 'used' to the account for this test
    response = {
        'return': {
            'funds': {'btc': '0.5'}
        }
    }
    # Patch the parse_balance output to add 'used' for BTC
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.3μs -> 26.7μs (13.7% faster)
    balance['BTC']['used'] = '0.2'
    # Now safe_balance should recalculate total
    balance = zaif_instance.safe_balance(balance)

def test_edge_debt_balance(zaif_instance):
    # Edge: Debt field is handled
    response = {
        'return': {
            'funds': {'btc': '1.0'},
        }
    }
    # Patch the parse_balance output to add 'debt'
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.4μs -> 26.7μs (14.0% faster)
    balance['BTC']['debt'] = '0.25'
    balance = zaif_instance.safe_balance(balance)

def test_edge_currency_id_case_insensitivity(zaif_instance):
    # Edge: Currency ids with mixed case
    response = {
        'return': {
            'funds': {'BtC': '0.5', 'JpY': '1000'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 41.2μs -> 36.2μs (14.0% faster)

def test_edge_nonexistent_deposit_currency(zaif_instance):
    # Edge: deposit has currency not in funds
    response = {
        'return': {
            'funds': {'btc': '1.0'},
            'deposit': {'eth': '2.0'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 30.3μs -> 26.7μs (13.3% faster)

def test_edge_unusual_currency_ids(zaif_instance):
    # Edge: Unusual currency ids
    response = {
        'return': {
            'funds': {'usdtt': '5.5', 'abc123': '9.9'}
        }
    }
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 40.8μs -> 36.1μs (13.0% faster)

# 3. Large Scale Test Cases

def test_large_scale_many_currencies(zaif_instance):
    # Large scale: 1000 currencies
    funds = {f'cur{i}': str(i) for i in range(1000)}
    response = {'return': {'funds': funds}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 6.57ms -> 5.12ms (28.4% faster)
    for i in range(1000):
        code = f'CUR{i}'.upper()

def test_large_scale_deposit_override(zaif_instance):
    # Large scale: 500 currencies, deposit overrides half
    funds = {f'cur{i}': str(i) for i in range(500)}
    deposit = {f'cur{i}': str(i + 1000) for i in range(250)}
    response = {'return': {'funds': funds, 'deposit': deposit}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 3.51ms -> 2.76ms (27.1% faster)
    for i in range(500):
        code = f'CUR{i}'.upper()
        expected_total = i + 1000 if i < 250 else i

def test_large_scale_empty_response(zaif_instance):
    # Large scale: Empty response
    response = {}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.58μs -> 4.96μs (52.8% faster)

def test_large_scale_all_zero_balances(zaif_instance):
    # Large scale: All balances zero
    funds = {f'cur{i}': '0' for i in range(1000)}
    response = {'return': {'funds': funds}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 6.63ms -> 5.15ms (28.6% faster)
    for i in range(1000):
        code = f'CUR{i}'.upper()

def test_large_scale_high_precision_balances(zaif_instance):
    # Large scale: High precision balances
    funds = {f'cur{i}': f'{i}.1234567890123456' for i in range(1000)}
    response = {'return': {'funds': funds}}
    codeflash_output = zaif_instance.parse_balance(response); balance = codeflash_output # 7.18ms -> 5.81ms (23.6% faster)
    for i in range(1000):
        code = f'CUR{i}'.upper()
        expected = float(f'{i}.1234567890123456')
# 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-zaif.parse_balance-mhx97taz and push.

Codeflash

The optimized code achieves a **27% speedup** through several key optimizations targeting the hot paths identified in the profiling data:

**Primary Optimizations:**

1. **Inline dictionary access in `safe_string` and `safe_value`**: Instead of calling `Exchange.key_exists()` (which adds function call overhead), the optimized version directly uses `isinstance(dictionary, dict)` checks and `dictionary.get()` or `key in dictionary` operations. This eliminates ~52ms of function call overhead seen in the original profiling.

2. **Direct dictionary iteration in `safe_balance`**: Replaced the inefficient `codes = list(balances.keys())` followed by `for i in range(0, len(codes)): code = codes[i]` pattern with direct `for code, entry in balances.items()`. This eliminates list allocation and indexing overhead, reducing iteration cost from O(n) list creation + O(n) indexing to O(n) direct iteration.

3. **Optimized dictionary comprehension for omitting keys**: Replaced the `self.omit()` function call with an inline dictionary comprehension `{k: v for k, v in balance.items() if k not in to_omit}` using a set for O(1) membership testing.

4. **Simplified loop in `parse_balance`**: Changed from `for i in range(0, len(currencyIds)): currencyId = currencyIds[i]` to direct `for currencyId, balance in funds.items()`, eliminating list creation and index-based access.

5. **Conditional optimization**: Combined the deposit check `if deposit is not None and currencyId in deposit` to short-circuit evaluation, avoiding unnecessary membership tests when deposit is None.

**Performance Impact:**
- The optimizations are particularly effective for large-scale test cases (28-30% speedup with 500-1000 currencies)
- Edge cases with empty/minimal data show even larger improvements (40-58% speedup) due to reduced setup overhead
- Basic cases with few currencies still benefit significantly (11-16% speedup)

**Why These Work:**
These optimizations target Python's performance characteristics: reducing function call overhead, minimizing dictionary lookups, eliminating unnecessary list allocations, and using more efficient iteration patterns. The improvements compound when processing many currencies, making this especially valuable for exchanges handling large numbers of trading pairs.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 13, 2025 09:56
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Nov 13, 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant