⚡️ Speed up method KeepKeyPlugin._dev_to_str by 43%
#125
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📄 43% (0.43x) speedup for
KeepKeyPlugin._dev_to_strinelectrum/plugins/keepkey/keepkey.py⏱️ Runtime :
743 microseconds→519 microseconds(best of163runs)📝 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:
["%03i" % (dev.getBusNumber(),)] + dev.getPortNumberList()which creates two temporary lists and concatenates them"%03i" % dev.getBusNumber()to a variable to avoid repeated formattingstr(x) for x in [...]with direct list construction[bus_number_str, *map(str, dev.getPortNumberList())]Why This Is Faster:
str(x)conversion.map(str, ...)is faster than a generator expression for simple transformations like string conversion.Performance Benefits by Test Case:
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:
🌀 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
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
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-mhwex1qwand push.