-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Open
Labels
Description
Bug Report
indexing fixed-length tuples is extremely slow and gets slower with increse in length
To Reproduce
cat reproduce.py
import timeit
from typing import Final
# fmt: off
FixedLength256 = tuple[
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int
]
FixedLength128 = tuple[
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int
]
FixedLength64 = tuple[
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int
]
FixedLength32 = tuple[
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int
]
FixedLength16 = tuple[
int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int
]
FixedLength8 = tuple[int, int, int, int, int, int, int, int]
FixedLength4 = tuple[int, int, int, int]
FixedLength2 = tuple[int, int]
FixedLength1 = tuple[int]
# fmt: on
FIXED_LENGTH_256: Final[FixedLength256] = (0,) * 256
FIXED_LENGTH_128: Final[FixedLength128] = (0,) * 128
FIXED_LENGTH_64: Final[FixedLength64] = (0,) * 64
FIXED_LENGTH_32: Final[FixedLength32] = (0,) * 32
FIXED_LENGTH_16: Final[FixedLength16] = (0,) * 16
FIXED_LENGTH_8: Final[FixedLength8] = (0,) * 8
FIXED_LENGTH_4: Final[FixedLength4] = (0,) * 4
FIXED_LENGTH_2: Final[FixedLength2] = (0,) * 2
FIXED_LENGTH_1: Final[FixedLength1] = (0,) * 1
VARIABLE_LENGTH_256: Final[tuple[int, ...]] = (0,) * 256
VARIABLE_LENGTH_128: Final[tuple[int, ...]] = (0,) * 128
VARIABLE_LENGTH_64: Final[tuple[int, ...]] = (0,) * 64
VARIABLE_LENGTH_32: Final[tuple[int, ...]] = (0,) * 32
VARIABLE_LENGTH_16: Final[tuple[int, ...]] = (0,) * 16
VARIABLE_LENGTH_8: Final[tuple[int, ...]] = (0,) * 8
VARIABLE_LENGTH_4: Final[tuple[int, ...]] = (0,) * 4
VARIABLE_LENGTH_2: Final[tuple[int, ...]] = (0,) * 2
VARIABLE_LENGTH_1: Final[tuple[int, ...]] = (0,) * 1
def loop_fixed_length_256() -> None:
for i in range(256):
FIXED_LENGTH_256[i]
def loop_variable_length_256() -> None:
for i in range(256):
VARIABLE_LENGTH_256[i]
def loop_fixed_length_128() -> None:
for _ in range(2): # normalize against largest
for i in range(128):
FIXED_LENGTH_128[i]
def loop_variable_length_128() -> None:
for _ in range(2):
for i in range(128):
VARIABLE_LENGTH_128[i]
def loop_fixed_length_64() -> None:
for _ in range(4):
for i in range(64):
FIXED_LENGTH_64[i]
def loop_variable_length_64() -> None:
for _ in range(4):
for i in range(64):
VARIABLE_LENGTH_64[i]
def loop_fixed_length_32() -> None:
for _ in range(8):
for i in range(32):
FIXED_LENGTH_32[i]
def loop_variable_length_32() -> None:
for _ in range(8):
for i in range(32):
VARIABLE_LENGTH_32[i]
def loop_fixed_length_16() -> None:
for _ in range(16):
for i in range(16):
FIXED_LENGTH_16[i]
def loop_variable_length_16() -> None:
for _ in range(16):
for i in range(16):
VARIABLE_LENGTH_16[i]
def loop_fixed_length_8() -> None:
for _ in range(32):
for i in range(8):
FIXED_LENGTH_8[i]
def loop_variable_length_8() -> None:
for _ in range(32):
for i in range(8):
VARIABLE_LENGTH_8[i]
def loop_fixed_length_4() -> None:
for _ in range(64):
for i in range(4):
FIXED_LENGTH_4[i]
def loop_variable_length_4() -> None:
for _ in range(64):
for i in range(4):
VARIABLE_LENGTH_4[i]
def loop_fixed_length_2() -> None:
for _ in range(128):
for i in range(2):
FIXED_LENGTH_2[i]
def loop_variable_length_2() -> None:
for _ in range(128):
for i in range(2):
VARIABLE_LENGTH_2[i]
def loop_fixed_length_1() -> None:
for _ in range(256):
FIXED_LENGTH_1[0]
def loop_variable_length_1() -> None:
for _ in range(256):
VARIABLE_LENGTH_1[0]
def bench() -> None:
pairs = [
("256", loop_fixed_length_256, loop_variable_length_256),
("128", loop_fixed_length_128, loop_variable_length_128),
("64", loop_fixed_length_64, loop_variable_length_64),
("32", loop_fixed_length_32, loop_variable_length_32),
("16", loop_fixed_length_16, loop_variable_length_16),
("8", loop_fixed_length_8, loop_variable_length_8),
("4", loop_fixed_length_4, loop_variable_length_4),
("2", loop_fixed_length_2, loop_variable_length_2),
("1", loop_fixed_length_1, loop_variable_length_1),
]
header = f"{'Length':<6} | {'Fixed (ms)':>12} | {'Variable (ms)':>14} | {'Ratio (V/F)':>10}"
print(header)
print("-" * len(header))
for length, fixed_func, variable_func in pairs:
fixed_time = timeit.timeit(fixed_func, number=10_000)
variable_time = timeit.timeit(variable_func, number=10_000)
ratio = variable_time / fixed_time if fixed_time != 0 else float("inf")
print(
f"{length:<6} | {fixed_time * 1000:12.3f} | {variable_time * 1000:14.3f} | {ratio:10.3f}"
)
mypyc reproduce.py
running build_ext
building 'reproduce' extension
clang -fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -arch arm64 -mmacosx-version-min=11.0 -Wno-nullability-completeness -Wno-expansion-to-defined -Wno-undef-prefix -fPIC -Werror=unguarded-availability-new -I/Users/bobronium/dev/py/duper/.venv/lib/python3.12/site-packages/mypyc/lib-rt -I/Users/bobronium/dev/py/duper/.venv/include -I/Users/bobronium/.local/share/uv/python/cpython-3.12.5-macos-aarch64-none/include/python3.12 -c build/__native.c -o build/temp.macosx-11.0-arm64-cpython-312/build/__native.o -O3 -g1 -Werror -Wno-unused-function -Wno-unused-label -Wno-unreachable-code -Wno-unused-variable -Wno-unused-command-line-argument -Wno-unknown-warning-option -Wno-unused-but-set-variable -Wno-ignored-optimization-argument -Wno-cpp
clang -bundle -undefined dynamic_lookup -arch arm64 -mmacosx-version-min=11.0 -LModules/_hacl build/temp.macosx-11.0-arm64-cpython-312/build/__native.o -L/install/lib -o build/lib.macosx-11.0-arm64-cpython-312/reproduce.cpython-312-darwin.so
ld: warning: search path 'Modules/_hacl' not found
ld: warning: search path '/install/lib' not found
copying build/lib.macosx-11.0-arm64-cpython-312/reproduce.cpython-312-darwin.so ->
python -c 'from reproduce import bench; bench()'
Length | Fixed (ms) | Variable (ms) | Ratio (F/V)
----------------------------------------------------
256 | 2625.639 | 6.894 | 380.847
128 | 1403.156 | 7.195 | 195.024
64 | 883.360 | 7.535 | 117.228
32 | 356.185 | 8.084 | 44.058
16 | 175.171 | 7.439 | 23.547
8 | 114.896 | 8.011 | 14.343
4 | 101.696 | 8.443 | 12.045
2 | 71.238 | 9.199 | 7.744
1 | 7.490 | 12.126 | 0.618
Expected Behavior
Fixed-length tuples behave faster or equal to variable-length tuples.
Actual Behavior
See output above: only case when fixed-length tuple is faster is when its length is 1. At length of 2, indexing fixed-length tuple is 7 times slower than variable-length one.
Your Environment
- Mypy version used: mypy 1.17.0 and 1.14.1
- Mypy command-line flags:
- Mypy configuration options from
mypy.ini
(and other config files):
pyproject.toml
[tool.mypy]
disallow_any_unimported = true
disallow_any_decorated = true
disallow_any_generics = true
disallow_subclassing_any = true
# Untyped definitions and calls
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
# None and Optional handling
no_implicit_optional = true
strict_optional = true
# Configuring warnings
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_return_any = true
warn_unreachable = true
warn_incomplete_stub = true
warn_unused_configs = true
# Suppressing errors
ignore_errors = false
enable_error_code = "ignore-without-code"
# Miscellaneous strictness flags
allow_untyped_globals = false
allow_redefinition = false
local_partial_types = false
implicit_reexport = false
strict_equality = true
strict = true
# enable once this is resolved: https://github.com/python/mypy/issues/14796
# no_silence_site_packages = true
# Configuring error messages
show_error_context = false
show_column_numbers = false
show_error_codes = true
color_output = true
error_summary = true
- Python version used: 3.12.5