Tuple Performance
Comprehensive Performance Analysis
Tuples have distinct performance characteristics:
- **Creation Speed**: Often faster than lists due to simpler, fixed-size allocation
- **Memory Usage**: More compact storage than lists (no per-item pointer array over-allocation)
- **Access Time**: Similar to lists for O(1) indexing
- **Iteration**: Can be slightly faster thanks to immutability
- **Hash Caching**: First hash computation walks the tuple; subsequent hashes are cached (implementation-dependent, e.g., CPython)
import sys
from timeit import timeit
# Memory comparison
lst = [1, 2, 3, 4, 5]
tpl = (1, 2, 3, 4, 5)
print(f"List size: {sys.getsizeof(lst)} bytes")
print(f"Tuple size: {sys.getsizeof(tpl)} bytes")
# Creation time (results vary by machine)
print("\nCreation time:")
print("List:", timeit('[1,2,3,4,5]', number=1_000_000))
print("Tuple:", timeit('(1,2,3,4,5)', number=1_000_000))
# Access time
setup = "data = list(range(100_000)); tdata = tuple(data)"
print("\nAccess time:")
print("List:", timeit('data[50_000]', setup=setup, number=1_000_0))
print("Tuple:", timeit('tdata[50_000]', setup=setup, number=1_000_0))
List size: 96 bytes Tuple size: 80 bytes Creation time: List: 0.12 sec Tuple: 0.04 sec Access time: List: 0.00002 sec Tuple: 0.00002 sec
Advanced Performance Patterns
Optimizing tuple usage for performance:
- **Pre-allocation**: Build tuples once and reuse instead of modifying (tuples are immutable)
- **Batch Processing**: Convert slices/chunks to tuples for stable, immutable processing
- **Memoization**: Use tuples as hashable keys for caching results
- **Struct-like Objects**: Prefer lightweight, immutable records when mutation isn’t required (e.g., namedtuple)
- **Interning/Reusing Constants**: Reuse common tuple constants like `()` to avoid repeat allocations
# Memoization example
from functools import lru_cache
from collections import namedtuple
@lru_cache(maxsize=None)
def factorial(n):
return 1 if n <= 1 else n * factorial(n-1)
# Tuples as cache keys
data = (5, 10, 15)
print(f"Factorials: {tuple(factorial(n) for n in data)}")
# Struct pattern with namedtuple
Person = namedtuple('Person', ['name', 'age'])
people = [Person('Alice', 30), Person('Bob', 25)]
print(f"Oldest: {max(people, key=lambda p: p.age).name}")
Factorials: (120, 3628800, 1307674368000) Oldest: Alice
Memory Optimization Techniques
Technique | Implementation | Benefit |
---|---|---|
Tuple Packing | Use tuples instead of lists for static data | Reduces memory usage |
Interning | Reuse common tuples like () or (None,) | Minimizes allocations |
Slotted Classes | __slots__ to store fixed attributes | Memory-efficient objects |
Batch Processing | Process immutable chunks | Reduces creation overhead |
Flyweight Pattern | Share immutable tuple instances | Minimizes duplicate data |
Real-world Performance Cases
Where tuple performance matters most:
- **Data Science**: Large numerical datasets (immutable coordinate pairs, keys)
- **Game Development**: Immutable game states/snapshots
- **Network Programming**: Packet header structures as fixed records
- **Compiler Design**: AST node representation
- **Database Systems**: Row-like records and indexing keys
# Data science-style example
import numpy as np
# Tuple-based coordinates (immutable)
points = tuple((x, y) for x in np.linspace(0, 1, 100)
for y in np.linspace(0, 1, 100))
# Processing with tuples
distances = tuple((x**2 + y**2)**0.5 for x, y in points)
print(f"Computed {len(distances)} distances")
Computed 10000 distances