file_cache: 使用文件缓存函数结果

简介: file_cache: 使用文件缓存函数结果

file_cache 使用文件缓存函数结果

更好的 Python 缓存,用于慢速函数调用

原文:https://docs.sweep.dev/blogs/file-cache

作者编写了一个文件缓存 - 它类似于 Python 的lru_cache ,但它将值存储在文件中而不是内存中。

这是链接:https://github.com/sweepai/sweep/blob/main/docs/public/file_cache.py

想使用它,只需将其作为装饰器添加到你的函数中, 例如:

import time
from file_cache import file_cache
 
@file_cache()
def slow_function(x, y):
    time.sleep(30)
    return x + y
 
print(slow_function(1, 2)) # -> 3, takes 30 seconds
print(slow_function(1, 2)) # -> 3, takes 0 seconds

背景

作者在一个LLM项目中需要缓存中间结果,便于节省时间。

但内置缓存函数lru_cache 不适合,

  • lru_cahce将结果保存在内存中,下次运行程序时缓存失效。
  • 并且作者添加了一个ignore_params参数,用于忽略不重要的参数,例如file_cache(ignore_params=["chat_logger"])

实现

我们的两个主要方法是 recursive_hashfile_cache

recursive_hash

我们希望稳定地对对象进行哈希处理,而这在 python 中是原生不支持的。

import hashlib
from cache import recursive_hash
 
 
class Obj:
    def __init__(self, name):
        self.name = name
 
obj = Obj("test")
print(recursive_hash(obj)) # -> this works fine
try:
    hashlib.md5(obj).hexdigest()
except Exception as e:
    print(e) # -> this doesn't work

原始的 hashlib.md5 不适用于任意对象,它会报错: TypeError: object supporting the buffer API required 。我们使用 recursive_hash,它适用于任意 python 对象。

def recursive_hash(value, depth=0, ignore_params=[]):
    """Hash primitives recursively with maximum depth."""
    if depth > MAX_DEPTH:
        return hashlib.md5("max_depth_reached".encode()).hexdigest()
 
    if isinstance(value, (int, float, str, bool, bytes)):
        return hashlib.md5(str(value).encode()).hexdigest()
    elif isinstance(value, (list, tuple)):
        return hashlib.md5(
            "".join(
                [recursive_hash(item, depth + 1, ignore_params) for item in value]
            ).encode()
        ).hexdigest()
    elif isinstance(value, dict):
        return hashlib.md5(
            "".join(
                [
                    recursive_hash(key, depth + 1, ignore_params)
                    + recursive_hash(val, depth + 1, ignore_params)
                    for key, val in value.items()
                    if key not in ignore_params
                ]
            ).encode()
        ).hexdigest()
    elif hasattr(value, "__dict__") and value.__class__.__name__ not in ignore_params:
        return recursive_hash(value.__dict__, depth + 1, ignore_params)
    else:
        return hashlib.md5("unknown".encode()).hexdigest()


file_cache

file_cache 是一个处理缓存逻辑的装饰器。

@file_cache()
def search_codebase(
    cloned_github_repo,
    query,
):
    # ... take a long time ...
    # ... llm agent logic to search through the codebase ...
    return top_results

Wrapper

首先,我们将缓存存储在 /tmp/file_cache .这使我们可以通过简单地删除目录(运行 rm -rf /tmp/file_cache )来删除缓存。

def wrapper(*args, **kwargs):
    cache_dir = "/tmp/file_cache"
    os.makedirs(cache_dir, exist_ok=True)

Cache Key Creation / Miss Conditions

我们还有另一个问题 - 我们希望在两种情况下不使用缓存:

  1. 函数参数更改 - 由 recursive_hash处理
  2. 代码更改
  1. 为了处理 2.我们使用 inspect.getsource(func) 将函数的源代码进行哈希,在代码更改时正确地丢失了缓存。
    func_source_code_hash = hash_code(inspect.getsource(func))
    args_names = func.__code__.co_varnames[: func.__code__.co_argcount]
    args_dict = dict(zip(args_names, args))
 
    # Remove ignored params
    kwargs_clone = kwargs.copy()
    for param in ignore_params:
        args_dict.pop(param, None)
        kwargs_clone.pop(param, None)
 
    # Create hash based on argument names, argument values, and function source code
    arg_hash = (
        recursive_hash(args_dict, ignore_params=ignore_params)
        + recursive_hash(kwargs_clone, ignore_params=ignore_params)
        + func_source_code_hash
    )
    cache_file = os.path.join(
        cache_dir, f"{func.__module__}_{func.__name__}_{arg_hash}.pickle"
    )


Cache hits and misses

最后,我们检查缓存键是否存在,并在缓存未命中的情况下写入缓存。

    try:
        # If cache exists, load and return it
        if os.path.exists(cache_file):
            if verbose:
                print("Used cache for function: " + func.__name__)
            with open(cache_file, "rb") as f:
                return pickle.load(f)
    except Exception:
        logger.info("Unpickling failed")
 
    # Otherwise, call the function and save its result to the cache
    result = func(*args, **kwargs)
    try:
        with open(cache_file, "wb") as f:
            pickle.dump(result, f)
    except Exception as e:
        logger.info(f"Pickling failed: {e}")
    return result

相关文章
|
6月前
|
存储 缓存 NoSQL
除了`functools.lru_cache`装饰器,还有哪些方法可以缓存函数的结果?
除了`functools.lru_cache`装饰器,还有哪些方法可以缓存函数的结果?
43 1
|
1月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
30 3
|
3月前
|
缓存 NoSQL Linux
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
131 1
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
|
3月前
|
缓存 JavaScript
Vue学习之--------编程式路由导航、缓存路由组件、新的钩子函数(4)(2022/9/5)
这篇文章介绍了Vue中编程式路由导航的方法,包括使用`$router.push`、`$router.replace`、`$router.forward`、`$router.back`和`$router.go`进行路由跳转和历史记录操作,以及如何利用`<keep-alive>`组件缓存路由组件,和Vue Router新增的两个生命周期钩子`activated`和`deactivated`的用法及其在项目中的应用和测试结果。
Vue学习之--------编程式路由导航、缓存路由组件、新的钩子函数(4)(2022/9/5)
|
3月前
|
存储 缓存 NoSQL
【Azure Redis 缓存 Azure Cache For Redis】如何设置让Azure Redis中的RDB文件暂留更久(如7天)
【Azure Redis 缓存 Azure Cache For Redis】如何设置让Azure Redis中的RDB文件暂留更久(如7天)
|
3月前
|
缓存 NoSQL Redis
【Azure Redis 缓存】Azure Cache for Redis 服务的导出RDB文件无法在自建的Redis服务中导入
【Azure Redis 缓存】Azure Cache for Redis 服务的导出RDB文件无法在自建的Redis服务中导入
|
3月前
|
缓存 NoSQL 算法
【Azure Redis 缓存】Redis导出数据文件变小 / 在新的Redis复原后数据大小压缩近一倍问题分析
【Azure Redis 缓存】Redis导出数据文件变小 / 在新的Redis复原后数据大小压缩近一倍问题分析
|
3月前
|
SQL 缓存 监控
实时计算 Flink版产品使用问题之怎么手动清理缓存或废弃文件
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
4月前
|
缓存
react18【系列实用教程】useCallback —— 缓存函数 (2024最新版)
react18【系列实用教程】useCallback —— 缓存函数 (2024最新版)
48 0
|
5月前
|
缓存 运维 Devops
阿里云云效操作报错合集之在构建过程中,Docker尝试从缓存中获取某个文件(或计算缓存键)时遇到了问题,该如何处理
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。