왜 파이썬의 yaml 시리얼라이제이션보다 json 시리얼라이제이션이 훨씬 빠를까요?
언어간 시리얼화를 위해 yaml에 크게 의존하는 코드를 가지고 있는데, 몇 가지 작업을 진행하면서 yaml이 다른 시리얼화 방법(피클, json 등)에 비해 엄청나게 느리다는 것을 알게 되었습니다.
그래서 정말 놀라운 것은 출력이 거의 동일할 때 json이 yaml보다 훨씬 더 빠르다는 것입니다.
>>> import yaml, cjson; d={'foo': {'bar': 1}}
>>> yaml.dump(d, Dumper=yaml.SafeDumper)
'foo: {bar: 1}\n'
>>> cjson.encode(d)
'{"foo": {"bar": 1}}'
>>> import yaml, cjson;
>>> timeit("yaml.dump(d, Dumper=yaml.SafeDumper)", setup="import yaml; d={'foo': {'bar': 1}}", number=10000)
44.506911039352417
>>> timeit("yaml.dump(d, Dumper=yaml.CSafeDumper)", setup="import yaml; d={'foo': {'bar': 1}}", number=10000)
16.852826118469238
>>> timeit("cjson.encode(d)", setup="import cjson; d={'foo': {'bar': 1}}", number=10000)
0.073784112930297852
PyYaml의 CSAFeDumper와 cjson은 모두 C로 표기되어 있기 때문에 C와 Python의 속도 문제가 아닙니다.cjson이 캐싱을 하고 있는지 확인하기 위해 랜덤 데이터까지 추가했지만 여전히 PyYaml보다 훨씬 빠릅니다.yaml이 json의 슈퍼셋인 것은 알고 있습니다만, 이렇게 간단한 입력으로 yaml serializer가 어떻게 2배 느릴 수 있습니까?
일반적으로 해석 속도를 결정하는 것은 출력의 복잡성이 아니라 허용되는 입력의 복잡성입니다.JSON 문법은 매우 간결합니다.YAML 파서는 비교적 복잡하기 때문에 오버헤드가 증가합니다.
JSON의 가장 중요한 디자인 목표는 단순성과 보편성입니다.따라서 JSON은 인간의 가독성 저하를 감수하면서 생성 및 해석하는 것이 간단하다.또한 최소 공통 분모 정보 모델을 사용하여 모든 최신 프로그래밍 환경에서 JSON 데이터를 쉽게 처리할 수 있습니다.
반면 YAML의 가장 중요한 설계 목표는 인간의 가독성과 임의의 네이티브 데이터 구조의 시리얼화를 지원하는 것입니다.따라서 YAML은 매우 읽기 쉬운 파일을 만들 수 있지만 생성과 해석은 더 복잡합니다.또한 YAML은 최소 공통분모 데이터 유형을 넘어서기 때문에 서로 다른 프로그래밍 환경을 교차할 때 보다 복잡한 처리를 필요로 합니다.
저는 YAML 파서 실장자가 아니기 때문에 프로파일링 데이터와 많은 예제가 없으면 그 규모에 대해 구체적으로 말할 수 없습니다.어떤 경우에도 벤치마크 수치를 확신하기 전에 많은 입력 정보를 테스트해야 합니다.
업데이트 후, 질문을 잘못 읽었습니다. :- (시리얼라이제이션은 큰 입력 문법에 관계없이 매우 빠를 수 있지만 소스를 참조하면 Py처럼 보입니다.YAML의 Python 레벨의 시리얼화는 표현 그래프를 구성하는 반면, Simplejson은 내장된 Python 데이터형을 텍스트 청크로 직접 인코딩합니다.
지금까지 작업한 어플리케이션에서는 문자열에서 숫자로의 유형 추론(플로트/int)은 yaml을 해석하는 데 가장 큰 오버헤드가 있는 부분입니다.따옴표 없이 문자열을 쓸 수 있기 때문입니다.json의 모든 문자열은 따옴표로 둘러싸여 있기 때문에 문자열을 해석할 때 역추적이 발생하지 않습니다.이것이 느려지는 좋은 예로는 00000000000000000s 값을 들 수 있습니다.이 값은 끝까지 읽을 때까지 문자열임을 알 수 없습니다.
다른 답변은 맞지만, 이것은 제가 실제로 발견한 구체적인 세부 사항입니다.
효율에 대해 말하자면, 저는 한동안 YAML을 사용했는데, 이 언어에서 이름이나 가치 할당이 차지하는 단순함에 매력을 느꼈습니다.그러나 그 과정에서 YAML의 핀스 중 하나, 문법의 미묘한 변형, 특별한 케이스를 보다 간결하게 쓸 수 있는 것 등에 대해 몇 번이나 실수를 했습니다.결국 YAML의 문법은 형식적으로 거의 일치하지만, 어느 정도 애매한 느낌이 들었습니다.그 후, 기존의 동작하고 있는 YAML 코드에 손을 대지 않고, 모든 것을 보다 우회적이고 안전한 구문으로 기술하는 것을 제한했습니다.그 결과, YAML은 W3C 표준처럼 보이려고 하고, 그 개념과 규칙에 관한 읽기 어려운 작은 문헌 라이브러리를 작성하게 되었습니다.
나는 이것이 필요 이상으로 지적 오버헤드라고 생각한다.SGML/XML을 보십시오. IBM이 60년대에 개발했으며 ISO에 의해 표준화되었습니다. (다운다운되고 수정된 형태로) 수많은 사람들에게 HTML로 알려져 있으며, 전 세계적으로 문서화되고 문서화되어 다시 문서화되어 문서화되어 있습니다.꼬마 JSON이 다가와 용을 죽인다.JSON은 어떻게 이렇게 짧은 시간에 널리 쓰이게 되었을까? 단 하나의 빈약한 웹사이트(그리고 그것을 뒷받침하는 자바스크립트 루미너리)만으로?그것은 단순함, 문법에 대한 의심의 여지가 전혀 없는 것, 배우고 사용하기 쉽다는 점이다.
XML과 YAML은 인간에게는 어렵고 컴퓨터에게는 어렵다.JSON은 인간과 컴퓨터 모두에게 매우 친절하고 쉽다.
python-yaml을 대충 살펴보면 디자인이 cjson보다 훨씬 복잡하다는 것을 알 수 있습니다.
>>> dir(cjson)
['DecodeError', 'EncodeError', 'Error', '__doc__', '__file__', '__name__', '__package__',
'__version__', 'decode', 'encode']
>>> dir(yaml)
['AliasEvent', 'AliasToken', 'AnchorToken', 'BaseDumper', 'BaseLoader', 'BlockEndToken',
'BlockEntryToken', 'BlockMappingStartToken', 'BlockSequenceStartToken', 'CBaseDumper',
'CBaseLoader', 'CDumper', 'CLoader', 'CSafeDumper', 'CSafeLoader', 'CollectionEndEvent',
'CollectionNode', 'CollectionStartEvent', 'DirectiveToken', 'DocumentEndEvent', 'DocumentEndToken',
'DocumentStartEvent', 'DocumentStartToken', 'Dumper', 'Event', 'FlowEntryToken',
'FlowMappingEndToken', 'FlowMappingStartToken', 'FlowSequenceEndToken', 'FlowSequenceStartToken',
'KeyToken', 'Loader', 'MappingEndEvent', 'MappingNode', 'MappingStartEvent', 'Mark',
'MarkedYAMLError', 'Node', 'NodeEvent', 'SafeDumper', 'SafeLoader', 'ScalarEvent',
'ScalarNode', 'ScalarToken', 'SequenceEndEvent', 'SequenceNode', 'SequenceStartEvent',
'StreamEndEvent', 'StreamEndToken', 'StreamStartEvent', 'StreamStartToken', 'TagToken',
'Token', 'ValueToken', 'YAMLError', 'YAMLObject', 'YAMLObjectMetaclass', '__builtins__',
'__doc__', '__file__', '__name__', '__package__', '__path__', '__version__', '__with_libyaml__',
'add_constructor', 'add_implicit_resolver', 'add_multi_constructor', 'add_multi_representer',
'add_path_resolver', 'add_representer', 'compose', 'compose_all', 'composer', 'constructor',
'cyaml', 'dump', 'dump_all', 'dumper', 'emit', 'emitter', 'error', 'events', 'load',
'load_all', 'loader', 'nodes', 'parse', 'parser', 'reader', 'representer', 'resolver',
'safe_dump', 'safe_dump_all', 'safe_load', 'safe_load_all', 'scan', 'scanner', 'serialize',
'serialize_all', 'serializer', 'tokens']
설계가 복잡하다는 것은 거의 변함없이 느린 설계를 의미하며, 이는 대부분의 사람들이 필요로 하는 것보다 훨씬 더 복잡합니다.
납득이 가는 답변이 있습니다만, 유감스럽게도 일부 PyYAML 매뉴얼 방향의 핸드웨이빙과 그 매뉴얼에 기재되어 있는 문장의 인용이 올바르지 않습니다.PyYAML은 덤프 중에 표현 그래프를 작성하지 않고 라인 에어 스트림을 생성합니다(및 마찬가지로).json는 재발이 없는지 확인하기 위해 ID 버킷을 유지합니다).
은 '우리'가 '우리'가 '우리'가 '우리'가 '우리'가 '우리'가'라는 걸 알아야 돼요.cjson스테이지 (dumper C-code)를 합니다.YAML 카페 덤퍼 4개Representer ★★★★★★★★★★★★★★★★★」Resolver Python 및 및 C에서 C 되어 있습니다.이 모듈에서는 Python SafeDumper는 C 라이브러리를 호출합니다.libyaml방출을 위해.
이 중요한 부분을 제외하고, 왜 시간이 더 걸리는지에 대한 간단한 답변은 YAML을 덤핑하는 것이 더 효과적이라는 것입니다.이것은 YAML이 @flow의 주장만큼 어렵기 때문이 아니라 YAML이 할 수 있는 추가 기능으로 JSON보다 훨씬 강력하며 에디터로 결과를 처리해야 할 경우 사용자 친화적이기 때문입니다.즉, 이러한 추가 기능을 적용해도 YAML 라이브러리에서 더 많은 시간이 소요되며, 대부분의 경우 적용 여부만 확인할 수 있습니다.
를 들어, PyYAML한 적이 PyYAML을 인용하지 않는 을 알 수 foo ★★★★★★★★★★★★★★★★★」bar이러한 문자열이 키이기 때문은 아닙니다.YAML에는 JSON의 제한이 없기 때문에 매핑의 키는 문자열이어야 합니다.예를 들어 매핑 값인 Python 문자열은 따옴표(일반)를 해제할 수도 있습니다.
항상 그렇지는 않기 때문에 캔에 중점을 둔다.숫자 문자로만 구성된 문자열을 예로 들어 보겠습니다.12345678로 쓸 따옴표로 쓸가 없습니다.따옴표로 쓸 .따옴표가 숫자와 때 이것은 따옴표로 기입해야 합니다.따옴표로 기입하지 않으면 숫자와 똑같이 표시됩니다(파싱할 때 다시 읽습니다).
PyYAML은 문자열을 인용할 때와 인용하지 않을 때를 어떻게 알 수 있습니까?덤프 시 처음에 문자열을 덤프한 후 결과를 해석하여 결과를 다시 읽었을 때 원래 값을 얻을 수 있도록 합니다.만약 그렇지 않다고 판명되면 인용문을 적용합니다.
이전 문장의 중요한 부분을 다시 읽으면 다시 읽을 필요가 없습니다.
스트링을 덤프하고 결과를 해석합니다.
즉, 로드 시 수행하는 모든 regex 매칭을 적용하여 결과 스칼라가 정수, 부동, 부울, datetime 등으로 로드되는지 확인하여 따옴표를 적용해야 하는지 여부를 결정합니다.¹
을 ""으로 앞뒤로 변환해야 .심플한 예로는 날짜 스탬프를 사용하여 작업하고 싶을 때 문자열을 다시 변환해야 합니다.datetime.datetimeJSON, JSON, JSON.에는 이 키와 값할 수 값)이라는 이 .
{ "datetime": "2018-09-03 12:34:56" }
또는 목록에 위치가 있는 경우:
["FirstName", "Lastname", "1991-09-12 08:45:00"]
또는 문자열 형식을 기반으로 합니다(예: regex 사용).
이러한 경우 모두 프로그램에서 훨씬 더 많은 작업을 수행해야 합니다.덤핑에 대해서도 마찬가지이며 그것은 단지 추가 개발 시간을 의미하는 것은 아니다.
다른 측정값과 비교할 수 있도록 내 기계에서 얻은 값으로 시간을 재생해 봅시다.이어서 조금.timeit수입하다했습니다.>>>프롬프트를 표시합니다.
from __future__ import print_function
import sys
import yaml
import cjson
from timeit import timeit
NR=10000
ds = "; d={'foo': {'bar': 1}}"
d = {'foo': {'bar': 1}}
print('yaml.SafeDumper:', end=' ')
yaml.dump(d, sys.stdout, Dumper=yaml.SafeDumper)
print('cjson.encode: ', cjson.encode(d))
print()
res = timeit("yaml.dump(d, Dumper=yaml.SafeDumper)", setup="import yaml"+ds, number=NR)
print('yaml.SafeDumper ', res)
res = timeit("yaml.dump(d, Dumper=yaml.CSafeDumper)", setup="import yaml"+ds, number=NR)
print('yaml.CSafeDumper', res)
res = timeit("cjson.encode(d)", setup="import cjson"+ds, number=NR)
print('cjson.encode ', res)
출력은 다음과 같습니다.
yaml.SafeDumper: foo: {bar: 1}
cjson.encode: {"foo": {"bar": 1}}
yaml.SafeDumper 3.06794905663
yaml.CSafeDumper 0.781533956528
cjson.encode 0.0133550167084
간단한 .datetime
import datetime
from collections import Mapping, Sequence # python 2.7 has no .abc
d = {'foo': {'bar': datetime.datetime(1991, 9, 12, 8, 45, 0)}}
def stringify(x, key=None):
# key parameter can be used to dump
if isinstance(x, str):
return x
if isinstance(x, Mapping):
res = {}
for k, v in x.items():
res[stringify(k, key=True)] = stringify(v) #
return res
if isinstance(x, Sequence):
res = [stringify(k) for k in x]
if key:
res = repr(res)
return res
if isinstance(x, datetime.datetime):
return x.isoformat(sep=' ')
return repr(x)
print('yaml.CSafeDumper:', end=' ')
yaml.dump(d, sys.stdout, Dumper=yaml.CSafeDumper)
print('cjson.encode: ', cjson.encode(stringify(d)))
print()
그 결과, 다음과 같이 됩니다.
yaml.CSafeDumper: foo: {bar: '1991-09-12 08:45:00'}
cjson.encode: {"foo": {"bar": "1991-09-12 08:45:00"}}
. myjson은 myjson을 감싸고 있습니다.cjson.encode의 「」를 있습니다.stringify정의되어 있습니다.「CHANGE MARGE:」
d = {'foo': {'bar': datetime.datetime(1991, 9, 12, 8, 45, 0)}}
ds = 'import datetime, myjson, yaml; d=' + repr(d)
res = timeit("yaml.dump(d, Dumper=yaml.CSafeDumper)", setup=ds, number=NR)
print('yaml.CSafeDumper', res)
res = timeit("myjson.encode(d)", setup=ds, number=NR)
print('cjson.encode ', res)
제공:
yaml.CSafeDumper 0.813436031342
cjson.encode 0.151570081711
이 간단한 출력은 속도 차이가 2단계에서 1단계 미만으로 되돌아갑니다.
YAML의 플레인 스칼라와 블록 스타일 포맷으로 데이터를 더 잘 읽을 수 있습니다.시퀀스(또는 매핑)에 후행 쉼표가 있으면 JSON의 동일한 데이터와 같이 수동으로 YAML 데이터를 편집할 때 오류가 줄어듭니다.
YAML 태그를 사용하면 (복잡한) 유형의 데이터 내 표시를 할 수 있습니다.JSON을 사용할 때는 코드 내에서 매핑, 시퀀스, 정수, 플로트, 부란 및 문자열보다 복잡한 모든 것에 주의해야 합니다.이러한 코드에는 개발 시간이 필요하며, 이러한 코드보다 빠를 가능성은 낮습니다.python-cjson(C는 C로 되어 있습니다).
재귀 데이터 구조(예: 위상 데이터) 또는 복합 키와 같은 일부 데이터 덤프는 PyYAML 라이브러리에 미리 정의되어 있습니다.여기서 JSON 라이브러리는 오류만 발생하며, 이를 위한 해결 방법을 구현하는 것은 간단하지 않으며 속도 차이는 그다지 중요하지 않을 정도로 속도가 느려질 수 있습니다.
이러한 파워와 유연성은 저속의 대가를 치르게 됩니다.JSON이 많은 간단한 것을 덤프하는 것이 더 나은 선택일 경우, 어쨌든 수동으로 결과를 편집할 가능성은 거의 없습니다.편집이나 복잡한 객체 또는 둘 다와 관련된 모든 작업에 대해 YAML 사용을 고려해야 합니다.
¹ It is possible to force dumping of all Python strings as YAML scalars with (double) quotes, but setting the style is not enough to prevent all readback.
언급URL : https://stackoverflow.com/questions/2451732/how-is-it-that-json-serialization-is-so-much-faster-than-yaml-serialization-in-p
'source' 카테고리의 다른 글
| Gradle 태스크에 시스템 속성을 전달하는 방법 (0) | 2023.03.16 |
|---|---|
| 프로젝터/셀레늄 "could found chromedriver at" (Windows에서) (0) | 2023.03.16 |
| 치명적인 오류:WP에 게시물을 추가할 때 메모리 부족 (0) | 2023.03.16 |
| 사용자 지정 JsonConverter에서 기본 직렬화를 사용하는 방법 (0) | 2023.03.16 |
| 한 페이지에 있는 총 시계 수를 어떻게 세나요? (0) | 2023.03.16 |