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