1

根据官方文档(https://pypi.org/project/jsonslicer/),Json Slicer 的基本配置产生 586.5K 对象/秒,ijson 与 Python 在后端产生 32.2K 对象/秒,而 ijson使用 C 后端 (ijson.yajl2_cffi) 产生 75.7Kobjects/sec。

当我在 5GB 大小的深度嵌套 JSON 文件上使用这两个库时,预计 Json Slicer 作为 YAJL ( https://lloyd.github.io/yajl/ ) 上的包装器将比使用纯 Python 的 ijson 执行得更快执行。但是,Json Slicer 花费的时间是 607.8014905452728 秒,而 ijson 花费了 308.19292974472046 秒。

根据各种来源(https://lpetr.org/2016/05/30/faster-json-parsing-python-ijson/http://explique.me/Ijson/),带有 C 后端的 ijson 应该比使用纯 Python 后端。但是,带有 C 后端的 ijson 花费的时间是 2016.68796280378929 秒。

在不同情况下对不同大小的不同 Json 文件集多次运行期间观察到此行为。

我的系统配置是带有 20GB RAM 的 Intel i7。执行脚本期间未使用多处理。

请问有人可以解释这种奇怪行为的根本原因吗?也请让我知道这个问题的解决方案。

4

2 回答 2

1

所有这些库和实现都执行某种模式匹配来查找您要检查的文件部分。然后必须对所选元素采取行动,通常是将内容提升为 Python 数据结构。

默认情况下,一旦发生初始模式匹配,ijson 就会将子文档复制到 Python dict/list 中。这会产生成本。如果你想在 Python 中操作文档的一部分(而不是提取一个标量/字符串),你仍然需要转换。使用 C 实现进行解析,这需要将 C 变量转换为 Python 变量。

在处理必须大量加载的深层数据结构时,您可能会发现在 C 中解析文件的好处与转换为 Python 的成本相比相形见绌,因此纯 Python 实现绕过了一些转换成本。

jsonslicer(对我来说)对于简单的提取任务来说非常快,但随着提取复杂性的增加,它也会遇到类似的限制。我有一个 970 MB 的未压缩 JSON 文件,其中包含一个文档列表。就我而言,从 3 级属性中进行简单的字符串选择如下:

with gzip.open('big_docs.json.gz') as file:
for thing in JsonSlicer(file, (None, 'key')):
    # print(thing)
    if thing == 'special_value':
        pass


with gzip.open('big_docs.json.gz') as file:
    for thing in ijson.items(file, 'item'):
        if thing['key'] == 'special_value':
            pass
  • ijson(使用默认 C 绑定)- 363s
  • ijson (纯 Python) - 686s
  • jsonslicer - 15s

在这个不公平的比较中,jsonslicer 能够以几乎零成本避免将字符串以外的任何内容转换为 Python,因此结果快得惊人。我天真的 ijson 使用必须做更多的工作。

在这种情况下,如果我在不改变交互的情况下更深入地挖掘两层,我会得到:

  • ijson - 350s
  • ijson(纯python) - 667s
  • jsonslicer - 16s

jsonslicer 有更多的检查速度会慢一点,而 ijson 做同样的事情,但通过对较小的文档部分进行建模来获得时间。更复杂的用途会改变平衡。证明从 C 到 Python 的转换是您看不见的代码变慢的原因是困难的,但它可能会有所贡献!

于 2020-01-22T17:24:56.420 回答
1

请注意,从 2.4 开始,ijson 有一个yajl2_c全部用 C 编写的新后端(即不使用 cffi 或 ctypes),它比其他后端快约 10 倍。从 2.5 开始,这是默认选择的,如果您的安装中存在(可能在您的情况下发生了什么,因此您看到了更快的执行)。从 2.6 开始,有一种新kvitems方法可以避免构造完整对象并迭代其成员,这在某些情况下很有用。

历史上没有yajl2_c后端,所以当人们提到“C 后端”时,实际上是yajl2_cffi后端。最重要的是,人们可能仍然认为 ijson 默认为python后端。

因此,回答您的问题:您可能正在使用yajl2_c后端运行 ijson,并且运行速度比 JsonSlicer 快。

于 2020-02-20T06:31:26.117 回答