¿Puedo acelerar YAML?

Hice un pequeño caso de prueba para comparar la velocidad de YAML y 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) 

Y el resultado es:

 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 

Estoy usando PyYAML 3.11 con la biblioteca libyaml C en ubuntu 12.04. Sé que json es mucho más simple que yaml, pero con una relación de 223x entre json y yaml, me pregunto si mi configuración es correcta o no.

¿Tienes la misma relación de velocidad?
¿Cómo puedo acelerar yaml.load() ?

Probablemente haya notado que la syntax de Python para estructuras de datos es muy similar a la syntax de JSON.

Lo que sucede es que la biblioteca json Python codifica los tipos de datos incorporados de Python directamente en fragmentos de texto , sustituyendo ' into " y eliminando , aquí y allá (para simplificar un poco).

Por otro lado, pyyaml tiene que construir un gráfico de representación completo antes de serializarlo en una cadena.

El mismo tipo de cosas tiene que suceder hacia atrás al cargar.

La única forma de yaml.load() sería escribir un nuevo Loader , pero dudo que pueda ser un gran salto en el rendimiento, excepto si está dispuesto a escribir su propio analizador YAML de tipo único propósito, tomando el siguiente comentario en consideración:

YAML crea un gráfico porque es un formato de serialización de propósito general que puede representar múltiples referencias al mismo objeto. Si sabe que no se repite ningún objeto y solo aparecen los tipos básicos, puede usar un serializador json, seguirá siendo un YAML válido.

ACTUALIZACIÓN

Lo que dije antes sigue siendo cierto, pero si está ejecutando Linux hay una manera de acelerar el análisis de Yaml . De forma predeterminada, el yaml de Python usa el analizador de Python. Tienes que decirle que quieres usar el analizador PyYaml C

Puedes hacerlo de esta manera:

 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) 

Para hacerlo, necesita que se yaml-cpp-dev (paquete que luego se renombró a libyaml-cpp-dev ), por ejemplo con apt-get:

 $ apt-get install yaml-cpp-dev 

Y PyYaml con LibYaml también. Pero ese ya es el caso basado en su salida.

No puedo probarlo ahora porque estoy ejecutando OS X y brew tiene algunos problemas para instalar yaml-cpp-dev pero si sigues la documentación de PyYaml , es bastante claro que el rendimiento será mucho mejor.

Para referencia, comparé un par de formatos legibles por humanos y, de hecho, el lector de yaml de Python es, con mucho, el más lento. (Tenga en cuenta la escala de registro en el gráfico a continuación). Si está buscando velocidad, desea el lector JSON incorporado de Python :

introduzca la descripción de la imagen aquí


Código para reproducir la ttwig:

 import numpy import perfplot import json import yaml from yaml import Loader, CLoader import pandas def setup(n): 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) return 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_read(arr): with open('out.json', 'r') as f: out = json.load(f) 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 perfplot.show( setup=setup, kernels=[ yaml_python, yaml_c, json_read, loadtxt, pandas_read ], n_range=[2**k for k in range(18)], logx=True, logy=True, ) 

Sí, también noté que JSON es mucho más rápido. Así que un enfoque razonable sería convertir YAML a JSON primero. Si no te importa Ruby, puedes obtener una gran aceleración y yaml instalación de 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]) 

Aquí está una comparación para los registros de 100K:

 load_yaml_file: 0.95 s yaml.load: 7.53 s 

Y para 1M registros:

 load_yaml_file: 11.55 s yaml.load: 77.08 s 

Si insiste en usar yaml.load de todos modos, recuerde ponerlo en un virtualenv para evitar conflictos con otro software.