⚡️ Speed up method IPAdapterField.validate_ip_adapter_weight by 33%
#148
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.
📄 33% (0.33x) speedup for
IPAdapterField.validate_ip_adapter_weightininvokeai/app/invocations/ip_adapter.py⏱️ Runtime :
319 microseconds→240 microseconds(best of130runs)📝 Explanation and details
The optimization replaces the original
any()function with generator expression approach with a more direct conditional logic that eliminates intermediate data structure creation and function call overhead.Key Changes:
Eliminated list creation for single floats: Instead of always converting single floats to
[weights], the optimized version handles single floats directly with a simple range check, avoiding list allocation entirely.Replaced
any()with explicit loop: The original code usedany(i < -1 or i > 2 for i in to_validate)which creates a generator object and involves function call overhead. The optimized version uses a directforloop that short-circuits immediately when an invalid value is found.Branch specialization: By using separate
if/elsebranches for lists vs single values, each code path is optimized for its specific data type without unnecessary conversions.Why This is Faster:
any()function call and generator creationPerformance Impact:
The test results show consistent 30-77% speedups across all scenarios, with particularly strong gains for single float validation (up to 77% faster). Even large lists with 1000 elements see ~30% improvement, demonstrating the optimization scales well. The validation is commonly called during model parameter setup, making these microsecond savings meaningful in aggregate.
✅ Correctness verification report:
🌀 Generated Regression Tests and Runtime
from typing import Union
imports
import pytest
from invokeai.app.invocations.ip_adapter import IPAdapterField
-------------------------------
Unit tests for validate_ip_adapter_weight
-------------------------------
1. Basic Test Cases
def test_single_valid_weight_middle():
# Test a single valid weight in the middle of the range
codeflash_output = IPAdapterField.validate_ip_adapter_weight(1.0) # 1.99μs -> 1.13μs (75.8% faster)
def test_single_valid_weight_lower_bound():
# Test a single valid weight at the lower bound
codeflash_output = IPAdapterField.validate_ip_adapter_weight(-1.0) # 1.76μs -> 1.07μs (65.3% faster)
def test_single_valid_weight_upper_bound():
# Test a single valid weight at the upper bound
codeflash_output = IPAdapterField.validate_ip_adapter_weight(2.0) # 1.71μs -> 997ns (71.5% faster)
def test_list_valid_weights():
# Test a list of valid weights
weights = [0.0, 1.5, -0.5, 2.0, -1.0]
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 2.10μs -> 1.41μs (49.0% faster)
def test_list_valid_weights_bounds():
# Test a list with weights at the bounds
weights = [-1.0, 2.0]
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 1.67μs -> 1.17μs (43.3% faster)
2. Edge Test Cases
def test_single_weight_below_lower_bound():
# Test a single weight just below the lower bound
with pytest.raises(ValueError) as excinfo:
IPAdapterField.validate_ip_adapter_weight(-1.00001) # 2.30μs -> 1.45μs (58.6% faster)
def test_single_weight_above_upper_bound():
# Test a single weight just above the upper bound
with pytest.raises(ValueError) as excinfo:
IPAdapterField.validate_ip_adapter_weight(2.00001) # 2.29μs -> 1.52μs (50.2% faster)
def test_list_with_one_invalid_weight_below():
# Test a list with one invalid weight below the lower bound
weights = [0.0, -1.1, 1.0]
with pytest.raises(ValueError) as excinfo:
IPAdapterField.validate_ip_adapter_weight(weights) # 2.47μs -> 1.70μs (45.6% faster)
def test_list_with_one_invalid_weight_above():
# Test a list with one invalid weight above the upper bound
weights = [2.1, 1.0, 0.0]
with pytest.raises(ValueError) as excinfo:
IPAdapterField.validate_ip_adapter_weight(weights) # 2.20μs -> 1.53μs (44.0% faster)
def test_list_with_multiple_invalid_weights():
# Test a list with multiple invalid weights
weights = [-1.2, 2.3, 0.5]
with pytest.raises(ValueError) as excinfo:
IPAdapterField.validate_ip_adapter_weight(weights) # 2.07μs -> 1.39μs (48.7% faster)
def test_list_with_valid_and_invalid_weights():
# Test a list with both valid and invalid weights
weights = [-1.0, 0.0, 2.1]
with pytest.raises(ValueError) as excinfo:
IPAdapterField.validate_ip_adapter_weight(weights) # 2.78μs -> 1.96μs (42.2% faster)
def test_empty_list():
# Test an empty list (should be valid as there are no invalid weights)
weights = []
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 1.31μs -> 770ns (70.1% faster)
def test_single_weight_at_float_precision_limit():
# Test a single weight at float precision boundary
codeflash_output = IPAdapterField.validate_ip_adapter_weight(-1.0 + 1e-15) # 1.79μs -> 1.01μs (77.3% faster)
codeflash_output = IPAdapterField.validate_ip_adapter_weight(2.0 - 1e-15) # 754ns -> 484ns (55.8% faster)
def test_list_with_float_precision_limits():
# Test a list with weights at float precision boundaries
weights = [-1.0, 2.0, 1.9999999999999998, -0.9999999999999998]
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 1.92μs -> 1.28μs (50.0% faster)
def test_weight_as_integer():
# Test integer values for weights
codeflash_output = IPAdapterField.validate_ip_adapter_weight(0) # 1.52μs -> 761ns (99.5% faster)
codeflash_output = IPAdapterField.validate_ip_adapter_weight(2) # 653ns -> 389ns (67.9% faster)
codeflash_output = IPAdapterField.validate_ip_adapter_weight(-1) # 556ns -> 329ns (69.0% faster)
codeflash_output = IPAdapterField.validate_ip_adapter_weight([0, 1, 2, -1]) # 898ns -> 737ns (21.8% faster)
def test_weight_as_large_negative_integer():
# Test a large negative integer
with pytest.raises(ValueError):
IPAdapterField.validate_ip_adapter_weight(-100) # 2.21μs -> 1.37μs (61.7% faster)
def test_weight_as_large_positive_integer():
# Test a large positive integer
with pytest.raises(ValueError):
IPAdapterField.validate_ip_adapter_weight(100) # 2.22μs -> 1.29μs (71.5% faster)
def test_weight_as_non_numeric_type():
# Test passing a string instead of a float/list
with pytest.raises(TypeError):
IPAdapterField.validate_ip_adapter_weight("not_a_number") # 2.92μs -> 2.17μs (34.7% faster)
def test_weight_as_list_with_non_numeric():
# Test passing a list with a non-numeric value
weights = [1.0, "bad", 0.0]
with pytest.raises(TypeError):
IPAdapterField.validate_ip_adapter_weight(weights) # 3.02μs -> 2.31μs (30.7% faster)
def test_weight_as_none():
# Test passing None as weight
with pytest.raises(TypeError):
IPAdapterField.validate_ip_adapter_weight(None) # 2.50μs -> 1.86μs (34.0% faster)
3. Large Scale Test Cases
def test_large_list_all_valid():
# Test a large list of valid weights
weights = [1.0 for _ in range(1000)]
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 44.3μs -> 34.2μs (29.4% faster)
def test_large_list_with_one_invalid_at_start():
# Test a large list with one invalid value at the start
weights = [-1.1] + [1.0 for _ in range(999)]
with pytest.raises(ValueError):
IPAdapterField.validate_ip_adapter_weight(weights) # 2.26μs -> 1.45μs (55.5% faster)
def test_large_list_with_one_invalid_at_end():
# Test a large list with one invalid value at the end
weights = [1.0 for _ in range(999)] + [2.1]
with pytest.raises(ValueError):
IPAdapterField.validate_ip_adapter_weight(weights) # 45.1μs -> 34.6μs (30.3% faster)
def test_large_list_with_one_invalid_in_middle():
# Test a large list with one invalid value in the middle
weights = [1.0 for _ in range(500)] + [-1.2] + [1.0 for _ in range(499)]
with pytest.raises(ValueError):
IPAdapterField.validate_ip_adapter_weight(weights) # 23.8μs -> 18.3μs (30.4% faster)
def test_large_list_with_multiple_invalid():
# Test a large list with multiple invalid values
weights = [1.0 for _ in range(495)] + [-2.0, 3.0, -1.1, 2.5, 1.0, 0.5, 2.0, -1.0, 1.0, 2.0]
with pytest.raises(ValueError):
IPAdapterField.validate_ip_adapter_weight(weights) # 23.7μs -> 18.1μs (30.9% faster)
def test_large_list_with_all_valid_and_edge_values():
# Test a large list with valid and edge values
weights = [-1.0, 2.0] * 500
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 44.5μs -> 34.6μs (28.7% faster)
def test_large_list_empty():
# Test a large empty list (should be valid)
weights = []
codeflash_output = IPAdapterField.validate_ip_adapter_weight(weights) # 1.28μs -> 780ns (64.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 typing import List, Optional, Union
imports
import pytest
from invokeai.app.invocations.ip_adapter import IPAdapterField
from pydantic import BaseModel, Field, field_validator
unit tests
---------------------------
Basic Test Cases
---------------------------
def test_single_weight_below_lower_bound():
"""Test a single weight just below the lower bound."""
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=-1.0001)
def test_single_weight_above_upper_bound():
"""Test a single weight just above the upper bound."""
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=2.0001)
def test_list_of_weights_one_below_bound():
"""Test a list with one weight below the lower bound."""
weights = [0.5, -1.1, 1.0]
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=weights)
def test_list_of_weights_one_above_bound():
"""Test a list with one weight above the upper bound."""
weights = [0.5, 2.1, 1.0]
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=weights)
def test_list_of_weights_multiple_out_of_bounds():
"""Test a list with multiple out-of-bounds weights."""
weights = [-1.5, 2.5, 0.0]
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=weights)
def test_weight_is_string_should_fail():
"""Test that a string weight raises a validation error."""
with pytest.raises(Exception):
IPAdapterField(weight="1.0")
def test_list_with_non_numeric_should_fail():
"""Test that a list with a non-numeric value raises a validation error."""
with pytest.raises(Exception):
IPAdapterField(weight=[1.0, "bad", 0.0])
def test_nan_weight_should_fail():
"""Test that a NaN weight raises a validation error."""
import math
with pytest.raises(ValueError):
IPAdapterField(weight=math.nan)
def test_list_with_nan_weight_should_fail():
"""Test that a list containing NaN raises a validation error."""
import math
with pytest.raises(ValueError):
IPAdapterField(weight=[0.0, math.nan, 1.0])
def test_inf_weight_should_fail():
"""Test that an infinite weight raises a validation error."""
with pytest.raises(ValueError):
IPAdapterField(weight=float('inf'))
def test_list_with_inf_weight_should_fail():
"""Test that a list containing infinity raises a validation error."""
with pytest.raises(ValueError):
IPAdapterField(weight=[0.0, float('-inf'), 1.0])
---------------------------
Large Scale Test Cases
---------------------------
def test_large_list_one_invalid_weight():
"""Test a large list (1000 elements) with one invalid weight."""
weights = [1.0 for _ in range(999)] + [2.1]
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=weights)
def test_large_list_all_invalid_weights():
"""Test a large list (1000 elements) all out of bounds."""
weights = [2.5 for _ in range(1000)]
with pytest.raises(ValueError) as excinfo:
IPAdapterField(weight=weights)
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-IPAdapterField.validate_ip_adapter_weight-mhvss6twand push.