我正在开发一个具有非常积极的缓存的网络应用程序。几乎 Web 应用程序的每个组件: View 、部分 View 、 Controller 输出、磁盘加载、REST-API 调用、数据库查询。所有可以缓存的东西,在任何级别,都被缓存,都使用装饰器。
当然,这非常快,因为绝大多数 HTML 生成都包含纯函数,很少有来自磁盘/REST API 的负载。此外,我执行的少数磁盘加载/数据库查询/REST API 查询也会被缓存直到失效,所以除非有什么只是改变,它们也非常快。
所以一切都非常快,但有一个障碍:所有这些东西都被缓存在内存中,在我的 WSGI 进程中的一个巨大的全局字典中,因此可以直接存储而无需序列化。一旦我开始将内容放入 memcached 中,缓存命中所花费的时间就不会改变太多,但是将内容放入缓存中开始花费更长的时间。一般来说,这没问题,但每个页面的初始“填充缓存”生成时间从 ~900 毫秒(考虑到它从磁盘读取的平面文件数量,这已经相当快了)到大约 9000 毫秒。作为引用,一旦缓存预热,生成任意页面大约需要 10 毫秒。
剖析代码,绝大多数时间都在使用 cPickle。所以问题是,我怎样才能让它更快?是否有任何内存缓存,我可以直接将我的对象传递给而无需序列化?或者有什么方法可以更快地缓存我的大量对象?我可以不使用持久性内存缓存,但我的性能(或性能不足)将取决于 Apache/WSGI 进程管理器。
最佳答案
如果您正在序列化 Python 对象而不是简单的数据类型,并且必须使用 pickle,请尝试 cPickle.HIGHEST_PROTOCOL:
my_serialized_object = cPickle.dumps(my_object, cPickle.HIGHEST_PROTOCOL)
默认协议(protocol)与旧版本的 Python 兼容,但您可能不关心这一点。
我刚刚用 1000 个键的字典做了一个简单的基准测试,它几乎快了一个数量级。
更新:由于您似乎已经在使用最高协议(protocol),因此您将不得不做一些额外的工作以获得更高的性能。这是我此时会做的事情:
确定哪些类 pickle 最慢
在类中创建一对方法来实现更快的序列化方法,比如 _to_string() 和 _from_string(s)。实际的序列化可以根据对象包含的内容以及它将如何使用来定制。例如,一些对象可能真的只包含一个简单的字符串,例如呈现的模板,而一些实际上可能作为 JSON 发送到浏览器,在这种情况下,您可以简单地序列化为 JSON 并直接提供它。使用 timeit module以确保您的方法实际上更快
在你的装饰器中,检查 hasattr(object, '_to_string') 并使用它,如果它存在的话
此方法可让您首先处理最差的类,并将对代码库的干扰降到最低。
关于Python:防止缓存减慢我的速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7563141/