Sobes.tech
Middle

What to do if the tested function takes a lot of time on repetitive operations inside it?

sobes.tech AI

Answer from AI

Use caching of function execution results.

The most common approaches in Python:

  • @functools.lru_cache: Built-in decorator for caching function results based on recent usage (Least Recently Used). Suitable for caching results of deterministic functions that depend on input arguments.

    # lru_cache decorator
    import functools
    
    @functools.lru_cache(maxsize=128)
    def expensive_function(n):
        # Simulate a costly operation
        import time
        time.sleep(1)
        return n * 2
    
    # First call (slow)
    result1 = expensive_function(5)
    print(f"First call with 5: {result1}")
    
    # Second call (fast, from cache)
    result2 = expensive_function(5)
    print(f"Second call with 5: {result2}")
    
  • Manual cache (dictionary): Suitable when more fine-grained control over caching is needed or when the lru_cache decorator is not applicable (e.g., for object methods or when function arguments are not hashable).

    # Manual cache
    cache = {}
    
    def another_expensive_function(n):
        if n in cache:
            return cache[n]
        else:
            # Simulate a costly operation
            import time
            time.sleep(1)
            result = n * 3
            cache[n] = result
            return result
    
    # First call (slow)
    result3 = another_expensive_function(10)
    print(f"First call with 10: {result3}")
    
    # Second call (fast, from cache)
    result4 = another_expensive_function(10)
    print(f"Second call with 10: {result4}")
    
  • External caching systems: For distributed systems or larger-scale caching, external systems such as Redis or Memcached can be used.

    # Example using Redis (pseudocode)
    import redis
    
    r = redis.Redis(host='localhost', port=6379, db=0)
    
    def redis_cached_function(arg):
        cache_key = f"cache:expensive_op:{arg}"
        cached_result = r.get(cache_key)
    
        if cached_result:
            return cached_result.decode('utf-8')  # Decode Redis bytes
        else:
            # Costly operation
            result = f"Result for {arg}"
            r.set(cache_key, result, ex=3600)  # Cache for 1 hour
            return result
    
    # Usage
    print(redis_cached_function("parameter_1"))
    print(redis_cached_function("parameter_1"))  # Retrieve from Redis
    

The choice of approach depends on specific requirements:

  • @functools.lru_cache - simplicity, automatic cache size management.
  • Manual cache - flexibility, control.
  • External systems - distributed caching, persistence.

Before implementing caching, ensure that the function is deterministic (returns the same result for the same inputs) and that the cost of computation outweighs the cost of storing and retrieving from the cache.