Scrapscript 직렬화를 위해 MessagePack과 CBOR를 성능, 단순성, 생태계 관점에서 비교하고, 여러 제약과 목표를 고려한 끝에 CBOR를 선택한 이유를 정리한다.
공유 가능한 프로그래밍 언어(sharable programming language)를 만들려면, (1) 전송/저장에 성능이 좋고 (2) 이해와 구현이 쉬운 알맞은 직렬화 포맷이 필요하다.
직렬화된 scrapscript 표현은 “flat scraps”라고 부른다.
이전 글에서 나는 scrapscript를 MessagePack에 욱여넣었다.
그때는 Max Bernstein이 이미 약 100줄로 직렬화기를 통째로 구현해두었다는 걸 몰랐다. 머리가 띵했다!
추가 실험 끝에, 이 직렬화 포맷은 (3) 기존 표준에 기대고, (4) 준-IR처럼 야근도 해주고, (5) 비교적 후진 하드웨어에서도 돌아가야 한다고 스스로를 설득하게 되었다.
아직은 탐색 단계다. 대중적인 CBOR와 msgpack 구현의 묵직함을 보고 나서도, Max의 포맷은 여전히 아주 매력적이다.
한편 Peter Saxton(EYG)이 CBOR를 MessagePack의 대안으로 추천하는 친절한 이메일을 보내줬다.
어라 — 경쟁 표준? 미묘한 설계 트레이드오프? 오픈소스의 고충? 안전벨트 매시라!
드라마는 재밌지만, 이 글과는 관련 없다. 이 글은 CBOR의 역사를 탄탄하게 요약한다. 이 댓글은 가장 논쟁적인 공개 설전을 모아둔다.
명백히 MessagePack이야말로 “쿨한” 사람들이 쓸 법한 것이다.
각 랜딩 페이지의 부제목을 비교해보자:
둘 중 하나는 젠장 넥타이를 매고 있다.
CBOR은 전반적으로 안 멋지다. 위원회가 설계했다. RFC 냄새가 진동한다. 두문자어는 시시하다. “씨-보어(SEE-BORE)”라고 발음하는 건 동전에 혀를 대는 기분이다. 저자 중 한 명이 “Carsten Bormann”이라서 이름부터 자위적인 느낌이 난다.
CBOR was inspired by MessagePack. MessagePack was developed and promoted by Sadayuki Furuhashi ("frsyuki").
-- RFC 8949
“원류” 브랜드에 대한 충성에는 분명한 가치가 있다. 단지 미미한 개선을 좇는 대신, 무(無)에서 가치를 합성해낸 창작자를 지지할 수 있기 때문이다. 파생 작업을 선호하는 건 개인적 표현보다 관료주의를 고르는 느낌을 줄 수 있다.
하지만 많은 사람은 “쿨함” 따위엔 관심 없다 — 그들이 원하는 건 압축률과 속도, 성능이다.
이 벤치마크에서, 작성자는 인기 있는 Go 라이브러리 둘의 성능을 비교했다. 이 테스트에 따르면 CBOR 라이브러리가 인코딩/디코딩을 약 200% 더 빠르게 수행한다.
하지만 효율이 전부는 아니다. 나는 보통 성능보다 개념적 단순함을 고른다. 대부분의 메이커처럼, 이해할 수 없는 힘에 의존해야 하는 상황을 싫어한다.
복잡도를 재는 데 문서 길이를 대리 지표로 쓰곤 한다. MessagePack은 그저 하나의 마크다운 파일이다. 반면 CBOR 스펙은 고유의 중력장을 거느린다.
그런데 더 자세히 들여다보니, 이 문서 냄새 테스트가 나를 헷갈리게 했다는 걸 알게 됐다. 이 HN 댓글이 요즘 내 생각을 잘 대변한다:
드라마는 다 건너뛰고 스펙을 읽은 다음 인코더/디코더를 구현했다. CBOR는 애초에 MessagePack 계열 포맷이 그렇게 되었어야 했던 그대로다: 여러 특수 규칙을 하나의 일반화로 대체해 단정하고 단순하다는 점에서 기술적으로 우월하다.
최상위 관점에서 MessagePack은 여러 타입을 정의한다: 정수, 부동소수, 배열, 확장 등등. CBOR는 이 타입들을 “태그”로 통일한다. 이 패턴은 설명하고 구현하기 훨씬 쉬워 보인다.
이 사람은 CBOR의 태그 설계가 엉성하다고 주장한다. 결론에는 공손히 동의하지 않지만, 좋은 지적이 많다고 생각한다.
“직렬화”는 흔히 “통신”을 함의한다. 대역폭은 비싸다. 그래서 MessagePack과 CBOR 같은 포맷은 컴퓨터 프로토콜의 후보로 눈에 띈다.
메트칼프의 법칙은 프로토콜의 가치는 그 인기(네트워크 크기)에 비례한다고 말한다.
데이터가 곧 증거다:
| 3.1K☆ | C | msgpack/msgpack-c |
| 2.4K☆ | Go | vmihailenco/msgpack |
| 1.9K☆ | Python | msgpack/msgpack-python |
| 1.8K☆ | Go | tinylib/msgp |
| 1.4K☆ | Java | msgpack/msgpack-java |
| 1.4K☆ | JS | msgpack/msgpack-javascript |
| 1.2K☆ | Rust# | 3Hren/msgpack-rust |
| 1.0K☆ | JS | kawanet/msgpack-lite |
| 837☆ | C# | msgpack/msgpack-cli |
| 806☆ | Go | fxamacker/cbor |
| 784☆ | PHP | msgpack/msgpack-php |
| 764☆ | Ruby | msgpack/msgpack-ruby |
| 529☆ | JS | kriszyp/msgpackr |
| 519☆ | C | intel/tinycbor |
| 364☆ | JS | hildjj/node-cbor |
| 354☆ | C | PJK/libcbor |
| 320☆ | JS | paroga/cbor-js |
| 311☆ | JS | msgpack/msgpack-node |
| 303☆ | JS | kriszyp/cbor-x |
| 300☆ | Rust | pyfisch/cbor |
| 284☆ | Rust | enarx/ciborium |
| 243☆ | Python | agronholm/cbor2 |
| 214☆ | C# | peteroupc/CBOR |
| 210☆ | Erlang | msgpack/msgpack-erlang |
| 196☆ | Haskell | well-typed/cborg |
| 142☆ | Swift | valpackett/SwiftCBOR |
| 138☆ | Haskell | msgpack/msgpack-haskell |
| 119☆ | Java | c-rack/cbor-java |
나의 구체적 용례에선 CBOR의 완승이다. Scrapscript 표현은 CBOR의 확장 태그 안에서 아주 기분 좋게 느껴진다.
난 CBOR를 선호하지만, CBOR를 선호한다는 사실이 마음에 들지는 않는다. 정치적 수식어가 붙은 기술을 쓰는 건 마음에 걸린다.
어쨌든 MessagePack과 CBOR는 JSON 대비 엄청난 진보다. MessagePack이 인기 면에서 우위를 유지하는 한, 두 선택지 모두 합리적으로 보인다.
프로토콜은 중요하다. 신중하게 소통하라.