2026-04-14 23:40:08 +00:00
|
|
|
"""
|
|
|
|
|
Benchmark for cache key computation in odoo/tools/cache.py
|
|
|
|
|
Focuses on the determine_key() and key() methods which are pure Python.
|
|
|
|
|
"""
|
2026-04-15 08:16:15 +00:00
|
|
|
|
2026-04-14 23:40:08 +00:00
|
|
|
import time
|
2026-04-15 08:16:15 +00:00
|
|
|
from inspect import Parameter, signature
|
|
|
|
|
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
# Simulate the key computation logic from ormcache
|
|
|
|
|
def build_cache_key_eval(method, cache_args):
|
|
|
|
|
"""Current implementation using eval - generates lambda from string"""
|
2026-04-15 08:16:15 +00:00
|
|
|
args = ", ".join(
|
2026-04-14 23:40:08 +00:00
|
|
|
str(params.replace(annotation=Parameter.empty))
|
|
|
|
|
for params in signature(method).parameters.values()
|
|
|
|
|
)
|
2026-04-15 08:16:15 +00:00
|
|
|
values = ["self._name", "method", *cache_args]
|
|
|
|
|
code = (
|
|
|
|
|
f"lambda {args}: ({''.join(a for arg in values for a in (arg, ','))})"
|
|
|
|
|
)
|
|
|
|
|
return eval(code, {"method": method})
|
|
|
|
|
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
# Test methods with various signatures
|
|
|
|
|
class MockModel:
|
2026-04-15 08:16:15 +00:00
|
|
|
_name = "test.model"
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
def simple_method(self, arg1, arg2):
|
|
|
|
|
return arg1 + arg2
|
|
|
|
|
|
|
|
|
|
def complex_method(self, model_name, mode="read", limit=None, offset=0):
|
|
|
|
|
return f"{model_name}:{mode}:{limit}:{offset}"
|
|
|
|
|
|
|
|
|
|
def many_args_method(self, a, b, c, d, e, f, g, h):
|
2026-04-15 08:16:15 +00:00
|
|
|
return sum([a, b, c, d, e, f, g, h])
|
|
|
|
|
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
def benchmark_key_computation():
|
|
|
|
|
"""Benchmark the key computation overhead"""
|
|
|
|
|
model = MockModel()
|
|
|
|
|
|
|
|
|
|
print("=== Cache Key Computation Benchmark ===\n")
|
|
|
|
|
|
|
|
|
|
# Scenario 1: Simple method, many lookups
|
|
|
|
|
print("Scenario 1: Simple method (2 args), 100k lookups")
|
2026-04-15 08:16:15 +00:00
|
|
|
key_func = build_cache_key_eval(MockModel.simple_method, ["arg1", "arg2"])
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
for i in range(100_000):
|
2026-04-15 08:16:15 +00:00
|
|
|
_ = key_func(model, i, i * 2)
|
2026-04-14 23:40:08 +00:00
|
|
|
elapsed = time.perf_counter() - start
|
|
|
|
|
print(f" Total: {elapsed:.3f}s")
|
2026-04-15 08:16:15 +00:00
|
|
|
print(f" Per call: {elapsed / 100_000 * 1e6:.2f} µs\n")
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
# Scenario 2: Complex method with defaults, many lookups
|
|
|
|
|
print("Scenario 2: Complex method (4 args with defaults), 100k lookups")
|
|
|
|
|
key_func = build_cache_key_eval(
|
2026-04-15 08:16:15 +00:00
|
|
|
MockModel.complex_method, ["model_name", "mode", "limit", "offset"]
|
2026-04-14 23:40:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
for i in range(100_000):
|
2026-04-15 08:16:15 +00:00
|
|
|
_ = key_func(model, "res.partner", "read", 10, i)
|
2026-04-14 23:40:08 +00:00
|
|
|
elapsed = time.perf_counter() - start
|
|
|
|
|
print(f" Total: {elapsed:.3f}s")
|
2026-04-15 08:16:15 +00:00
|
|
|
print(f" Per call: {elapsed / 100_000 * 1e6:.2f} µs\n")
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
# Scenario 3: Many args method
|
|
|
|
|
print("Scenario 3: Many args method (8 args), 100k lookups")
|
|
|
|
|
key_func = build_cache_key_eval(
|
2026-04-15 08:16:15 +00:00
|
|
|
MockModel.many_args_method, ["a", "b", "c", "d", "e", "f", "g", "h"]
|
2026-04-14 23:40:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
for i in range(100_000):
|
|
|
|
|
_ = key_func(model, 1, 2, 3, 4, 5, 6, 7, 8)
|
|
|
|
|
elapsed = time.perf_counter() - start
|
|
|
|
|
print(f" Total: {elapsed:.3f}s")
|
2026-04-15 08:16:15 +00:00
|
|
|
print(f" Per call: {elapsed / 100_000 * 1e6:.2f} µs\n")
|
2026-04-14 23:40:08 +00:00
|
|
|
|
|
|
|
|
# Scenario 4: Key function creation overhead
|
|
|
|
|
print("Scenario 4: Key function creation (determine_key), 10k methods")
|
|
|
|
|
start = time.perf_counter()
|
|
|
|
|
for i in range(10_000):
|
2026-04-15 08:16:15 +00:00
|
|
|
_ = build_cache_key_eval(MockModel.simple_method, ["arg1", "arg2"])
|
2026-04-14 23:40:08 +00:00
|
|
|
elapsed = time.perf_counter() - start
|
|
|
|
|
print(f" Total: {elapsed:.3f}s")
|
2026-04-15 08:16:15 +00:00
|
|
|
print(f" Per call: {elapsed / 10_000 * 1e6:.2f} µs\n")
|
|
|
|
|
|
2026-04-14 23:40:08 +00:00
|
|
|
|
2026-04-15 08:16:15 +00:00
|
|
|
if __name__ == "__main__":
|
2026-04-14 23:40:08 +00:00
|
|
|
benchmark_key_computation()
|