Могу ли я ускорить YAML?

Я сделал небольшой тестовый пример для сравнения скорости 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

Я использую PyYAML 3.11 с библиотекой libyaml C на ubuntu 12.04. Я знаю, что json намного проще, чем yaml, но с коэффициентом 223x между json и yaml. Мне интересно, правильна ли моя конфигурация или нет.

У вас есть такое же соотношение скоростей?
Как ускорить yaml.load()?

Ответ 1

Вы, наверное, заметили, что синтаксис Python для структур данных очень похож на синтаксис JSON.

Что происходит, библиотека Python json кодирует встроенные типы данных Python непосредственно в текстовые фрагменты, заменяя ' на " и удаляя , здесь и там (чтобы упростить бит).

С другой стороны, pyyaml должен построить весь граф представления перед его сериализацией в строку.

То же самое происходит при загрузке в обратном направлении.

Единственным способом ускорения yaml.load() было бы написать новый Loader, но я сомневаюсь, что это может быть огромным скачком в производительности, за исключением случаев, когда вы готовы написать свой собственный одноцелевой вид YAML, принимая следующий комментарий:

YAML создает граф, потому что это сериализация общего назначения формат, который может представлять несколько ссылок на один и тот же объект. Если вы знаете, что объект не повторяется и появляются только базовые типы, вы можете использовать сериализатор json, он все равно будет действительным YAML.

- ОБНОВЛЕНИЕ

То, что я сказал ранее, остается верным, но если вы используете Linux, способ ускорить разбор YAML. По умолчанию Python YAML использует парсер Python. Вы должны сказать, что хотите использовать парсер pyyaml C.

Вы можете сделать это следующим образом:

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, например, с помощью apt-get:

$ apt-get install yaml-cpp-dev

И pyyaml с помощью LibYaml. Но это уже случай, основанный на вашем выходе.

Я не могу проверить его прямо сейчас, потому что я запускаю OS X и brew имеет некоторые проблемы с установкой yaml-cpp-dev, но если вы следуете PyYaml документация, они довольно понятны, что производительность будет намного лучше.

Ответ 2

Да, я также заметил, что JSON работает быстрее. Поэтому разумным подходом было бы преобразование YAML в JSON в первую очередь. Если вы не возражаете против рубина, вы можете получить большое ускорение и полностью остановить установку 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])

Вот сравнение для 100K записей:

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, чтобы избежать конфликтов с другим программным обеспечением.