นากกายจัดกล่องแคชสองใบบนโต๊ะมิ้นต์ ใบหนึ่งไม่จำกัดขนาด ใบหนึ่งมีช่อง LRU สีอำพัน

functools.cache กับ lru_cache: เมื่อไหร่ควรจำกัดขนาดแคช

น้องกายEngineering

เคยคำนวณค่าเดิมซ้ำในฟังก์ชันบริสุทธิ์ไหม ผมเคย จนหันมาใช้แคชใน functools

ตามเอกสาร functools มีสองตัวที่คนสับสนบ่อย:

  • @functools.cache — จำผลไม่จำกัดขนาด (เทียบเท่า @lru_cache(maxsize=None))
  • @functools.lru_cache(maxsize=…) — จำผลแบบ LRU จำกัดจำนวนรายการ

เลือกยังไง

  • อาร์กิวเมนต์ที่เป็นไปได้น้อยและคงที่ → @cache สั้นๆ ชัด
  • อาร์กิวเมนต์หลากหลายหรือมาจากอินพุตผู้ใช้ → @lru_cache(maxsize=…) กันหน่วยความจำโตไม่รู้จบ
  • ฟังก์ชันต้องมีอาร์กิวเมนต์ hashable ทั้งคู่
from functools import cache, lru_cache

calls = {"cache": 0, "lru": 0}

@cache
def fib_cache(n):
    calls["cache"] += 1
    return n if n < 2 else fib_cache(n - 1) + fib_cache(n - 2)

@lru_cache(maxsize=4)
def fib_lru(n):
    calls["lru"] += 1
    return n if n < 2 else fib_lru(n - 1) + fib_lru(n - 2)

assert fib_cache(10) == 55
assert fib_lru(10) == 55
assert calls["cache"] == 11  # n=0..10 once each
info = fib_lru.cache_info()
assert info.maxsize == 4
assert info.hits + info.misses >= 1
print("ok", calls["cache"], info.hits, info.misses)

Recap

  1. @cache = ไม่จำกัดขนาด ใช้เมื่อคีย์น้อย
  2. @lru_cache(maxsize=…) เมื่อกลัวหน่วยความจำโต
  3. อาร์กิวเมนต์ต้อง hashable
  4. ดู cache_info() ตอนไล่ประสิทธิภาพ