应用-为 Python 选择更快的 JSON 库

目录

为 Python 选择更快的 JSON 库

你使用 JSON 越多,就越有可能遇到 JSON 编码或解码成为性能瓶颈的情况。Python 的内置库并不差,但有多个更快的 JSON 库可供选择:你该如何选择使用哪一个呢?

事实上,并没有一个放之四海而皆准的答案,也没有一个最快的 JSON 库能够适用于所有场景:

  1. 对于不同的人来说,“快速的 JSON 库”意味着不同的东西,因为他们的使用模式不同。
  2. 速度并不是一切——你可能还关心其他方面,比如安全性和可定制性。

因此,为了帮助你选择最适合需求的快速 JSON 库,我想分享一下我为 Python 选择快速 JSON 库的过程。你可以使用这个过程来选择最适合你特定需求的库:

  1. 确保确实存在问题。
  2. 定义基准测试。
  3. 根据额外需求进行筛选。
  4. 对剩余的候选库进行基准测试。
第一步:你真的需要一个新的 JSON 库吗?

仅仅因为你使用了 JSON,并不意味着它就是一个相关的性能瓶颈。在你花时间思考哪个JSON 库之前,你需要一些证据表明 Python 的内置 JSON 库确实在你的特定应用中存在问题。

我了解到,JSON 编码占用了生成消息时 CPU 时间的约 25%。我能获得的最大加速是运行速度提高 33%(如果 JSON 编码时间降为零),但这是一个相当大的时间块,迟早会排到优化列表的顶部。

第二步:定义基准测试

如果你查看各种 JSON 库的基准测试页面,它们会讨论它们在不同消息上的表现。这些消息并不一定与你的使用情况相对应。通常情况下,它们测量的是非常大的消息,而在我的案例中,我关心的是小消息。

因此,你需要提出一些符合你特定使用模式的测量标准:

  1. 你关心编码、解码,还是两者都关心?
  2. 你使用的是小消息还是大消息?
  3. 典型的消息是什么样的?

在我的案例中,我主要关心编码小消息,特别是由 Eliot 生成的日志消息的结构。我根据一些真实的日志提出了以下示例消息:

{
    "timestamp": 1556283673.1523004,
    "task_uuid": "0ed1a1c3-050c-4fb9-9426-a7e72d0acfc7",
    "task_level": [1, 2, 1],
    "action_status": "started",
    "action_type": "main",
    "key": "value",
    "another_key": 123,
    "and_another": ["a", "b"],
}
第三步:根据额外需求进行筛选

性能并不是一切——你可能还关心其他方面。在我的案例中:

  1. 安全性/抗崩溃性:日志消息可能包含来自不受信任来源的数据。如果 JSON 编码器在遇到错误数据时崩溃,这对可靠性和安全性都不好。
  2. 自定义编码:Eliot 支持 JSON 编码的自定义,因此你可以序列化其他类型的 Python 对象。一些 JSON 库支持这一点,而另一些则不支持。
  3. 跨平台:能够在 Linux、macOS 和 Windows 上运行。
  4. 维护性:我不想依赖一个没有积极支持的库。

我考虑的库包括 orjsonrapidjsonujsonhyperjson

根据上述标准,我筛选掉了一些库:

  • 在我最初撰写这篇文章时,ujson 有许多关于崩溃的 bug 报告,并且自 2016 年以来没有发布新版本。虽然现在看起来它又开始维护了,但我还没有重新审视它。
  • hyperjson 只有 macOS 的包,而且总体上看起来非常不成熟。现在他们只推荐使用 orjson
第四步:基准测试

最终的两个候选者是 rapidjsonorjson。我运行了以下基准测试:

import time
import json
import orjson
import rapidjson

m = {
    "timestamp": 1556283673.1523004,
    "task_uuid": "0ed1a1c3-050c-4fb9-9426-a7e72d0acfc7",
    "task_level": [1, 2, 1],
    "action_status": "started",
    "action_type": "main",
    "key": "value",
    "another_key": 123,
    "and_another": ["a", "b"],
}

def benchmark(name, dumps):
    start = time.time()
    for i in range(1000000):
        dumps(m)
    print(name, time.time() - start)

benchmark("Python", json.dumps)
# orjson 只输出字节,但我们通常需要 Unicode:
benchmark("orjson", lambda s: str(orjson.dumps(s), "utf-8"))
benchmark("rapidjson", rapidjson.dumps)

结果如下:

$ python jsonperf.py 
Python 4.829106330871582
orjson 1.0466396808624268
rapidjson 2.1441543102264404

即使需要额外的 Unicode 解码,orjson 仍然是最快的(在这个特定的基准测试中!)。

当然,总有一些权衡。orjson 的用户比 rapidjson 少(比较 orjson PyPI 统计数据rapidjson PyPI 统计数据),而且没有 Conda 包,所以我必须自己为 Conda-forge 打包。但它确实快得多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李星星BruceL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值