Skip to content

Conversation

@codeflash-ai
Copy link

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

📄 43% (0.43x) speedup for KeepKeyPlugin._dev_to_str in electrum/plugins/keepkey/keepkey.py

⏱️ Runtime : 743 microseconds 519 microseconds (best of 163 runs)

📝 Explanation and details

The optimization replaces a generator expression with list construction and unpacking, achieving a 43% speedup by eliminating expensive intermediate operations.

Key Changes:

  • Eliminated list concatenation: Removed ["%03i" % (dev.getBusNumber(),)] + dev.getPortNumberList() which creates two temporary lists and concatenates them
  • Pre-computed bus number string: Extracted "%03i" % dev.getBusNumber() to a variable to avoid repeated formatting
  • Used unpacking with map(): Replaced generator expression str(x) for x in [...] with direct list construction [bus_number_str, *map(str, dev.getPortNumberList())]

Why This Is Faster:

  1. Reduced memory allocations: The original creates a temporary list for the bus number, concatenates it with the port list, then iterates with a generator. The optimized version constructs the final list directly.
  2. Eliminated redundant string conversion: The bus number is pre-formatted once instead of being processed through the generic str(x) conversion.
  3. More efficient iteration: map(str, ...) is faster than a generator expression for simple transformations like string conversion.

Performance Benefits by Test Case:

  • Small inputs (1-3 ports): 13-27% faster due to reduced overhead
  • Large inputs (999+ ports): 47-58% faster as the optimization scales better with list size
  • Edge cases (empty ports, special values): 20-30% faster from eliminating unnecessary list operations

This function appears in the USB device enumeration path for KeepKey hardware wallets, where it converts USB device identifiers to string format. The optimization is particularly valuable during device discovery when multiple USB devices may be processed rapidly.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 50 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime

import pytest
from electrum.plugins.keepkey.keepkey import KeepKeyPlugin

function to test (copied and slightly adapted for standalone testing)

class DummyUSBDevice:
"""
Dummy class to simulate usb1.USBDevice for testing _dev_to_str.
"""
def init(self, bus_number, port_number_list):
self._bus_number = bus_number
self._port_number_list = port_number_list

def getBusNumber(self):
    return self._bus_number

def getPortNumberList(self):
    # Return a *copy* to avoid mutating internal state in tests
    return list(self._port_number_list)

from electrum.plugins.keepkey.keepkey import KeepKeyPlugin

-------------------------------

Basic Test Cases

-------------------------------

def test_single_port_basic():
# Single port, typical case
dev = DummyUSBDevice(1, [2])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 4.08μs -> 3.52μs (15.7% faster)

def test_multiple_ports_basic():
# Multiple ports, typical case
dev = DummyUSBDevice(5, [3, 4, 7])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.69μs -> 3.26μs (13.1% faster)

def test_no_ports():
# No ports, should only show bus number
dev = DummyUSBDevice(12, [])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 2.84μs -> 2.36μs (20.4% faster)

def test_bus_number_padding():
# Bus number should always be 3 digits, zero-padded
dev = DummyUSBDevice(7, [1])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.24μs -> 2.81μs (15.2% faster)
dev2 = DummyUSBDevice(99, [5])
codeflash_output = KeepKeyPlugin._dev_to_str(dev2); result2 = codeflash_output # 1.67μs -> 1.43μs (17.0% faster)
dev3 = DummyUSBDevice(100, [8])
codeflash_output = KeepKeyPlugin._dev_to_str(dev3); result3 = codeflash_output # 1.42μs -> 1.14μs (25.4% faster)

def test_port_numbers_as_integers():
# Port numbers should be converted to strings, not zero-padded
dev = DummyUSBDevice(2, [0, 10, 255])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.32μs -> 2.87μs (15.8% faster)

-------------------------------

Edge Test Cases

-------------------------------

def test_bus_number_zero():
# Edge: bus number is zero
dev = DummyUSBDevice(0, [1, 2])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.23μs -> 2.56μs (25.7% faster)

def test_large_bus_number():
# Edge: bus number is large (>=1000)
dev = DummyUSBDevice(1234, [5, 6])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.00μs -> 2.56μs (17.4% faster)

def test_large_port_numbers():
# Edge: port numbers are large
dev = DummyUSBDevice(3, [999, 1000, 12345])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.48μs -> 2.90μs (20.0% faster)

def test_empty_port_list_and_zero_bus():
# Edge: both bus number and port list are minimal
dev = DummyUSBDevice(0, [])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 2.66μs -> 2.04μs (30.5% faster)

def test_negative_bus_number():
# Edge: negative bus number (should still format with zero padding and minus sign)
dev = DummyUSBDevice(-1, [1])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.27μs -> 2.73μs (19.8% faster)

def test_negative_port_numbers():
# Edge: negative port numbers
dev = DummyUSBDevice(1, [-2, -3])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.31μs -> 2.75μs (20.4% faster)

def test_non_integer_port_numbers():
# Edge: port numbers are not integers (should still stringify)
dev = DummyUSBDevice(2, ["a", 3.5, None])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 4.95μs -> 4.45μs (11.5% faster)

def test_port_number_list_is_tuple():
# Edge: port number list is a tuple, not a list
dev = DummyUSBDevice(4, (7, 8))
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.15μs -> 2.65μs (18.7% faster)

def test_port_number_list_has_empty_string():
# Edge: port number list contains an empty string
dev = DummyUSBDevice(5, [""])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 2.75μs -> 2.29μs (20.4% faster)

def test_port_number_list_has_none():
# Edge: port number list contains None
dev = DummyUSBDevice(6, [None])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 2.92μs -> 2.57μs (13.6% faster)

-------------------------------

Large Scale Test Cases

-------------------------------

def test_large_port_number_list():
# Large scale: port list with 999 elements
ports = list(range(1, 1000))
dev = DummyUSBDevice(42, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 130μs -> 82.4μs (57.8% faster)

def test_large_bus_number_and_ports():
# Large scale: large bus number, large port numbers
ports = [99999 for _ in range(100)]
dev = DummyUSBDevice(12345, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 18.1μs -> 13.7μs (32.3% faster)

def test_performance_large_input():
# Large scale: stress test for performance (not to exceed 1000 elements)
ports = list(range(1000))
dev = DummyUSBDevice(999, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 127μs -> 84.7μs (50.5% faster)
# Should not raise or hang, and should be correct
expected = "999" if 999 >= 1000 else "999".zfill(3)
expected = "%03i" % 999
expected_str = expected + ":" + ":".join(str(i) for i in range(1000))

def test_all_zero_ports_large():
# Large scale: all ports are zero
ports = [0]*500
dev = DummyUSBDevice(1, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 61.7μs -> 42.0μs (47.0% faster)

-------------------------------

Mutation Testing Guards

-------------------------------

@pytest.mark.parametrize("bus,ports,expected", [
(1, [], "001"),
(1, [2], "001:2"),
(1, [2, 3], "001:2:3"),
(0, [], "000"),
(0, [0], "000:0"),
(999, [], "999"),
(1000, [], "1000"),
(1, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "001:0:1:2:3:4:5:6:7:8:9"),
])
def test_parametrized_cases(bus, ports, expected):
# Parametrized to catch off-by-one, wrong join, wrong padding, etc.
dev = DummyUSBDevice(bus, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 25.8μs -> 21.5μs (19.9% 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
from electrum.plugins.keepkey.keepkey import KeepKeyPlugin

function to test (extracted and simplified for unit testing)

class DummyUSBDevice:
"""
Dummy class to simulate usb1.USBDevice for testing _dev_to_str.
"""
def init(self, bus_number, port_number_list):
self._bus_number = bus_number
self._port_number_list = port_number_list

def getBusNumber(self):
    return self._bus_number

def getPortNumberList(self):
    # Defensive copy to avoid accidental mutation
    return list(self._port_number_list)

from electrum.plugins.keepkey.keepkey import KeepKeyPlugin

---------------------- UNIT TESTS ----------------------

1. BASIC TEST CASES

def test_single_port_basic():
# Device with bus 1, port [2]
dev = DummyUSBDevice(1, [2])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.85μs -> 3.40μs (13.2% faster)

def test_multiple_ports_basic():
# Device with bus 5, ports [3, 4, 7]
dev = DummyUSBDevice(5, [3, 4, 7])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.85μs -> 3.38μs (14.2% faster)

def test_bus_number_padding():
# Device with bus 12, port [8]
dev = DummyUSBDevice(12, [8])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.38μs -> 2.88μs (17.3% faster)

def test_bus_number_triple_digits():
# Device with bus 123, port [1]
dev = DummyUSBDevice(123, [1])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.04μs -> 2.51μs (20.9% faster)

def test_port_numbers_are_strings():
# Device with bus 2, ports [11, 22]
dev = DummyUSBDevice(2, [11, 22])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.56μs -> 3.03μs (17.7% faster)

2. EDGE TEST CASES

def test_zero_bus_and_port():
# Bus number 0, port [0]
dev = DummyUSBDevice(0, [0])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.18μs -> 2.65μs (20.0% faster)

def test_empty_port_list():
# Bus number 7, no ports
dev = DummyUSBDevice(7, [])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 2.88μs -> 2.28μs (26.3% faster)

def test_long_port_list():
# Bus 9, many ports
ports = list(range(1, 10)) # [1,2,3,4,5,6,7,8,9]
dev = DummyUSBDevice(9, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 4.78μs -> 3.69μs (29.5% faster)

def test_negative_bus_number():
# Negative bus number, valid port
dev = DummyUSBDevice(-1, [2])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.39μs -> 2.91μs (16.5% faster)

def test_negative_port_number():
# Valid bus, negative port
dev = DummyUSBDevice(1, [-2])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.11μs -> 2.62μs (18.8% faster)

def test_large_bus_and_port_numbers():
# Large bus and port numbers
dev = DummyUSBDevice(999, [999, 1000, 12345])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.61μs -> 2.84μs (27.4% faster)

def test_port_numbers_are_mutable():
# Ensure that changing the returned list from getPortNumberList doesn't affect future calls
ports = [1, 2, 3]
dev = DummyUSBDevice(10, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result1 = codeflash_output # 3.54μs -> 3.02μs (17.1% faster)
ports.append(4) # mutate external list
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result2 = codeflash_output # 2.44μs -> 1.83μs (32.9% faster)

def test_port_numbers_are_non_integer_strings():
# Simulate a device returning strings (should be robust to this)
class WeirdUSBDevice(DummyUSBDevice):
def getPortNumberList(self):
return ["a", "b"]
dev = WeirdUSBDevice(5, [])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.15μs -> 2.87μs (9.68% faster)

def test_port_numbers_are_mixed_types():
# Simulate a device returning mixed types
class MixedUSBDevice(DummyUSBDevice):
def getPortNumberList(self):
return [1, "x", 3.5]
dev = MixedUSBDevice(4, [])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 5.24μs -> 4.69μs (11.7% faster)

3. LARGE SCALE TEST CASES

def test_large_port_list():
# Device with bus 42, 999 ports
ports = list(range(1, 1000))
dev = DummyUSBDevice(42, ports)
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 129μs -> 85.5μs (51.2% faster)

def test_performance_large_scale():
# This test checks that the function completes in a reasonable time for large input
import time
ports = list(range(1000))
dev = DummyUSBDevice(1, ports)
start = time.time()
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 127μs -> 84.9μs (50.5% faster)
end = time.time()

4. ADDITIONAL EDGE CASES

def test_port_list_with_none():
# Port list contains None
class NonePortUSBDevice(DummyUSBDevice):
def getPortNumberList(self):
return [None, 2]
dev = NonePortUSBDevice(1, [])
codeflash_output = KeepKeyPlugin._dev_to_str(dev); result = codeflash_output # 3.63μs -> 3.24μs (11.8% faster)

def test_port_list_is_none():
# getPortNumberList returns None
class NoneListUSBDevice(DummyUSBDevice):
def getPortNumberList(self):
return None
dev = NoneListUSBDevice(1, [])
with pytest.raises(TypeError):
KeepKeyPlugin._dev_to_str(dev) # 3.88μs -> 3.89μs (0.385% slower)

def test_missing_getBusNumber():
# Device without getBusNumber
class NoBusUSBDevice:
def getPortNumberList(self):
return [1]
dev = NoBusUSBDevice()
with pytest.raises(AttributeError):
KeepKeyPlugin._dev_to_str(dev) # 1.90μs -> 1.60μs (18.7% faster)

def test_missing_getPortNumberList():
# Device without getPortNumberList
class NoPortUSBDevice:
def getBusNumber(self):
return 1
dev = NoPortUSBDevice()
with pytest.raises(AttributeError):
KeepKeyPlugin._dev_to_str(dev) # 3.07μs -> 2.92μs (5.14% faster)

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-KeepKeyPlugin._dev_to_str-mhwex1qw and push.

Codeflash Static Badge

The optimization replaces a generator expression with list construction and unpacking, achieving a **43% speedup** by eliminating expensive intermediate operations.

**Key Changes:**
- **Eliminated list concatenation**: Removed `["%03i" % (dev.getBusNumber(),)] + dev.getPortNumberList()` which creates two temporary lists and concatenates them
- **Pre-computed bus number string**: Extracted `"%03i" % dev.getBusNumber()` to a variable to avoid repeated formatting
- **Used unpacking with map()**: Replaced generator expression `str(x) for x in [...]` with direct list construction `[bus_number_str, *map(str, dev.getPortNumberList())]`

**Why This Is Faster:**
1. **Reduced memory allocations**: The original creates a temporary list for the bus number, concatenates it with the port list, then iterates with a generator. The optimized version constructs the final list directly.
2. **Eliminated redundant string conversion**: The bus number is pre-formatted once instead of being processed through the generic `str(x)` conversion.
3. **More efficient iteration**: `map(str, ...)` is faster than a generator expression for simple transformations like string conversion.

**Performance Benefits by Test Case:**
- **Small inputs** (1-3 ports): 13-27% faster due to reduced overhead
- **Large inputs** (999+ ports): 47-58% faster as the optimization scales better with list size
- **Edge cases** (empty ports, special values): 20-30% faster from eliminating unnecessary list operations

This function appears in the USB device enumeration path for KeepKey hardware wallets, where it converts USB device identifiers to string format. The optimization is particularly valuable during device discovery when multiple USB devices may be processed rapidly.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 12, 2025 19:48
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Nov 12, 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