【问题标题】:Can I speedup YAML?我可以加速 YAML 吗?
【发布时间】:2025-12-22 19:35:06
【问题描述】:

我做了一个小测试用例来比较 YAML 和 JSON 的速度:

import json
import yaml
from datetime import datetime
from random import randint

NB_ROW=1024

print 'Does yaml is using libyaml ? ',yaml.__with_libyaml__ and 'yes' or 'no'

dummy_data = [ { 'dummy_key_A_%s' % i: i, 'dummy_key_B_%s' % i: i } for i in xrange(NB_ROW) ]


with open('perf_json_yaml.yaml','w') as fh:
    t1 = datetime.now()
    yaml.safe_dump(dummy_data, fh, encoding='utf-8', default_flow_style=False)
    t2 = datetime.now()
    dty = (t2 - t1).total_seconds()
    print 'Dumping %s row into a yaml file : %s' % (NB_ROW,dty)

with open('perf_json_yaml.json','w') as fh:
    t1 = datetime.now()
    json.dump(dummy_data,fh)
    t2 = datetime.now()
    dtj = (t2 - t1).total_seconds()
    print 'Dumping %s row into a json file : %s' % (NB_ROW,dtj)

print "json is %dx faster for dumping" % (dty/dtj)

with open('perf_json_yaml.yaml') as fh:
    t1 = datetime.now()
    data = yaml.safe_load(fh)
    t2 = datetime.now()
    dty = (t2 - t1).total_seconds()
    print 'Loading %s row from a yaml file : %s' % (NB_ROW,dty)

with open('perf_json_yaml.json') as fh:
    t1 = datetime.now()
    data = json.load(fh)
    t2 = datetime.now()
    dtj = (t2 - t1).total_seconds()
    print 'Loading %s row into from json file : %s' % (NB_ROW,dtj)

print "json is %dx faster for loading" % (dty/dtj)

结果是:

Does yaml is using libyaml ?  yes
Dumping 1024 row into a yaml file : 0.251139
Dumping 1024 row into a json file : 0.007725
json is 32x faster for dumping
Loading 1024 row from a yaml file : 0.401224
Loading 1024 row into from json file : 0.001793
json is 223x faster for loading

我在 ubuntu 12.04 上使用 PyYAML 3.11 和 libyaml C 库。 我知道 json 比 yaml 简单得多,但是 json 和 yaml 之间的比率是 223 倍,我想知道我的配置是否正确。

你们的速比一样吗?
如何加快yaml.load() 的速度?

【问题讨论】:

    标签: python json yaml


    【解决方案1】:

    您可能已经注意到 Python 的数据结构语法非常类似于 JSON 的语法。

    发生了什么是 Python 的 json 库对 Python 的内置数据类型 directly into text chunks 进行编码,将 ' 替换为 " 并在这里和那里删除 ,(有点过于简单化了)。

    另一方面,pyyaml 在将其序列化为字符串之前必须construct a whole representation graph

    加载时必须向后发生相同类型的事情。

    加快yaml.load() 的唯一方法是编写一个新的Loader,但我怀疑这可能是性能上的巨大飞跃,除非你愿意编写自己的单一用途的@ 987654333@解析器,考虑the following comment

    YAML 构建图是因为它是通用的序列化 能够表示对相同的多个引用的格式 目的。如果您知道没有对象重复并且只出现基本类型, 您可以使用 json 序列化程序,它仍然是有效的 YAML。

    -- 更新

    我之前所说的仍然正确,但是如果您正在运行 Linux,则有一种方法可以加快 Yaml 解析。默认情况下,Python 的 yaml 使用 Python 解析器。你必须告诉它你想使用PyYamlC解析器。

    你可以这样做:

    import yaml
    from yaml import CLoader as Loader, CDumper as Dumper
    
    dump = yaml.dump(dummy_data, fh, encoding='utf-8', default_flow_style=False, Dumper=Dumper)
    data = yaml.load(fh, Loader=Loader)
    

    为此,您需要安装 yaml-cpp-dev(包后来重命名为 libyaml-cpp-dev),例如使用 apt-get:

    $ apt-get install yaml-cpp-dev
    

    还有PyYamlLibYaml。但根据您的输出,情况已经如此。

    我现在无法测试它,因为我运行的是 OS X,brew 在安装 yaml-cpp-dev 时遇到了一些问题,但如果你关注 PyYaml documentation,他们很清楚性能会好得多。

    【讨论】:

    • 使用 yaml 的加载速度仍然慢 12 倍。我的示例是 600,000 个空字典的列表。 Yaml 不需要做任何额外的事情,除了稍微聪明一点的语法分析,这几乎不需要额外的时间。
    • 在 mac 上:brew install yaml-cpp libyaml
    • Jivan 你是一个血腥的传奇。我打算用 C++ 重写一些 python 代码来加快速度。使用标准 yaml 加载器加载我的 6MB yaml 文件需要 53 秒,而使用 CLoader 只需 3 秒。
    • 我不知道你为什么说只有在 Linux 下运行 CLoader 加速才有意义;我刚刚在 Windows 下尝试了这个,它可以工作,给了我一个巨大的加速。
    • 如果你cannot import name 'CLoader' from 'yaml'尝试安装libyaml-dev然后重新安装pyyaml:pip --no-cache-dir install --verbose --force-reinstall -I pyyamlgithub.com/yaml/pyyaml/issues/108
    【解决方案2】:

    作为参考,我比较了几种人类可读的格式,确实 Python 的 yaml 阅读器到目前为止是最慢的。 (注意下图中的对数缩放。)如果您正在寻找速度,您需要一个 JSON 加载器,例如,orjson


    重现情节的代码:

    import numpy
    import perfplot
    
    import json
    import ujson
    import orjson
    import toml
    import yaml
    from yaml import Loader, CLoader
    import pandas
    
    
    def setup(n):
        numpy.random.seed(0)
        data = numpy.random.rand(n, 3)
    
        with open("out.yml", "w") as f:
            yaml.dump(data.tolist(), f)
    
        with open("out.json", "w") as f:
            json.dump(data.tolist(), f, indent=4)
    
        with open("out.dat", "w") as f:
            numpy.savetxt(f, data)
    
        with open("out.toml", "w") as f:
            toml.dump({"data": data.tolist()}, f)
    
    
    def yaml_python(arr):
        with open("out.yml", "r") as f:
            out = yaml.load(f, Loader=Loader)
        return out
    
    
    def yaml_c(arr):
        with open("out.yml", "r") as f:
            out = yaml.load(f, Loader=CLoader)
        return out
    
    
    def json_load(arr):
        with open("out.json", "r") as f:
            out = json.load(f)
        return out
    
    
    def ujson_load(arr):
        with open("out.json", "r") as f:
            out = ujson.load(f)
        return out
    
    
    def orjson_load(arr):
        with open("out.json", "rb") as f:
            out = orjson.loads(f.read())
        return out
    
    
    def loadtxt(arr):
        with open("out.dat", "r") as f:
            out = numpy.loadtxt(f)
        return out
    
    
    def pandas_read(arr):
        out = pandas.read_csv("out.dat", header=None, sep=" ")
        return out.values
    
    
    def toml_load(arr):
        with open("out.toml", "r") as f:
            out = toml.load(f)
        return out["data"]
    
    
    perfplot.save(
        "out.png",
        setup=setup,
        kernels=[
            yaml_python,
            yaml_c,
            json_load,
            loadtxt,
            pandas_read,
            toml_load,
            ujson_load,
            orjson_load,
        ],
        n_range=[2 ** k for k in range(18)],
    )
    

    【讨论】:

      【解决方案3】:

      是的,我还注意到 JSON 更快。所以一个合理的方法是首先将 YAML 转换为 JSON。如果您不介意 ruby​​,那么您可以获得很大的加速并完全放弃 yaml 安装:

      import commands, json
      def load_yaml_file(fn):
          ruby = "puts YAML.load_file('%s').to_json" % fn
          j = commands.getstatusoutput('ruby -ryaml -rjson -e "%s"' % ruby)
          return json.loads(j[1])
      

      这是 10 万条记录的比较:

      load_yaml_file: 0.95 s
      yaml.load: 7.53 s
      

      对于 1M 条记录:

      load_yaml_file: 11.55 s
      yaml.load: 77.08 s
      

      如果你还是坚持使用yaml.load,记得把它放在一个virtualenv中,以免与其他软件冲突。

      【讨论】:

      • 我不介意 ruby​​,但我确实介意虚假的答案。 1)您并没有真正使用 ruby​​,在您的代码中您使用的是thin layer around libyaml C library:“底层实现是 libyaml 包装器 Psych”。 2)您将其与没有 libyaml C 库的 PyYAML 进行比较。如果你有,你会看到 Python 包装 libyaml 的速度不是慢 7 倍,而是只有百分之几。 3) commands 模块的弃用公告是在 2006 年的 PEP 0361 中发布的,您仍然建议在 11 年后使用它。