diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 000000000000..afd5e940a6eb --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,73 @@ +name: serialization-benchmark + +# Runs the serialization benchmark and uploads the results (CSV + HTML report + encoded +# samples) as a downloadable workflow artifact. +# +# Push/PR runs always use the `quick` preset. Their *size* and *losslessness* numbers are +# deterministic and meaningful; the *timing* numbers are only indicative on a shared runner — +# use workflow_dispatch (or a dedicated machine) when the exact timings matter. +on: + push: + branches: + - exploration/serialization + pull_request: + workflow_dispatch: + inputs: + preset: + description: "Corpus size preset" + type: choice + options: [quick, full] + default: quick + repeat: + description: "Timed runs per measurement (median reported)" + default: "5" + compas_pb_ref: + description: "compas_pb git ref to benchmark (branch/tag/sha)" + default: "benchmark/double-precision" + +permissions: + contents: read + +# One run per ref; a new push cancels the previous (and its PR event's duplicate) run. +concurrency: + group: serialization-benchmark-${{ github.ref }} + cancel-in-progress: true + +jobs: + benchmark: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install compas + benchmark dependencies + run: | + python -m pip install --upgrade pip + pip install -e . + # Optional benchmark formats (skipped automatically if absent, but we want them here): + pip install msgspec zstandard + # The optimized compas_pb under test. Its generated _pb2 modules are committed, so no + # protoc is needed. Public repo -> no auth required. + pip install "compas_pb @ git+https://github.com/gramaziokohler/compas_pb.git@${{ inputs.compas_pb_ref || 'benchmark/double-precision' }}" + + - name: Show versions + run: | + python -c "import compas, compas_pb, msgspec, zstandard; print('compas', compas.__version__); print('compas_pb', compas_pb.__version__)" + + - name: Run benchmark (${{ inputs.preset || 'quick' }}) + run: | + python -m benchmarks.serialization.run \ + --preset "${{ inputs.preset || 'quick' }}" \ + --repeat "${{ inputs.repeat || '5' }}" \ + --out "benchmarks/serialization/results/baseline_${{ inputs.preset || 'quick' }}.csv" + + - name: Upload results + uses: actions/upload-artifact@v4 + with: + name: serialization-benchmark-${{ inputs.preset || 'quick' }}-${{ github.run_number }} + path: benchmarks/serialization/results/ + retention-days: 90 + if-no-files-found: error diff --git a/CHANGELOG.md b/CHANGELOG.md index 48f846510721..76f4845fdbf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `TOL.update()` method for explicit global state modification. * Added `TOL.temporary()` context manager for scoped changes. * Added missing implementation of `Brep.to_polygons()` in `compas_rhino.geometry.RhinoBrep`. +* Added `Data.canonical_hash()` for content-based hashing that is independent of guid, name, and serialization format (`sha256()` is unchanged). ### Changed diff --git a/PRD-serialization.md b/PRD-serialization.md new file mode 100644 index 000000000000..270c54f1879a --- /dev/null +++ b/PRD-serialization.md @@ -0,0 +1,304 @@ +# PRD — Improving Serialization in COMPAS core + +**Status:** Draft / exploratory +**Author:** (you) +**Date:** 2026-07-07 +**Related code:** `compas.data` (JSON), `compas_pb` (protobuf), `arrow-opfs-poc` (zero-deserialization experiment) + +--- + +## 1. Summary + +COMPAS has two serialization modes today: + +1. **JSON (default, in core)** — human-readable, universal, self-describing via a + `dtype` string; implemented in `compas/data/`. +2. **Protobuf binary (opt-in, external)** — `compas_pb`, a plugin that maps + registered COMPAS types to hand-written `.proto` messages for a smaller, + faster wire format. + +Both are **row/object-oriented and fully deserializing**: every element is parsed +into a Python object on load. For COMPAS's heavy payloads — meshes, pointclouds, +graphs with millions of numeric values — this is the dominant cost in time, +memory, and (for JSON) size. This PRD scopes the problem and lays out candidate +directions, informed by a separate Arrow experiment showing that the expensive +part to eliminate is **deserialization**, not the unavoidable I/O copy. + +**This is a measurement-driven PRD.** No direction is adopted on argument alone; +each is decided against a benchmark suite built around two representative +large-data types — **`Mesh`** and **`Pointcloud`** — at varying sizes (§10). + +## 2. Background — current state + +### 2.1 JSON path (core) + +- `Data.__jsondump__()` produces `{"dtype", "data", "guid", "name"}`; + `__data__` is the per-class payload. +- `DataEncoder(json.JSONEncoder)` walks objects; `DataDecoder` reads `dtype` + (e.g. `"compas.geometry/Point"`), imports the class, and calls + `__from_data__(data)`. +- Entry points: `json_dump/dumps/load/loads`, plus zip-compressed + `json_dumpz/loadz`. Options: `pretty`, `compact`, `minimal`. +- Numpy arrays are flattened with `.tolist()`. +- **Coupling to hashing:** `Data.sha256()` hashes `json_dumps(self)`. Object + identity / change detection is therefore tied to the JSON text encoding. + +**Strengths:** universal, debuggable, no schema/codegen, forward/backward +tolerant. +**Weaknesses:** large (text + repeated keys), slow to parse, float precision +depends on `repr`, no columnar/bulk representation for numeric arrays. + +### 2.2 Protobuf path (`compas_pb`) + +- Plugin discovered via `compas_pb.plugins` entry-point group; a + `SerializerRegistry` maps `type → serializer` and `proto type_url → deserializer`, + registered by `@pb_serializer` / `@pb_deserializer` decorators in + `conversions.py`. +- Container schema (`message.proto`): `AnyData` is a `oneof` of a packed + `google.protobuf.Any`, a `struct.Value` primitive, or a `FallbackData` + wrapping a `DictData`. `MessageData` carries `data` + a `version` string. +- Hand-written `.proto` per type (`geometry.proto`, `datastructures.proto`) plus + two conversion functions per type. +- **Fallback:** unregistered `Data` subclasses are serialized as their + `__jsondump__()` dict inside a protobuf `DictData` — i.e. the JSON shape in a + protobuf envelope, with little size/speed benefit and still routed through the + JSON `DataDecoder` on the way back. + +**Strengths:** compact binary, schema'd, cross-language potential (any protobuf +runtime, including JS/Wasm), versioned envelope. +**Weaknesses (as-is):** + +- **Precision loss.** Geometry `.proto` stores coordinates as proto `float` + (32-bit IEEE-754). COMPAS geometry is float64 → **lossy round-trip** for + `x/y/z`, matrices, radii, angles. (To be quantified in the benchmark suite.) +- **Integer coercion.** The primitive path routes `int` through + `struct.Value.number_value` (float64), reconstructing `int` only when the + value `is_integer()`. Large/precise ints and the int/float distinction are at + risk. +- **Coverage & maintenance.** Every new type needs a `.proto` message, codegen, + and two hand-written functions. Anything not covered silently degrades to the + JSON-shaped fallback. +- **Brittle versioning.** `version` compatibility is an exact string match that + only emits a warning on mismatch. +- **Still fully deserializing.** Numeric bulk (vertices, faces) is decoded + element-by-element into Python objects, same as JSON. + +## 3. Motivation / problem statement + +1. **Heavy numeric payloads are the real cost.** A `Mesh`/`Pointcloud` with N + vertices pays O(N) Python-object construction on load in *both* modes. + Neither format has a bulk/columnar representation for coordinate and index + arrays. +2. **Browser & cross-language interop is growing** (web viewers, JS tooling). + JSON is large and slow there; protobuf needs generated JS stubs and still + deserializes fully. +3. **The binary path has correctness gaps** (float32, int coercion) that make it + unsafe as a silent drop-in for JSON today. +4. **Two divergent code paths** with different type coverage, guarantees, and + failure modes create a maintenance and correctness burden. + +## 4. Findings from the Arrow zero-deserialization experiment + +A sibling PoC (`arrow-opfs-poc`) moved large columnar data between Python and a +browser. The transferable conclusions: + +- **The boundary copy is unavoidable and cheap; deserialization is the expensive + part and *is* avoidable.** With a columnar layout (Arrow IPC), the receiver + points typed vectors straight at the received buffer — **zero per-element + parsing**. Proven both in Python (mmap; column buffer aliases the mapped file) + and in the browser (`Float64Array` aliases the fetched `ArrayBuffer`). +- **True cross-sandbox "same physical pages" zero-copy is impossible** (browser + has no mmap/shared memory with a foreign process). So the realistic target for + COMPAS is **zero-deserialization**, not zero-copy. +- **Implication for COMPAS:** representing the numeric-heavy parts of data + structures (vertex coordinates, face indices, pointclouds, transformation + matrices) as contiguous typed buffers would let both Python and JS consumers + skip per-element construction — the single biggest lever for large-model load + time and memory. `Mesh` and `Pointcloud` are the natural first targets. + +## 5. Goals / non-goals + +### Goals + +- Preserve JSON as the **default, universal** format; no regression for existing + files or callers. +- Make a binary mode that is a **lossless, safe** alternative to JSON (fix + float32/int issues) with a clear coverage-and-fallback contract. +- Introduce a path that avoids **per-element deserialization** for large numeric + arrays (columnar/buffer-backed), usable from both Python and the browser. +- Unify the two modes behind a **single, stable serialization API** with format + negotiation, so callers choose a format without divergent semantics. +- Keep object identity/versioning (`sha256`, schema) **format-independent**. +- **Decide every direction on measured evidence** from the §10 benchmark suite. + +### Non-goals + +- Replacing JSON as the default. +- True zero-copy across the browser boundary (physically impossible). +- Changing COMPAS object models / public class APIs. +- Solving streaming/partial loading in v1 (note it as future work). + +## 6. Constraints & assumptions + +- **IronPython/.NET is no longer a binding constraint.** The next major release + of COMPAS drops IronPython support. This removes the historical barrier to + numpy/pyarrow-based approaches and makes a columnar/typed-buffer path viable + much closer to the core rather than only in an isolated extension. +- **Backward compatibility.** Existing `.json` files must keep loading; `dtype` + resolution and `__from_data__` remain the contract. +- **Precision.** Geometry is float64; the default must be lossless and any binary + mode must be lossless unless the caller explicitly opts into a compact/lossy + profile. +- **Dependency weight, not availability, is the trade-off now.** pyarrow/numpy + are acceptable where they earn their keep; keep them optional extras so a + minimal core install stays light, but they are no longer disqualified by + runtime. + +## 7. Requirements + +### Functional + +- **F1** A single high-level API (e.g. `compas.data.dump/load`) that accepts a + `format=` selector (`"json" | "protobuf" | "arrow" | ...`) and produces/reads a + self-identifying container (magic bytes / MIME) so `load` can auto-detect. +- **F2** Round-trip **losslessness** for the default and the "safe binary" + profile across all registered COMPAS types (property-based equality tests). +- **F3** A documented **fallback contract**: unregistered types degrade + predictably (and loudly, not silently) with a single shared type-resolution + mechanism (`dtype` ↔ proto type_url ↔ Arrow schema metadata). +- **F4** A **columnar/buffer profile** for numeric-heavy structures (`Mesh` + vertices/faces, `Pointcloud`, transformation matrices) that reconstructs typed + arrays without per-element Python object creation. +- **F5** Format-independent **object hashing/versioning** (decouple `sha256` from + the JSON text; hash the canonical `__data__`/schema instead). + +### Non-functional + +- **N1** Binary/columnar load of a large `Mesh`/`Pointcloud` is materially faster + and lower-memory than JSON (targets set from the §10 baseline, not guessed). +- **N2** Default JSON path performance and behavior unchanged (no regression). +- **N3** Heavy deps (pyarrow/protobuf) isolated as optional extras; minimal core + install works without them. +- **N4** Clear, versioned wire format with a real compatibility policy (not an + exact-string warning). + +## 8. Candidate directions (to evaluate) + +These are not mutually exclusive; the likely answer is a layered combination. +**Each is gated on §10 benchmark results.** + +### Direction A — Harden & optionally accelerate the JSON path +- Decouple `sha256`/versioning from JSON text (canonical form). +- Optional fast encoder (e.g. `orjson`) behind the same API where available. +- *Pros:* low risk, immediate. *Cons:* doesn't address bulk-numeric cost. + +### Direction B — Make `compas_pb` a safe, first-class binary mode +- Fix precision: `double` not `float`; explicit int handling. +- Reduce per-type boilerplate (generate `.proto`/conversions from `__data__` + schema, or a reflective serializer) and define the fallback contract. +- Real version-compat policy. +- *Pros:* compact, cross-language, builds on existing work. *Cons:* still fully + deserializing; codegen/runtime dep for consumers. + +### Direction C — Columnar/buffer profile (Arrow-informed) for numeric bulk +- Encode `Mesh` vertex coords + face indices, `Pointcloud` points, and matrices + as contiguous typed buffers (Arrow IPC or a minimal home-grown buffer format); + keep metadata/attributes in a flexible container (JSON or protobuf). +- Reconstruct as numpy/typed arrays with **zero per-element parsing**; first-class + JS consumption via Arrow-JS in the browser. +- *Pros:* attacks the real bottleneck; strong browser story; now viable close to + core (no IronPython barrier). *Cons:* pyarrow dependency weight; hybrid layout + (bulk buffers + heterogeneous attributes) needs design. + +### Direction D — Unified serialization façade + format negotiation +- One API over all formats, self-identifying containers, shared type registry. +- Callers pick a format by capability/target (debugging → JSON; web/native bulk → + Arrow/protobuf) without semantic divergence. +- *Pros:* consolidates guarantees and coverage. *Cons:* design/coordination + effort; must not leak heavy deps into a minimal install. + +**Straw-man recommendation for discussion:** D as the umbrella; A first (cheap, +de-risks hashing); B to make binary *safe*; C for the numeric-heavy win — +**pending the numbers**, since C is only worth its dependency weight if the +measured `Mesh`/`Pointcloud` load-time and memory gains are large. + +## 9. Open questions + +- Where does the **type registry** live so JSON, protobuf, and Arrow share one + `dtype` ↔ schema mapping instead of three? +- Can `__data__` schemas be introspected to **auto-generate** binary encoders + (kill the per-type boilerplate), or is hand-tuning required for the hot types? +- What is the **canonical form** for hashing that is stable across formats? +- Attribute dictionaries are heterogeneous — do they stay in a flexible container + even in the columnar profile (hybrid layout)? (Likely yes.) +- Compatibility policy: semantic versioning of the wire format + capability flags + vs. current exact-string check. +- Is **streaming / partial load** (huge models) in scope later, and does the + chosen container support it? + +## 10. Benchmarking — the decision instrument + +Measurement is the backbone of this PRD, not an afterthought. Directions A–D are +accepted or rejected on these numbers. + +### 10.1 Corpus + +Two types stand in for "large data chunks", each at several sizes so we can see +scaling, not a single point: + +| Subject | Payload | Sizes (approx.) | +|--------------|-------------------------------------------|---------------------------------------| +| `Mesh` | vertices (float64 ×3) + faces (int index) | 10³, 10⁵, 10⁶, 5·10⁶ vertices | +| `Pointcloud` | points (float64 ×3), optional attributes | 10⁴, 10⁶, 10⁷, 5·10⁷ points | + +Include one variant **with per-element attributes** and one **without**, since +attribute dicts are the part a columnar layout cannot flatten — this exposes the +hybrid-layout cost. + +Fixtures should be generated deterministically (seeded) so runs are comparable, +and reused across all formats. + +### 10.2 Formats under test + +- JSON (`compact`), and JSON+zip (`json_dumpz`) as the size baseline. +- `compas_pb` as-is (float32) and a `double` variant (to isolate the precision + fix's size/speed cost). +- Columnar/Arrow prototype (Direction C). + +### 10.3 Metrics (per subject × size × format) + +- **Serialized size** (bytes on disk/wire); compression ratio vs JSON. +- **Serialize time** and **deserialize time** (wall clock, warm cache, multiple + runs → median + spread). +- **Peak memory** during deserialize (e.g. `tracemalloc` / RSS sampling). +- **Round-trip fidelity:** exact equality of `__data__`; for lossy profiles, + max/RMS coordinate error (this is where float32 gets quantified). +- **Browser** (Direction C): fetch→reconstruct time for the largest `Mesh`/ + `Pointcloud` in JS, and whether typed arrays alias the received buffer. + +### 10.4 Reporting + +A small, repeatable harness (script + fixtures) that emits a table/CSV per run, +committed alongside the PRD so results are reproducible and reviewable. Targets +for N1 are set **after** the baseline exists, expressed as a required factor +improvement over JSON for the largest sizes. + +## 11. Rough phasing + +1. **Baseline & harness.** Build the §10 corpus (`Mesh`, `Pointcloud`) and + benchmark harness; measure JSON and current `compas_pb`. Decouple `sha256` + from JSON text. *Exit:* a committed results table and agreed N1 targets. +2. **Safe binary.** Fix `compas_pb` precision/int issues; measure the `double` + cost; define fallback + version policy; expand/auto-generate coverage. +3. **Columnar prototype.** Arrow/buffer profile for `Mesh` + `Pointcloud`; Python + and browser reconstruction benchmarked against the baseline. *Go/no-go on the + measured gain vs pyarrow dependency weight.* +4. **Unified API.** Format negotiation + self-identifying containers + shared + registry; docs and migration guidance. + +--- + +*Appendix — key source references:* `compas/data/data.py`, +`compas/data/encoders.py`, `compas/data/json.py`; +`compas_pb/core.py`, `compas_pb/registry.py`, `compas_pb/conversions.py`, +`compas_pb/protobuf_defs/.../{message,geometry,datastructures}.proto`. diff --git a/benchmarks/README.md b/benchmarks/README.md new file mode 100644 index 000000000000..d18d3e8887b3 --- /dev/null +++ b/benchmarks/README.md @@ -0,0 +1,249 @@ +# Serialization benchmarks + +The measurement instrument for [`PRD-serialization.md`](../PRD-serialization.md). Directions A–D +in the PRD are accepted or rejected on the numbers this harness produces, not on argument. + +## What it measures (PRD §10) + +For each **subject × size × format**: + +- **serialized size** (bytes on the wire) and compression ratio vs JSON; +- **serialize** and **deserialize** time (median + spread over `--repeat` runs), the + **round-trip total**, and **throughput** (MB of wire per second) so formats are + comparable across sizes — this is how the impact of zipping / protobuf / Arrow gets read; +- **peak memory** during deserialize (`tracemalloc`); +- **round-trip fidelity**: exact equality of `__data__` and of the format-independent + `canonical_hash` — where a lossy profile (e.g. protobuf float32) will show up. + +## Subjects (PRD §10.1) + +| Subject | Payload | Attributes | +|---------------|--------------------------------------------|------------| +| `mesh` | jittered grid, float64 verts + int faces | none | +| `mesh_attrs` | same + a per-vertex float | per-element (hybrid-layout cost) | +| `pointcloud` | N float64 points | none¹ | +| `graph` | jittered grid, nodes (x,y,z) + edges | per-node coords | +| `points`, `vectors`, `lines`, `frames`, `planes`, `boxes`, `spheres`, `circles` | a list of N primitives/shapes | — | +| `polylines`, `polygons`, `beziers`, `polyhedrons` | a list of N compound objects (each holds several points) | — | +| `arcs`, `ellipses`, `parabolas`, `hyperbolas` | a list of N conics | — | +| `cylinders`, `cones`, `capsules`, `toruses` | a list of N solids | — | +| `transformations`, `translations`, `rotations`, `scales`, `shears`, `reflections`, `projections`, `quaternions` | a list of N transforms/quaternions | — | + +¹ The COMPAS `Pointcloud` type has no per-point attribute slot, so the "with per-element +attributes" case lives on `mesh_attrs`. + +The primitive/shape subjects are **collections** (a Python `list` of N objects, a common +real-world payload — e.g. a list of frames as robot targets). The compound subjects each +hold several points, exercising compas_pb's flat `repeated double` point arrays. **The corpus +now benchmarks all 31 of compas_pb's native serializable types** (the report shows a live +coverage banner). All round-trip losslessly except `frames`/`planes`, which show a ~1e-16 +discrepancy for **every** format including JSON — COMPAS re-normalizes their axes on +construction (`normalize(normalize(v)) ≠ normalize(v)` at the last ULP), a geometry quirk the +fidelity check surfaces, not a serialization defect. + +Fixtures are seeded ([`fixtures.py`](serialization/fixtures.py)) so runs are comparable and +reused across every format; the harness measures a list or a single `Data` object +transparently. + +## Formats under test + +Registered in [`formats.py`](serialization/formats.py): + +- `json` — compact text (the default, lossless); +- `json_zip` — zip-compressed JSON (size baseline); +- `compas_pb` — protobuf binary via the `compas_pb` plugin (optional dep). We track the + **optimized branch** (`benchmark/double-precision`): double precision, flat packed + coordinate arrays, columnar vertex attributes. Skipped automatically if not installed. + The harness still quantifies any residual error (`max_abs_error` / `rms_error`). +- `compas_pb_zip` — the above, zip-compressed (DEFLATE); +- `compas_pb_zstd` — the above, zstandard-compressed (optional `zstandard` dep; similar + ratio to zip at ~3× faster compression). +- `compas_msgpack` — **MessagePack over the JSON-shape tree**, the approach from the Kumiki + project (which uses `msgspec`), applied to COMPAS types via an `msgspec` `enc_hook` on + `__jsondump__` (no `Mesh` subclass needed). Optional `msgspec` dep. Binary but + row-oriented / schemaless — a midpoint between JSON and the columnar `compas_pb`. +- `compas_msgpack_zstd` — the above, zstandard-compressed. + +An Arrow/columnar prototype registers the same way; the runner picks up any registered, +available format automatically and skips those whose optional dependency is missing +(`available=False`). + +## Running + +Every run writes a CSV **and** a self-contained, theme-aware HTML report next to it +(`results/.html`) — open it in a browser for a readable, per-subject view with +grouped bars (round-trip time, wire size) and a full table. The CSV is the machine +record; the HTML is the human one. Each format serializes a **fresh** fixture, so one +format's dump (JSON accesses `.guid`) can't mutate what a later format encodes. + +It also writes **encoded-format samples** to `results/samples/` — tiny, fully-inspectable +fixtures encoded with the three main formats, so you can see the *shape* of each encoding: +`.json`, `.pb` + `.pb.json` (the protobuf bytes rendered back to +JSON, showing the flat/columnar wire structure), and `.msgpack` + +`.msgpack.json` (the row-oriented tree). Regenerate standalone with +`python -m benchmarks.serialization.samples`; skip during a run with `--no-samples`. + +```bash +# Quick baseline (small sizes, fast) — writes results/baseline_quick.{csv,html} + samples/ +python -m benchmarks.serialization.run + +# Full PRD corpus (large; slow, memory-hungry) +python -m benchmarks.serialization.run --preset full --out benchmarks/serialization/results/baseline_full.csv + +# Subset +python -m benchmarks.serialization.run --subjects mesh pointcloud --formats json +``` + +Run from the repository root so `benchmarks` imports as a package. Requires a working +`numpy` (COMPAS geometry imports it at load time). + +### On CI + +The `serialization-benchmark` GitHub Actions workflow +([`.github/workflows/benchmark.yml`](../.github/workflows/benchmark.yml)) runs the benchmark +on demand (**Actions → serialization-benchmark → Run workflow**) and uploads the whole +`results/` folder (CSV + HTML report + samples) as a downloadable artifact. Inputs let you +pick the `preset`, `repeat` count, and the `compas_pb` git ref to test (default the +`benchmark/double-precision` branch — its `_pb2` modules are committed, so no protoc is +needed). It is manual-only: benchmark timings are meaningless on a noisy shared runner if +triggered on every push. + +## Results + +`results/baseline_quick.{csv,html}` — JSON, JSON+zip, and the **optimized** `compas_pb` +(raw and zip-compressed) on the quick corpus. The HTML report opens with an **executive +summary** (headline stat tiles + a per-subject winners table) so the conclusion is visible +before any detail. Headline: with double precision + flat coordinate arrays + inline +attribute maps, `compas_pb` goes from *larger and slower than JSON* (as shipped) to the +**smallest and fastest lossless** option on numeric-heavy data: + +| subject (largest size) | JSON | json_zip | compas_pb | compas_pb_zip | +|---|---|---|---|---| +| mesh @10k, wire | 864 KB | 238 KB | 320 KB | **158 KB** | +| mesh @10k, round-trip | 66 ms | 87 ms | **50 ms** | 58 ms | +| mesh_attrs @10k, wire | 1.1 MB | 339 KB | 398 KB | **233 KB** | +| mesh_attrs @10k, round-trip | 67 ms | 97 ms | **52 ms** | 65 ms | +| pointcloud @100k, wire | 5.5 MB | 2.7 MB | 2.3 MB | **2.2 MB** | +| pointcloud @100k, round-trip | 343 ms | 551 ms | **186 ms** | 254 ms | + +On the bulk-numeric types every format is **lossless** and `compas_pb` is smallest+fastest +(`_zip`/`_zstd` smallest on the wire, raw `compas_pb` fastest to load). Applied optimizations: + +- **Precision fixed** (`float → double`): coordinate error is **0** everywhere. +- **Flat coordinate arrays** replaced a `PointData` message *per vertex/point* (each of + which carried a per-point UUID + name) with packed `repeated double` triplets — the + dominant size/speed win, for Mesh, Pointcloud, Polyline, Polygon, Bezier, Polyhedron. +- **Inline `map` attributes** (Mesh, Graph) dropped the `DictData` + wrapper and skip empty/default entries. +- **Int/float distinction preserved** — `AnyData` gained explicit `int64`/`double` arms + instead of routing numbers through `google.protobuf.Value`, so `0.0` no longer comes + back as `0`. This makes `Mesh`/`Graph` fully lossless (canonical hash matches). +- **CSR face storage** — faces are a flat packed `face_vertices` index array + a + `face_sizes` length array, instead of one `FaceList` message per face. +- **No `Any` wrapper on plain dicts/lists** — `AnyData` gained explicit `dict_value` / + `list_value` arms, so every nested dict/list no longer carries a ~44-byte + `google.protobuf.Any` `type_url`. Helps Graph, fallback, and any nested container. +- **Columnar attributes (mesh vertices + graph nodes/edges)** — each attribute name is + stored once with a packed value array (typed `double`/`int`/`bool`, generic fallback, + dense columns skip indices), instead of a dict per element. `mesh_attrs@10k`: + **1.1 MB → 398 KB** / **130 ms → 56 ms**; `graph@10k`: **931 KB → 360 KB** / **379 ms → + 87 ms** — both now smaller *and* faster to load than JSON. +- **guid/name only when explicitly set** — auto-generated guids and default names are no + longer written (an object serializes its guid only if `_guid` was set). This removes + ~40 bytes per object, including every `Point`/`Vector` nested in Lines/Frames/shapes. + `boxes@10k` **3.4 MB → 1.6 MB** (now < JSON 2.7 MB). Contract change: auto guids no longer + round-trip (they are session-local uuid4s); explicit guids still do. + +### What the expanded corpus shows + +The corpus also covers `graph` and collections of primitives/shapes, plus a third format: +`compas_msgpack` (MessagePack over the JSON-shape tree — the Kumiki approach). Findings: + +- **`compas_pb` is the smallest on 11/12 subjects** (bulk-numeric *and* primitive/shape + lists) after the guid fairness fix (see below). It is also **fastest to load on the + bulk-numeric types** (mesh, mesh_attrs, pointcloud, graph). For **small-primitive lists** + (points/vectors/lines/…) it is still smallest but *loads* slightly slower than JSON — + each element goes through registry dispatch + message unpack. See Pending #1. +- **`compas_msgpack` sits between JSON and `compas_pb`**: binary and ~30–45% smaller than + JSON with *very fast encode*, but row-oriented — so its **load speed is JSON-like** (no + columnar/bulk win). A cheap JSON upgrade that does not fix deserialization. +- **`frames`/`planes` show `lossless=no` for every format including JSON** (~1e-16 error): + `Frame`/`Plane` re-normalize their vectors on construction, so `normalize(normalize(v))` + differs at the last ULP. A COMPAS geometry quirk the fidelity check surfaces, not a + serialization defect. + +> **Fairness fix.** Each format serializes a *fresh* fixture. Serializing to JSON accesses +> `.guid` (forcing it onto the object); when all formats shared one object, `compas_pb` then +> re-serialized those forced guids, overstating its size on primitive/shape lists by ~30%. +> With independent objects, `compas_pb`'s smallest-on-11/12 result stands. + +The HTML report opens with a **three-pill toggle** (Uncompressed / Compressed / All) that +hides non-matching formats, re-normalizes the bars, and re-bases the summary — so you can +read *json vs compas_pb vs compas_msgpack* raw, or the compressed variants, in isolation. +The summary tiles report **median** ratios with their range across subjects (not a single +best), plus how many subjects `compas_pb` wins on size/speed. + +To inspect the actual encodings, see `results/samples/` (per subject: `.json`, `.pb` + +`.pb.json`, `.msgpack` + `.msgpack.json`) — `*.pb.json` shows the columnar/flat wire shape, +`*.msgpack.json` the row-oriented tree. + +The optimizations live on the `benchmark/double-precision` branch of the external +`compas_pb` repo (regenerate `_pb2` with the pinned protoc `invocations.PROTOC_VERSION`, +then `pip install -e .` into this `.venv`). + +### N1 — binary-vs-JSON scaling (`--preset full`) + +`results/baseline_full.{csv,html}` sweeps the bulk-numeric subjects across **1e3 → 1e6** +elements. compas_pb vs JSON **at 1e6 elements**: + +| subject | wire | load time | peak memory | +|---|---|---|---| +| `mesh` | **2.8×** smaller (100 → 35 MB) | 1.3× faster | 1.9× lower | +| `mesh_attrs` | **3.0×** smaller (128 → 43 MB) | 1.3× faster | 1.8× lower | +| `pointcloud` | **2.4×** smaller (55 → 23 MB) | 1.2× faster | 1.2× lower | +| `graph` | **2.1×** smaller (85 → 40 MB) | **3.2×** faster | 1.7× lower | + +Wire and peak-memory ratios are ~flat with size; the **load-time** advantage *grows* with +size (compas_pb has fixed per-call overhead — it can be slower than JSON at 1e3, but is +amortized by 1e5 and clearly ahead by 1e6). Graph shows the biggest load win (3.2×) because +the columnar node layout skips rebuilding N per-node dicts. + +**N1 is met**, with a proposed acceptance bar (holds for all four subjects at **≥ 1e5** +elements): **wire ≥ 2× smaller, load ≥ 1.2× faster, peak memory ≥ 1.2× lower** than compact +JSON. + +The preset caps at 1e6 because beyond that the `tracemalloc` peak-memory probe (it traces +every allocation on deserialize) dominates runtime — measuring memory at PRD scale needs an +RSS-sampling probe instead. A one-off **spot check at 5e7 points** (size + time only, no +tracemalloc) confirms the ratios are flat with size and it does not OOM (RSS ≈ 9 GB of 36): + +| pointcloud @ 5e7 | json | compas_pb | ratio | +|---|---|---|---| +| wire | 2.90 GB | 1.20 GB | **2.42× smaller** | +| dump | 208 s | 24 s | **8.6× faster** | +| load | 188 s | 155 s | **1.21× faster** | + +(The wire/load ratios match the 1e6 numbers almost exactly; the dump gap widens because JSON +spends ~200 s building a 2.9 GB string while compas_pb writes packed doubles.) + +## Pending optimizations + +1. **Batched primitive lists** — collections of small primitives load slightly slower than + JSON because each element goes through registry lookup + message unpack. A batched/columnar + encoding for homogeneous primitive lists (e.g. N points/frames as flat arrays) would close + it, the same way the mesh/graph rewrites did. +2. **Peak memory at PRD scale (5e6/5e7)** — size and load-time ratios are already confirmed + flat at 5e7 (spot check above), so this is a *productization stress test*, not an N1 gap. + The only thing unmeasured at those sizes is peak memory, which needs an RSS-sampling probe + (the `tracemalloc` probe is too slow past ~1e6). + +## Status + +Phase 1: baseline harness + JSON numbers, plus `Data.canonical_hash()` decoupling object +identity from the JSON text (`sha256()` is unchanged). +Phase 2: `compas_pb` measured and **optimized** — double precision, flat coordinate arrays, +columnar attributes (mesh vertices/faces/edges, graph nodes/edges), explicit int/float, CSR +faces, guid/name omission, msgpack comparison. Now a **lossless** binary mode, **smaller and +faster than JSON** on numeric data (**N1 met** — see the full-preset table). All **31** +serializable types benchmarked. The wire-version check is a **hard gate** (PRD N4) — the +version bump that activates rejection of older data happens at release. diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/benchmarks/serialization/__init__.py b/benchmarks/serialization/__init__.py new file mode 100644 index 000000000000..dfb7d15ee589 --- /dev/null +++ b/benchmarks/serialization/__init__.py @@ -0,0 +1,10 @@ +"""Serialization benchmark harness for COMPAS core (PRD-serialization.md, phase 1). + +This package builds the measurement instrument the serialization PRD is decided +against: deterministic ``Mesh``/``Pointcloud`` fixtures at several sizes, a +pluggable registry of serialization formats, and metrics (size, serialize / +deserialize time, peak memory, round-trip fidelity). + +New formats (safe protobuf, Arrow/columnar) plug into ``formats.py`` without +touching the fixtures or the runner, so results stay comparable across phases. +""" diff --git a/benchmarks/serialization/fixtures.py b/benchmarks/serialization/fixtures.py new file mode 100644 index 000000000000..0cb9fe0efdbd --- /dev/null +++ b/benchmarks/serialization/fixtures.py @@ -0,0 +1,262 @@ +"""Deterministic, seeded fixtures for the serialization benchmark. + +Two subjects stand in for "large numeric payloads" (PRD 10.1): + +* ``Mesh`` -- vertices (float64 x3) + integer face indices. Generated as a jittered + grid so the vertex/face counts and coordinates are reproducible. A ``with_attributes`` + variant attaches a per-vertex float, exposing the hybrid-layout cost that a columnar + format cannot flatten. +* ``Pointcloud`` -- N points (float64 x3). The COMPAS ``Pointcloud`` type has no + per-point attribute slot, so only a points-only variant exists here; the Mesh + attribute variant carries the "with per-element attributes" case. + +All generators are seeded so runs are comparable and fixtures are reused across +every format under test. +""" + +import math +import random + +from compas.datastructures import Graph +from compas.datastructures import Mesh +from compas.geometry import Arc +from compas.geometry import Bezier +from compas.geometry import Box +from compas.geometry import Capsule +from compas.geometry import Circle +from compas.geometry import Cone +from compas.geometry import Cylinder +from compas.geometry import Ellipse +from compas.geometry import Frame +from compas.geometry import Hyperbola +from compas.geometry import Line +from compas.geometry import Parabola +from compas.geometry import Plane +from compas.geometry import Point +from compas.geometry import Pointcloud +from compas.geometry import Polygon +from compas.geometry import Polyhedron +from compas.geometry import Polyline +from compas.geometry import Projection +from compas.geometry import Quaternion +from compas.geometry import Reflection +from compas.geometry import Rotation +from compas.geometry import Scale +from compas.geometry import Shear +from compas.geometry import Sphere +from compas.geometry import Torus +from compas.geometry import Transformation +from compas.geometry import Translation +from compas.geometry import Vector + +DEFAULT_SEED = 42 + + +def make_mesh(n_vertices, with_attributes=False, seed=DEFAULT_SEED): + """Build a deterministic jittered-grid mesh with approximately ``n_vertices`` vertices. + + Parameters + ---------- + n_vertices : int + Target number of vertices. The mesh is a ``side x side`` grid with + ``side = round(sqrt(n_vertices))``, so the actual count is the nearest square. + with_attributes : bool, optional + If True, attach a seeded per-vertex float attribute (``quality``). + seed : int, optional + Seed for the coordinate jitter and attributes. + + Returns + ------- + :class:`compas.datastructures.Mesh` + """ + rng = random.Random(seed) + side = max(2, int(round(math.sqrt(n_vertices)))) + + vertices = [] + for y in range(side): + for x in range(side): + vertices.append([float(x), float(y), rng.uniform(-0.5, 0.5)]) + + faces = [] + for y in range(side - 1): + for x in range(side - 1): + i = y * side + x + faces.append([i, i + 1, i + 1 + side, i + side]) + + mesh = Mesh.from_vertices_and_faces(vertices, faces) + + if with_attributes: + for vertex in mesh.vertices(): + mesh.vertex_attribute(vertex, "quality", rng.uniform(0.0, 1.0)) + + return mesh + + +def make_pointcloud(n_points, seed=DEFAULT_SEED): + """Build a deterministic pointcloud of ``n_points`` float64 points. + + Parameters + ---------- + n_points : int + Number of points. + seed : int, optional + Seed for the point coordinates. + + Returns + ------- + :class:`compas.geometry.Pointcloud` + """ + rng = random.Random(seed) + points = [[rng.uniform(-100.0, 100.0), rng.uniform(-100.0, 100.0), rng.uniform(-100.0, 100.0)] for _ in range(n_points)] + return Pointcloud(points) + + +def make_graph(n_nodes, seed=DEFAULT_SEED): + """Build a deterministic jittered-grid graph with approximately ``n_nodes`` nodes. + + Nodes carry ``x``, ``y``, ``z`` coordinates; edges connect grid neighbours. + + Parameters + ---------- + n_nodes : int + Target number of nodes (nearest square of a ``side x side`` grid). + seed : int, optional + + Returns + ------- + :class:`compas.datastructures.Graph` + """ + rng = random.Random(seed) + side = max(2, int(round(math.sqrt(n_nodes)))) + graph = Graph() + keys = {} + for y in range(side): + for x in range(side): + keys[(x, y)] = graph.add_node(x=float(x), y=float(y), z=rng.uniform(-0.5, 0.5)) + for y in range(side): + for x in range(side): + if x < side - 1: + graph.add_edge(keys[(x, y)], keys[(x + 1, y)]) + if y < side - 1: + graph.add_edge(keys[(x, y)], keys[(x, y + 1)]) + return graph + + +def _make_primitive(kind, rng): + def coord(): + return [rng.uniform(-100.0, 100.0) for _ in range(3)] + + def frame(): + # Axis-aligned (only translated): a jittered frame re-normalizes with ~1e-16 error on + # round-trip (a COMPAS construction quirk, exercised by the `frames` subject itself), which + # would otherwise mask the losslessness of the shape/conic placed on it. + return Frame(coord(), [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) + + if kind == "point": + return Point(*coord()) + if kind == "vector": + return Vector(*coord()) + if kind == "line": + return Line(coord(), coord()) + if kind == "frame": + # near-orthonormal axes with jitter so the frame is well-conditioned + j = rng.uniform(-0.1, 0.1) + return Frame(coord(), [1.0, j, j], [j, 1.0, j]) + if kind == "plane": + return Plane(coord(), coord()) + if kind == "box": + f = Frame(coord(), [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) + return Box(rng.uniform(1.0, 10.0), rng.uniform(1.0, 10.0), rng.uniform(1.0, 10.0), frame=f) + if kind == "sphere": + return Sphere(rng.uniform(1.0, 10.0), point=Point(*coord())) + if kind == "circle": + f = Frame(coord(), [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) + return Circle(rng.uniform(1.0, 10.0), frame=f) + # Compound geometry — exercises the flat `repeated double` point arrays in compas_pb. + if kind == "polyline": + return Polyline([coord() for _ in range(8)]) + if kind == "polygon": + return Polygon([coord() for _ in range(6)]) + if kind == "bezier": + return Bezier([coord() for _ in range(4)]) + if kind == "polyhedron": + return Polyhedron([coord() for _ in range(4)], [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]) + if kind == "transformation": + return Transformation.from_frame(frame()) + # Conics + if kind == "arc": + return Arc(radius=rng.uniform(1.0, 9.0), start_angle=0.2, end_angle=1.5, frame=frame()) + if kind == "ellipse": + return Ellipse(major=rng.uniform(3.0, 9.0), minor=rng.uniform(1.0, 3.0), frame=frame()) + if kind == "parabola": + return Parabola(focal=rng.uniform(1.0, 5.0), frame=frame()) + if kind == "hyperbola": + return Hyperbola(major=rng.uniform(3.0, 9.0), minor=rng.uniform(1.0, 3.0), frame=frame()) + # Solids + if kind == "cylinder": + return Cylinder(radius=rng.uniform(1.0, 5.0), height=rng.uniform(2.0, 9.0), frame=frame()) + if kind == "cone": + return Cone(radius=rng.uniform(1.0, 5.0), height=rng.uniform(2.0, 9.0), frame=frame()) + if kind == "capsule": + return Capsule(radius=rng.uniform(1.0, 3.0), height=rng.uniform(2.0, 9.0), frame=frame()) + if kind == "torus": + return Torus(radius_axis=rng.uniform(3.0, 9.0), radius_pipe=rng.uniform(1.0, 2.0), frame=frame()) + # Quaternion + transformation subtypes (each has its own proto message) + if kind == "quaternion": + return Quaternion(1.0 + rng.uniform(-0.3, 0.3), *coord()).unitized() + if kind == "translation": + return Translation.from_vector(coord()) + if kind == "rotation": + return Rotation.from_axis_and_angle([0.0, 0.0, 1.0], rng.uniform(0.1, 3.0)) + if kind == "scale": + return Scale.from_factors([rng.uniform(0.5, 3.0) for _ in range(3)]) + if kind == "shear": + return Shear.from_angle_direction_plane(rng.uniform(0.1, 0.8), [1.0, 0.0, 0.0], Plane([0, 0, 0], [0, 0, 1])) + if kind == "reflection": + return Reflection.from_plane(Plane(coord(), [0.0, 0.0, 1.0])) + if kind == "projection": + return Projection.from_plane(Plane(coord(), [0.0, 0.0, 1.0])) + raise ValueError("Unknown primitive kind: {}".format(kind)) + + +def make_primitives(kind, count, seed=DEFAULT_SEED): + """Build a deterministic list of ``count`` primitives of the given ``kind``. + + Collections of primitives (e.g. a list of frames as robot targets) are a common + real-world payload; the harness measures the list as a whole. + + Parameters + ---------- + kind : str + One of point, vector, line, frame, plane, box, sphere, circle. + count : int + seed : int, optional + + Returns + ------- + list[:class:`compas.data.Data`] + """ + rng = random.Random(seed) + return [_make_primitive(kind, rng) for _ in range(count)] + + +_PRIMITIVE_KINDS = [ + "point", "vector", "line", "frame", "plane", "box", "sphere", "circle", + "polyline", "polygon", "bezier", "polyhedron", "transformation", + "arc", "ellipse", "parabola", "hyperbola", "cylinder", "cone", "capsule", "torus", + "quaternion", "translation", "rotation", "scale", "shear", "reflection", "projection", +] + + +# Subject catalogue: label -> factory(size, seed) returning a Data object (or list of them). +# Sizes are selected by the runner (see run.py PRESETS / DEFAULT_SIZES). +SUBJECTS = { + "mesh": lambda size, seed=DEFAULT_SEED: make_mesh(size, with_attributes=False, seed=seed), + "mesh_attrs": lambda size, seed=DEFAULT_SEED: make_mesh(size, with_attributes=True, seed=seed), + "pointcloud": lambda size, seed=DEFAULT_SEED: make_pointcloud(size, seed=seed), + "graph": lambda size, seed=DEFAULT_SEED: make_graph(size, seed=seed), +} +# One subject per primitive kind (pluralized label), each a list of `size` primitives. +for _kind in _PRIMITIVE_KINDS: + _label = _kind + ("es" if _kind.endswith(("x", "s")) else "s") + SUBJECTS[_label] = (lambda k: lambda size, seed=DEFAULT_SEED: make_primitives(k, size, seed=seed))(_kind) diff --git a/benchmarks/serialization/formats.py b/benchmarks/serialization/formats.py new file mode 100644 index 000000000000..cb73f648adfb --- /dev/null +++ b/benchmarks/serialization/formats.py @@ -0,0 +1,245 @@ +"""Registry of serialization formats under test. + +Each format is a :class:`Format` with ``dumps(obj) -> bytes`` and ``loads(bytes) -> obj``. +Phase 1 ships the two JSON baselines (compact text, and zip-compressed) that every +later format (safe protobuf, Arrow/columnar) is measured against. Register new +formats with :func:`register`; the runner picks them up automatically. + +Formats whose optional dependency (protobuf, pyarrow, ...) is missing should register +with ``available=False`` and be skipped by the runner rather than crashing it. +""" + +import io +import zipfile + +import compas + +_REGISTRY = {} + + +class Format(object): + """A named, round-trippable serialization format. + + Parameters + ---------- + name : str + Unique identifier used in result rows. + dumps : callable + ``obj -> bytes``. + loads : callable + ``bytes -> obj``. + available : bool, optional + False if a required optional dependency is missing; the runner skips it. + note : str, optional + Short human-readable description of the profile (e.g. "lossy float32"). + """ + + def __init__(self, name, dumps, loads, available=True, note=""): + self.name = name + self.dumps = dumps + self.loads = loads + self.available = available + self.note = note + + +def register(fmt): + _REGISTRY[fmt.name] = fmt + return fmt + + +def formats(): + """Return the registered formats in registration order.""" + return list(_REGISTRY.values()) + + +# --------------------------------------------------------------------------- +# JSON baselines (phase 1) +# --------------------------------------------------------------------------- + +def _json_compact_dumps(obj): + return compas.json_dumps(obj, compact=True).encode("utf-8") + + +def _json_compact_loads(blob): + return compas.json_loads(blob.decode("utf-8")) + + +def _json_zip_dumps(obj): + buffer = io.BytesIO() + compas.json_dumpz(obj, buffer, compact=True) + return buffer.getvalue() + + +def _json_zip_loads(blob): + return compas.json_loadz(io.BytesIO(blob)) + + +register(Format("json", _json_compact_dumps, _json_compact_loads, note="compact text, lossless")) +register(Format("json_zip", _json_zip_dumps, _json_zip_loads, note="zip-compressed json, size baseline")) + + +# --------------------------------------------------------------------------- +# Protobuf (compas_pb plugin, optional). Measured against the optimized branch: +# double precision + flat coordinate arrays + inline attribute maps. +# --------------------------------------------------------------------------- + +try: + import compas_pb # noqa: F401 + + _PB_AVAILABLE = True +except ImportError: + _PB_AVAILABLE = False + + +def _pb_dumps(obj): + from compas_pb import pb_dump_bts + + return pb_dump_bts(obj) + + +def _pb_loads(blob): + from compas_pb import pb_load_bts + + return pb_load_bts(blob) + + +def _pb_zip_dumps(obj): + buffer = io.BytesIO() + with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf: + zf.writestr("content.pb", _pb_dumps(obj)) + return buffer.getvalue() + + +def _pb_zip_loads(blob): + with zipfile.ZipFile(io.BytesIO(blob)) as zf: + return _pb_loads(zf.read("content.pb")) + + +register( + Format( + "compas_pb", + _pb_dumps, + _pb_loads, + available=_PB_AVAILABLE, + note="protobuf binary, double + flat arrays (optimized)", + ) +) +register( + Format( + "compas_pb_zip", + _pb_zip_dumps, + _pb_zip_loads, + available=_PB_AVAILABLE, + note="protobuf binary, zip-compressed", + ) +) + +try: + import zstandard # noqa: F401 + + _ZSTD_AVAILABLE = _PB_AVAILABLE +except ImportError: + _ZSTD_AVAILABLE = False + + +def _pb_zstd_dumps(obj): + import zstandard + + return zstandard.ZstdCompressor(level=10).compress(_pb_dumps(obj)) + + +def _pb_zstd_loads(blob): + import zstandard + + return _pb_loads(zstandard.ZstdDecompressor().decompress(blob)) + + +register( + Format( + "compas_pb_zstd", + _pb_zstd_dumps, + _pb_zstd_loads, + available=_ZSTD_AVAILABLE, + note="protobuf binary, zstandard-compressed", + ) +) + + +# --------------------------------------------------------------------------- +# MessagePack over the JSON-shape tree (the Kumiki-project approach, applied to COMPAS +# types via an msgspec enc_hook instead of Kumiki's own Serializable dataclasses). +# Binary but row-oriented / schemaless — a midpoint between JSON and the columnar compas_pb. +# --------------------------------------------------------------------------- + +try: + import msgspec # noqa: F401 + + _MSGPACK_AVAILABLE = True +except ImportError: + _MSGPACK_AVAILABLE = False + + +def _msgpack_enc_hook(obj): + # msgspec calls this for types it doesn't natively encode; COMPAS Data objects expose + # their {dtype, data, guid, name} dict via __jsondump__, and nested Data recurse the same way. + if hasattr(obj, "__jsondump__"): + return obj.__jsondump__() + raise NotImplementedError("Cannot msgpack-encode {}".format(type(obj))) + + +def _msgpack_reconstruct(node): + from compas.data.encoders import cls_from_dtype + + if isinstance(node, dict): + if "dtype" in node: + data = _msgpack_reconstruct(node["data"]) + cls = cls_from_dtype(node["dtype"], node.get("inheritance")) + return cls.__jsonload__(data, guid=node.get("guid"), name=node.get("name")) + return {key: _msgpack_reconstruct(value) for key, value in node.items()} + if isinstance(node, list): + return [_msgpack_reconstruct(item) for item in node] + return node + + +def _msgpack_dumps(obj): + import msgspec + + return msgspec.msgpack.encode(obj, enc_hook=_msgpack_enc_hook) + + +def _msgpack_loads(blob): + import msgspec + + return _msgpack_reconstruct(msgspec.msgpack.decode(blob)) + + +def _msgpack_zstd_dumps(obj): + import zstandard + + return zstandard.ZstdCompressor(level=10).compress(_msgpack_dumps(obj)) + + +def _msgpack_zstd_loads(blob): + import zstandard + + return _msgpack_loads(zstandard.ZstdDecompressor().decompress(blob)) + + +register( + Format( + "compas_msgpack", + _msgpack_dumps, + _msgpack_loads, + available=_MSGPACK_AVAILABLE, + note="msgpack over the JSON-shape tree (Kumiki-style)", + ) +) +register( + Format( + "compas_msgpack_zstd", + _msgpack_zstd_dumps, + _msgpack_zstd_loads, + available=_MSGPACK_AVAILABLE and _ZSTD_AVAILABLE, + note="msgpack, zstandard-compressed", + ) +) diff --git a/benchmarks/serialization/metrics.py b/benchmarks/serialization/metrics.py new file mode 100644 index 000000000000..fc80f18be667 --- /dev/null +++ b/benchmarks/serialization/metrics.py @@ -0,0 +1,129 @@ +"""Metrics for the serialization benchmark (PRD 10.3). + +For a given fixture object and format, :func:`measure` reports: + +* serialized size (bytes on the wire); +* serialize and deserialize time (median + spread over several runs); +* peak memory during deserialize (``tracemalloc``); +* round-trip fidelity: exact equality of ``__data__`` and of the format-independent + ``canonical_hash`` (this is where a lossy profile such as protobuf-float32 would show up). +""" + +import statistics +import time +import tracemalloc + + +def _time(callable_, repeat): + samples = [] + result = None + for _ in range(repeat): + start = time.perf_counter() + result = callable_() + samples.append(time.perf_counter() - start) + return result, samples + + +def _summarize(samples): + return { + "median_s": statistics.median(samples), + "min_s": min(samples), + "max_s": max(samples), + "stdev_s": statistics.pstdev(samples) if len(samples) > 1 else 0.0, + } + + +def _collect_numeric_pairs(a, b, out): + """Walk two same-shaped structures in parallel, collecting (original, roundtrip) numeric leaves. + + Returns False if the shapes diverge (e.g. a lossy format dropped keys or changed the + layout), in which case a coordinate error is not meaningful. + """ + if isinstance(a, bool) or isinstance(b, bool): + return a == b + if isinstance(a, dict) and isinstance(b, dict): + if set(a) != set(b): + return False + return all(_collect_numeric_pairs(a[k], b[k], out) for k in a) + if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)): + if len(a) != len(b): + return False + return all(_collect_numeric_pairs(x, y, out) for x, y in zip(a, b)) + if isinstance(a, (int, float)) and isinstance(b, (int, float)): + out.append((float(a), float(b))) + return True + return a == b + + +def numeric_error(original, roundtrip): + """Max-absolute and RMS error over paired numeric leaves of two ``__data__`` structures. + + This is where a lossy profile (e.g. protobuf float32) gets quantified (PRD 10.3). + Returns ``None`` errors when the structures are not comparable leaf-for-leaf. + """ + pairs = [] + if not _collect_numeric_pairs(original, roundtrip, pairs) or not pairs: + return {"max_abs_error": None, "rms_error": None} + diffs = [abs(x - y) for x, y in pairs] + rms = (sum(d * d for d in diffs) / len(diffs)) ** 0.5 + return {"max_abs_error": max(diffs), "rms_error": rms} + + +def _data_of(obj): + """__data__ of a single Data object, or a list of __data__ for a collection subject.""" + if isinstance(obj, (list, tuple)): + return [item.__data__ for item in obj] + return obj.__data__ + + +def _hash_of(obj): + """Canonical hash of a single Data object, or of each item for a collection subject.""" + if isinstance(obj, (list, tuple)): + return [item.canonical_hash() for item in obj] + return obj.canonical_hash() + + +def measure(fmt, obj, repeat=5): + """Serialize/deserialize ``obj`` with ``fmt`` and return a metrics dict. + + Parameters + ---------- + fmt : :class:`.formats.Format` + obj : :class:`compas.data.Data` + repeat : int, optional + Number of timed runs; the median and spread are reported. + + Returns + ------- + dict + """ + blob, dump_samples = _time(lambda: fmt.dumps(obj), repeat) + roundtrip, load_samples = _time(lambda: fmt.loads(blob), repeat) + + # Peak memory during a single, untimed deserialize (tracemalloc perturbs timing). + tracemalloc.start() + tracemalloc.reset_peak() + fmt.loads(blob) + _, peak_bytes = tracemalloc.get_traced_memory() + tracemalloc.stop() + + orig_data, rt_data = _data_of(obj), _data_of(roundtrip) + data_equal = orig_data == rt_data + hash_equal = _hash_of(obj) == _hash_of(roundtrip) + error = numeric_error(orig_data, rt_data) + + row = { + "format": fmt.name, + "size_bytes": len(blob), + "peak_mem_bytes": peak_bytes, + "lossless": bool(data_equal and hash_equal), + "data_equal": bool(data_equal), + "canonical_hash_equal": bool(hash_equal), + "max_abs_error": error["max_abs_error"], + "rms_error": error["rms_error"], + } + for key, value in _summarize(dump_samples).items(): + row["dump_" + key] = value + for key, value in _summarize(load_samples).items(): + row["load_" + key] = value + return row diff --git a/benchmarks/serialization/report.py b/benchmarks/serialization/report.py new file mode 100644 index 000000000000..02d5a486ec7e --- /dev/null +++ b/benchmarks/serialization/report.py @@ -0,0 +1,443 @@ +"""Render benchmark result rows as a self-contained, readable HTML report. + +The CSV is the machine record; this is the human view. One standalone ``.html`` file +(no external assets, theme-aware) with, per subject: grouped bars comparing formats at +each size on the metrics that matter (round-trip time and wire size), plus a full table. + +New formats (protobuf, Arrow, ...) need no changes here: colors are assigned to formats +in first-appearance order from a validated categorical palette, so the report grows with +the harness. +""" + +import datetime +import html +import json + +# Validated categorical palette (dataviz skill reference instance): (light, dark) per slot. +_SERIES = [ + ("#2a78d6", "#3987e5"), # blue + ("#1baf7a", "#199e70"), # aqua + ("#eda100", "#c98500"), # yellow + ("#008300", "#008300"), # green + ("#4a3aa7", "#9085e9"), # violet + ("#e34948", "#e66767"), # red + ("#e87ba4", "#d55181"), # magenta + ("#eb6834", "#d95926"), # orange +] + +_METRICS = [ + ("roundtrip_median_s", "Round-trip time", "time"), + ("size_bytes", "Wire size", "bytes"), +] + +# Client-side filter + summary re-render. __SLOTS__ is replaced with a {format: seriesSlot} map. +_FILTER_JS = """ +var SLOT = __SLOTS__; +var ROWS = JSON.parse(document.getElementById('rows-data').textContent); +function isCompressed(f){ return f.indexOf('zip') >= 0 || f.indexOf('zstd') >= 0; } +function inGroup(f, g){ return g === 'all' || (g === 'compressed') === isCompressed(f); } +function isPb(f){ return f.indexOf('compas_pb') === 0; } +function fmtInt(n){ return (+n).toLocaleString('en-US'); } +function fmtBytes(n){ var u=['B','KB','MB','GB'],i=0; n=+n; while(n>=1024&&i<3){n/=1024;i++;} return n.toFixed(1)+' '+u[i]; } +function fmtTime(s){ s=+s; return s<1 ? (s*1000).toFixed(1)+' ms' : s.toFixed(3)+' s'; } +function ratio(x){ return x.toFixed(1)+'\\u00d7'; } +function dot(f){ return ''; } +function median(a){ if(!a.length) return 0; var s=a.slice().sort(function(x,y){return x-y;}); var m=Math.floor(s.length/2); return s.length%2 ? s[m] : (s[m-1]+s[m])/2; } +function range(a){ return 'range '+Math.min.apply(null,a).toFixed(1)+'\\u2013'+Math.max.apply(null,a).toFixed(1)+'\\u00d7 across '+a.length+' subjects'; } +function currentGroup(){ var b=document.querySelector('.segmented button.active'); return b ? b.getAttribute('data-value') : 'all'; } +function minBy(a, f){ return a.reduce(function(x,y){return f(y)
'+big+'
'+lbl+'
'+sub+'
'; } +function factor(r, better, worse){ return r>=1 ? ratio(r)+' '+better : ratio(1/r)+' '+worse; } + +function renderSummary(rows){ + var subjects=[]; rows.forEach(function(r){ if(subjects.indexOf(r.subject)<0) subjects.push(r.subject); }); + var baseName = rows.some(function(r){return r.format==='json';}) ? 'json' + : (rows.some(function(r){return r.format==='json_zip';}) ? 'json_zip' : null); + var baseLabel = baseName || 'baseline'; + var per = subjects.map(function(subj){ + var sr = rows.filter(function(r){return r.subject===subj;}); + var maxSize = Math.max.apply(null, sr.map(function(r){return r.size;})); + return { subj: subj, size: maxSize, group: sr.filter(function(r){return r.size===maxSize;}) }; + }); + var sizeRatios=[], speedRatios=[], smallWins=0, fastWins=0, winTot=0; + per.forEach(function(p){ + var base = p.group.filter(function(r){return r.format===baseName;})[0]; + var pbs = p.group.filter(function(r){return isPb(r.format);}); + if(base && pbs.length){ + sizeRatios.push(base.size_bytes / minBy(pbs, function(r){return r.size_bytes;}).size_bytes); + speedRatios.push(base.roundtrip / minBy(pbs, function(r){return r.roundtrip;}).roundtrip); + winTot++; + if(isPb(minBy(p.group, function(r){return r.size_bytes;}).format)) smallWins++; + if(isPb(minBy(p.group, function(r){return r.roundtrip;}).format)) fastWins++; + } + }); + var tiles=''; + if(sizeRatios.length){ var ms=median(sizeRatios); tiles+=tile(factor(ms,'smaller','larger'), ms>=1?' win':'', 'median wire size vs '+baseLabel, range(sizeRatios)); } + if(speedRatios.length){ var mt=median(speedRatios); tiles+=tile(factor(mt,'faster','slower'), mt>=1?' win':'', 'median round-trip vs '+baseLabel, range(speedRatios)); } + if(winTot) tiles+=tile(smallWins+'/'+winTot, (smallWins===winTot?' win':''), 'subjects where compas_pb is smallest', 'and fastest to load on '+fastWins+'/'+winTot); + var head='subjectelementssmallestvs '+baseLabel + +'fastest round-tripvs '+baseLabel+'lossless'; + var body=''; + per.forEach(function(p){ + if(!p.group.length) return; + var base = p.group.filter(function(r){return r.format===baseName;})[0]; + var smallest = minBy(p.group, function(r){return r.size_bytes;}); + var fastest = minBy(p.group, function(r){return r.roundtrip;}); + var sr = base ? (base.size_bytes/smallest.size_bytes).toFixed(2)+'\\u00d7' : '\\u2014'; + var spr = base ? (base.roundtrip/fastest.roundtrip).toFixed(2)+'\\u00d7' : '\\u2014'; + var pb = p.group.filter(function(r){return isPb(r.format);})[0]; + var badge = pb ? (pb.lossless ? '\\u2713 yes' : '\\u2717 no') : '\\u2014'; + body+=''+p.subj+''+fmtInt(p.size)+'' + +''+dot(smallest.format)+smallest.format+' \\u00b7 '+fmtBytes(smallest.size_bytes)+''+sr+'' + +''+dot(fastest.format)+fastest.format+' \\u00b7 '+fmtTime(fastest.roundtrip)+''+spr+'' + +''+badge+''; + }); + return '

Summary

'+tiles+'
'+head+body+'
'; +} + +function applyFilter(){ + var g = currentGroup(); + document.querySelectorAll('[data-format]').forEach(function(el){ + el.style.display = inGroup(el.getAttribute('data-format'), g) ? '' : 'none'; + }); + document.querySelectorAll('.sizegroup').forEach(function(sg){ + var fills = [].slice.call(sg.querySelectorAll('.bar-row')).filter(function(r){return r.style.display!=='none';}) + .map(function(r){return r.querySelector('.fill');}); + var max = Math.max.apply(null, fills.map(function(f){return parseFloat(f.dataset.value);}).concat([0])); + fills.forEach(function(f){ f.style.width = max>0 ? (parseFloat(f.dataset.value)/max*100).toFixed(1)+'%' : '0%'; }); + }); + document.getElementById('summary-body').innerHTML = renderSummary(ROWS.filter(function(r){return inGroup(r.format, g);})); +} +document.querySelectorAll('.segmented button').forEach(function(b){ + b.addEventListener('click', function(){ + document.querySelectorAll('.segmented button').forEach(function(x){ x.classList.remove('active'); }); + b.classList.add('active'); + applyFilter(); + }); +}); +applyFilter(); +""" + + +def _fmt_int(n): + return "{:,}".format(int(n)) + + +def _fmt_bytes(n): + n = float(n) + for unit in ["B", "KB", "MB", "GB"]: + if n < 1024 or unit == "GB": + return "{:.1f} {}".format(n, unit) + n /= 1024.0 + + +def _fmt_time(seconds): + seconds = float(seconds) + if seconds < 1.0: + return "{:.1f} ms".format(seconds * 1000.0) + return "{:.3f} s".format(seconds) + + +def _fmt_value(value, kind): + return _fmt_bytes(value) if kind == "bytes" else _fmt_time(value) + + +def _fmt_error(value): + if value in (None, "", "None"): + return "—" + value = float(value) + if value == 0.0: + return "0" + return "{:.1e}".format(value) + + +def _color_map(rows): + order = [] + for r in rows: + if r["format"] not in order: + order.append(r["format"]) + return {name: _SERIES[i % len(_SERIES)] for i, name in enumerate(order)} + + +def _css(colors): + series_light = "\n".join(" --series-{}: {};".format(i + 1, lo) for i, (lo, _) in enumerate(_SERIES)) + series_dark = "\n".join(" --series-{}: {};".format(i + 1, hi) for i, (_, hi) in enumerate(_SERIES)) + return """ +:root { + --page: #f9f9f7; --surface: #fcfcfb; + --text-primary: #0b0b0b; --text-secondary: #52514e; --muted: #898781; + --grid: #e1e0d9; --track: #efeee9; --border: rgba(11,11,11,0.10); + --good: #006300; --critical: #d03b3b; +%SERIES_LIGHT% +} +@media (prefers-color-scheme: dark) { + :root { + --page: #0d0d0d; --surface: #1a1a19; + --text-primary: #ffffff; --text-secondary: #c3c2b7; --muted: #898781; + --grid: #2c2c2a; --track: #232322; --border: rgba(255,255,255,0.10); + --good: #0ca30c; --critical: #e66767; +%SERIES_DARK% + } +} +:root[data-theme="light"] { + --page:#f9f9f7; --surface:#fcfcfb; --text-primary:#0b0b0b; --text-secondary:#52514e; + --grid:#e1e0d9; --track:#efeee9; --border:rgba(11,11,11,0.10); +} +:root[data-theme="dark"] { + --page:#0d0d0d; --surface:#1a1a19; --text-primary:#ffffff; --text-secondary:#c3c2b7; + --grid:#2c2c2a; --track:#232322; --border:rgba(255,255,255,0.10); +} + +* { box-sizing: border-box; } +body { margin: 0; background: var(--page); color: var(--text-primary); + font-family: system-ui, -apple-system, "Segoe UI", sans-serif; line-height: 1.5; } +.wrap { max-width: 980px; margin: 0 auto; padding: 32px 20px 64px; } +h1 { font-size: 22px; margin: 0 0 4px; } +h2 { font-size: 17px; margin: 40px 0 12px; padding-top: 16px; border-top: 1px solid var(--grid); } +.meta { color: var(--text-secondary); font-size: 13px; margin: 0 0 8px; } +.legend { display: flex; flex-wrap: wrap; gap: 14px; margin: 16px 0 8px; } +.chip { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text-secondary); } +.dot { width: 11px; height: 11px; border-radius: 3px; flex: none; } +.metric-title { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin: 18px 0 8px; } +.sizegroup { margin: 0 0 12px; } +.sizelabel { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; margin: 0 0 4px; } +.bar-row { display: grid; grid-template-columns: 96px 1fr auto; align-items: center; gap: 10px; padding: 3px 0; } +.bar-name { font-size: 12px; color: var(--text-secondary); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.track { background: var(--track); border-radius: 4px; height: 14px; overflow: hidden; } +.fill { height: 100%; border-radius: 4px; } +.bar-val { font-size: 12px; font-variant-numeric: tabular-nums; color: var(--text-primary); white-space: nowrap; } +.tablewrap { overflow-x: auto; margin-top: 10px; border: 1px solid var(--border); border-radius: 8px; } +table { border-collapse: collapse; width: 100%; font-size: 13px; background: var(--surface); } +th, td { text-align: right; padding: 7px 12px; white-space: nowrap; font-variant-numeric: tabular-nums; } +th { color: var(--muted); font-weight: 600; border-bottom: 1px solid var(--grid); text-align: right; } +th:first-child, td:first-child, th:nth-child(2), td:nth-child(2) { text-align: left; } +tr + tr td { border-top: 1px solid var(--grid); } +.fmt-cell { display: inline-flex; align-items: center; gap: 7px; } +.badge { font-size: 12px; font-weight: 600; } +.badge.ok { color: var(--good); } +.badge.no { color: var(--critical); } +.note { color: var(--muted); font-size: 12px; } +.summary { margin: 20px 0 8px; } +.takeaway { font-size: 15px; line-height: 1.55; margin: 0 0 18px; color: var(--text-primary); } +.takeaway b { font-weight: 600; } +.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 0 0 20px; } +.tile { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; } +.tile .big { font-size: 26px; font-weight: 650; letter-spacing: -0.01em; } +.tile .lbl { font-size: 12px; color: var(--text-secondary); margin-top: 3px; } +.tile .sub { font-size: 11px; color: var(--muted); margin-top: 1px; } +.win { color: var(--good); font-weight: 600; } +h2.section { margin-top: 8px; } +.controls { margin: 14px 0 4px; display: flex; align-items: center; gap: 12px; } +.controls-label { font-size: 13px; color: var(--text-secondary); } +.segmented { display: inline-flex; background: var(--track); border: 1px solid var(--border); + border-radius: 9px; padding: 2px; gap: 2px; } +.segmented button { font: inherit; font-size: 13px; color: var(--text-secondary); cursor: pointer; + background: transparent; border: 0; border-radius: 7px; padding: 5px 14px; line-height: 1.4; } +.segmented button:hover { color: var(--text-primary); } +.segmented button.active { background: var(--surface); color: var(--text-primary); font-weight: 600; + box-shadow: 0 1px 2px rgba(0,0,0,0.10); } +.tile .rng { font-size: 11px; color: var(--muted); margin-top: 2px; font-variant-numeric: tabular-nums; } +.coverage { margin: 18px 0 0; font-size: 13px; color: var(--text-secondary); + background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 12px 16px; } +.coverage b { color: var(--text-primary); font-weight: 650; } +.coverage .miss { color: var(--muted); font-size: 12px; } +""".replace("%SERIES_LIGHT%", series_light).replace("%SERIES_DARK%", series_dark) + + +def _slot(colors, fmt): + # 1-based series index matching --series-N + return list(colors).index(fmt) % len(_SERIES) + 1 + + +def _legend(rows, colors): + parts = ['
'] + seen = [] + for r in rows: + if r["format"] in seen: + continue + seen.append(r["format"]) + slot = _slot(colors, r["format"]) + parts.append( + '' + "{fmt} · {note}".format( + slot=slot, fmt=html.escape(r["format"]), note=html.escape(r.get("note", "")) + ) + ) + parts.append("
") + return "".join(parts) + + +def _bars(subject_rows, colors, metric_key, kind): + # group by size (preserve order of first appearance) + sizes = [] + for r in subject_rows: + if r["size"] not in sizes: + sizes.append(r["size"]) + + blocks = [] + for size in sizes: + group = [r for r in subject_rows if r["size"] == size] + group_max = max(float(r[metric_key]) for r in group) or 1.0 + rows_html = [] + for r in group: + width = float(r[metric_key]) / group_max * 100.0 + slot = _slot(colors, r["format"]) + rows_html.append( + '
' + '
{name}
' + '
' + '
{val}
' + "
".format( + name=html.escape(r["format"]), + w=width, + s=slot, + dv=float(r[metric_key]), + val=_fmt_value(r[metric_key], kind), + ) + ) + blocks.append( + '
{lbl} elements
{rows}
'.format( + lbl=_fmt_int(size), rows="".join(rows_html) + ) + ) + return "".join(blocks) + + +def _table(subject_rows, colors): + head = ( + "sizeformatwire sizevs JSON" + "dumploadround-tripload MB/speak mem" + "max errlossless" + ) + body = [] + for r in subject_rows: + slot = _slot(colors, r["format"]) + lossless = str(r["lossless"]).lower() in ("true", "1") + badge = '✓ yes' if lossless else '✗ no' + body.append( + '' + "{size}" + '{fmt}' + "{wire}{ratio}×" + "{dump}{load}{trip}" + "{mbps}{mem}{err}{badge}" + "".format( + size=_fmt_int(r["size"]), + slot=slot, + fmt=html.escape(r["format"]), + wire=_fmt_bytes(r["size_bytes"]), + ratio=r["compression_vs_json"], + dump=_fmt_time(r["dump_median_s"]), + load=_fmt_time(r["load_median_s"]), + trip=_fmt_time(r["roundtrip_median_s"]), + mbps=r["load_mb_s"], + mem=_fmt_bytes(r["peak_mem_bytes"]), + err=_fmt_error(r.get("max_abs_error")), + badge=badge, + ) + ) + return '
{}{}
'.format(head, "".join(body)) + + +def _coverage_banner(coverage): + """Static banner: how many of compas_pb's serializable types the corpus benchmarks.""" + if not coverage: + return "" + n, total = coverage["benchmarked"], coverage["serializable"] + missing = coverage.get("missing") or [] + if missing: + tail = ' not yet covered: {}'.format(html.escape(", ".join(missing))) + else: + tail = " — full coverage." + return '
{}/{} of compas_pb\'s serializable types are benchmarked.{}
'.format( + n, total, tail + ) + + +def build_html(rows, meta=None): + """Return a full standalone HTML document for the given result rows. + + Parameters + ---------- + rows : list[dict] + Result rows as produced by :func:`benchmarks.serialization.run.run`. + meta : dict, optional + Run metadata (preset, repeat, seed, ...) shown in the header. + + Returns + ------- + str + """ + meta = meta or {} + colors = _color_map(rows) + + subjects = [] + for r in rows: + if r["subject"] not in subjects: + subjects.append(r["subject"]) + + meta_bits = ["generated {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))] + for key in ("preset", "repeat", "seed", "compas"): + if key in meta: + meta_bits.append("{} {}".format(key, meta[key])) + + controls = ( + '
Show formats' + '
' + '' + '' + '' + "
" + ) + sections = [ + controls, + _coverage_banner(meta.get("coverage")), + '
', + _legend(rows, colors), + ] + for subject in subjects: + subject_rows = [r for r in rows if r["subject"] == subject] + sections.append("

{}

".format(html.escape(subject))) + for metric_key, title, kind in _METRICS: + sections.append('
{}
'.format(html.escape(title))) + sections.append(_bars(subject_rows, colors, metric_key, kind)) + sections.append(_table(subject_rows, colors)) + + data_rows = [ + { + "subject": r["subject"], + "size": int(r["size"]), + "format": r["format"], + "size_bytes": int(r["size_bytes"]), + "roundtrip": float(r["roundtrip_median_s"]), + "lossless": str(r["lossless"]).lower() in ("true", "1"), + } + for r in rows + ] + slot_map = {r["format"]: _slot(colors, r["format"]) for r in rows} + script = ( + '' + "" + ).format(data=json.dumps(data_rows), js=_FILTER_JS.replace("__SLOTS__", json.dumps(slot_map))) + + return ( + "" + "" + "COMPAS serialization benchmark" + "
" + "

COMPAS serialization benchmark

" + "

{meta}

" + "{body}{script}" + "
" + ).format( + css=_css(colors), + meta=html.escape(" · ".join(meta_bits)), + body="".join(sections), + script=script, + ) + + +def write_html(rows, out_path, meta=None): + with open(out_path, "w") as f: + f.write(build_html(rows, meta)) + return out_path diff --git a/benchmarks/serialization/results/baseline_full.csv b/benchmarks/serialization/results/baseline_full.csv new file mode 100644 index 000000000000..b296c8ab63d0 --- /dev/null +++ b/benchmarks/serialization/results/baseline_full.csv @@ -0,0 +1,85 @@ +subject,size,format,size_bytes,compression_vs_json,dump_median_s,dump_stdev_s,load_median_s,load_stdev_s,roundtrip_median_s,dump_mb_s,load_mb_s,peak_mem_bytes,lossless,data_equal,canonical_hash_equal,max_abs_error,rms_error,note +mesh,1000,json,82154,1.0,0.001588,7e-05,0.003597,4.3e-05,0.005186,51.719,22.839,1611855,True,True,True,0.0,0.0,"compact text, lossless" +mesh,1000,json_zip,28054,2.928,0.00363,2.4e-05,0.003815,2.9e-05,0.007445,7.728,7.354,1614528,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh,1000,compas_pb,32913,2.496,0.018644,0.016929,0.006108,0.002727,0.024752,1.765,5.388,878003,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh,1000,compas_pb_zip,16331,5.031,0.002269,4.7e-05,0.003461,1.4e-05,0.00573,7.197,4.718,912312,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh,1000,compas_pb_zstd,16203,5.07,0.002115,3.5e-05,0.003406,3.4e-05,0.00552,7.661,4.758,910949,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh,1000,compas_msgpack,58668,1.4,0.000451,1.7e-05,0.007539,3e-06,0.00799,130.018,7.782,2063130,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh,1000,compas_msgpack_zstd,25134,3.269,0.001035,2.3e-05,0.007638,2.4e-05,0.008673,24.288,3.291,2121831,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh,100000,json,9728057,1.0,0.245125,0.001937,0.511089,0.017704,0.756215,39.686,19.034,180545434,True,True,True,0.0,0.0,"compact text, lossless" +mesh,100000,json_zip,2735132,3.557,0.505539,0.012054,0.492855,0.001524,0.998394,5.41,5.55,180547939,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh,100000,compas_pb,3621642,2.686,0.174104,0.000909,0.415152,0.036329,0.589256,20.802,8.724,96717179,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh,100000,compas_pb_zip,1593475,6.105,0.244928,0.004265,0.407833,0.001066,0.65276,6.506,3.907,100340217,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh,100000,compas_pb_zstd,1511414,6.436,0.208225,0.001305,0.4214,0.022523,0.629625,7.259,3.587,100338854,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh,100000,compas_msgpack,6812720,1.428,0.053044,0.000329,0.917587,0.042876,0.970632,128.435,7.425,230247254,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh,100000,compas_msgpack_zstd,2316762,4.199,0.130802,0.000602,0.95081,0.008568,1.081612,17.712,2.437,237058399,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh,1000000,json,105070316,1.0,3.32282,0.001914,5.268474,0.069022,8.591294,31.621,19.943,1734567813,True,True,True,0.0,0.0,"compact text, lossless" +mesh,1000000,json_zip,25633083,4.099,5.381697,0.173626,5.570754,0.106143,10.952451,4.763,4.601,1734570478,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh,1000000,compas_pb,36910415,2.847,1.730868,0.003477,4.194566,0.175501,5.925434,21.325,8.8,928274235,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh,1000000,compas_pb_zip,16157397,6.503,2.456532,0.022398,4.281049,0.372908,6.737581,6.577,3.774,965186046,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh,1000000,compas_pb_zstd,15116181,6.951,2.2248,0.036781,4.242941,0.220328,6.467741,6.794,3.563,965184683,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh,1000000,compas_msgpack,76074466,1.381,0.586803,0.006091,9.717308,0.370121,10.304111,129.642,7.829,2206637440,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh,1000000,compas_msgpack_zstd,16241122,6.469,1.305171,0.010959,9.866773,0.167038,11.171945,12.444,1.646,2282711939,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh_attrs,1000,json,112098,1.0,0.001949,8.4e-05,0.003888,2.6e-05,0.005836,57.526,28.834,1666431,True,True,True,0.0,0.0,"compact text, lossless" +mesh_attrs,1000,json_zip,38108,2.942,0.004446,8.4e-05,0.003928,4.6e-05,0.008374,8.572,9.701,1668904,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh_attrs,1000,compas_pb,41120,2.726,0.00201,7.8e-05,0.003411,1.7e-05,0.005421,20.457,12.054,967891,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh_attrs,1000,compas_pb_zip,24312,4.611,0.002646,1.9e-05,0.003525,7.7e-05,0.006171,9.189,6.897,1010407,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh_attrs,1000,compas_pb_zstd,24277,4.617,0.00233,3e-06,0.003464,6.9e-05,0.005794,10.417,7.009,1009044,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh_attrs,1000,compas_msgpack,76076,1.474,0.000452,1.9e-05,0.007962,1.9e-05,0.008413,168.411,9.555,2087922,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh_attrs,1000,compas_msgpack_zstd,33792,3.317,0.001278,2.1e-05,0.008068,2.8e-05,0.009345,26.45,4.188,2164031,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh_attrs,100000,json,12650695,1.0,0.262538,0.01947,0.498159,0.001606,0.760697,48.186,25.395,185864672,True,True,True,0.0,0.0,"compact text, lossless" +mesh_attrs,100000,json_zip,3751837,3.372,0.595554,0.014026,0.512142,0.003597,1.107696,6.3,7.326,185867345,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh_attrs,100000,compas_pb,4420507,2.862,0.210594,0.000532,0.431852,0.026599,0.642446,20.991,10.236,105504283,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh_attrs,100000,compas_pb_zip,2347229,5.39,0.291434,0.000396,0.428419,0.029657,0.719852,8.054,5.479,109926186,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh_attrs,100000,compas_pb_zstd,2260734,5.596,0.240334,0.001115,0.422614,0.037193,0.662948,9.407,5.349,109924823,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh_attrs,100000,compas_msgpack,8510272,1.487,0.053848,0.000763,0.955974,0.028654,1.009823,158.041,8.902,232653822,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh_attrs,100000,compas_msgpack_zstd,3018129,4.192,0.153727,0.003384,0.987908,0.025667,1.141635,19.633,3.055,241162519,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh_attrs,1000000,json,134340069,1.0,3.630149,0.125062,5.784048,0.176622,9.414196,37.007,23.226,1787837790,True,True,True,0.0,0.0,"compact text, lossless" +mesh_attrs,1000000,json_zip,35931527,3.739,8.309961,0.46875,9.516919,1.091954,17.826881,4.324,3.776,1787840287,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh_attrs,1000000,compas_pb,44910434,2.991,2.157818,0.006395,4.291322,0.032096,6.44914,20.813,10.465,1016274011,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh_attrs,1000000,compas_pb_zip,23702009,5.668,2.989804,0.029878,4.278502,0.350805,7.268306,7.928,5.54,1061185841,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh_attrs,1000000,compas_pb_zstd,22625801,5.937,2.604904,0.086562,4.366527,0.206522,6.97143,8.686,5.182,1061184478,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh_attrs,1000000,compas_msgpack,93074466,1.443,0.646555,0.043181,10.293254,0.415641,10.939809,143.954,9.042,2230747928,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh_attrs,1000000,compas_msgpack_zstd,24043399,5.587,1.560466,0.020783,10.374759,0.197704,11.935225,15.408,2.317,2323822427,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +pointcloud,10000,json,580633,1.0,0.013419,2.9e-05,0.017131,0.003778,0.03055,43.27,33.893,4261534,True,True,True,0.0,0.0,"compact text, lossless" +pointcloud,10000,json_zip,280731,2.068,0.033451,4.6e-05,0.018083,0.003665,0.051534,8.392,15.524,4264751,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +pointcloud,10000,compas_pb,240074,2.419,0.003116,1.6e-05,0.013894,0.003616,0.01701,77.041,17.279,3599350,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +pointcloud,10000,compas_pb_zip,228795,2.538,0.009403,7.1e-05,0.014358,0.003481,0.023762,24.331,15.935,3841460,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +pointcloud,10000,compas_pb_zstd,227771,2.549,0.003473,0.000233,0.013189,0.002907,0.016662,65.581,17.27,3839457,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +pointcloud,10000,compas_msgpack,280093,2.073,0.003596,8.8e-05,0.030573,0.002846,0.034169,77.887,9.161,4564763,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +pointcloud,10000,compas_msgpack_zstd,250823,2.315,0.0049,0.000404,0.029857,0.003191,0.034757,51.188,8.401,4844841,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +pointcloud,100000,json,5804860,1.0,0.143278,0.000924,0.176979,0.001365,0.320257,40.515,32.8,42602609,True,True,True,0.0,0.0,"compact text, lossless" +pointcloud,100000,json_zip,2792759,2.079,0.345942,0.001727,0.194397,0.001754,0.540338,8.073,14.366,42604858,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +pointcloud,100000,compas_pb,2400078,2.419,0.030712,0.000118,0.156316,0.022119,0.187028,78.148,15.354,35991070,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +pointcloud,100000,compas_pb_zip,2286030,2.539,0.094568,0.000259,0.151686,0.001758,0.246254,24.174,15.071,38397656,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +pointcloud,100000,compas_pb_zstd,2276982,2.549,0.033546,0.00011,0.153148,0.016629,0.186694,67.876,14.868,38396589,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +pointcloud,100000,compas_msgpack,2800095,2.073,0.047455,0.00034,0.336245,0.013206,0.3837,59.006,8.328,45600139,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +pointcloud,100000,compas_msgpack_zstd,2457514,2.362,0.080387,0.002344,0.343124,0.017659,0.423511,30.571,7.162,48398819,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +pointcloud,1000000,json,58048950,1.0,1.605381,0.005699,1.930924,0.260703,3.536305,36.159,30.063,426937003,True,True,True,0.0,0.0,"compact text, lossless" +pointcloud,1000000,json_zip,27911105,2.08,3.580347,0.042655,1.959775,0.070332,5.540122,7.796,14.242,426944436,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +pointcloud,1000000,compas_pb,24000078,2.419,0.309769,0.008174,1.556407,0.059545,1.866175,77.477,15.42,360891966,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +pointcloud,1000000,compas_pb_zip,22857818,2.54,0.934602,0.009969,1.58929,0.008555,2.523892,24.457,14.382,384893144,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +pointcloud,1000000,compas_pb_zstd,22767197,2.55,0.336973,0.007113,1.633115,0.073565,1.970088,67.564,13.941,384892077,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +pointcloud,1000000,compas_msgpack,28000095,2.073,0.560265,0.006441,3.560925,0.09335,4.12119,49.976,7.863,457341976,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +pointcloud,1000000,compas_msgpack_zstd,24349903,2.384,0.986783,0.008256,3.48896,0.001671,4.475743,24.676,6.979,485342104,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +graph,1000,json,76719,1.0,0.001676,7.1e-05,0.011913,0.00024,0.013589,45.769,6.44,1946545,True,True,True,0.0,0.0,"compact text, lossless" +graph,1000,json_zip,24328,3.154,0.003359,1.6e-05,0.011612,6e-06,0.014971,7.242,2.095,1940890,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +graph,1000,compas_pb,37178,2.064,0.004242,0.00012,0.006352,0.00266,0.010594,8.763,5.853,1115748,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +graph,1000,compas_pb_zip,15463,4.961,0.004638,3.1e-05,0.003813,3.4e-05,0.00845,3.334,4.056,1154254,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +graph,1000,compas_pb_zstd,16070,4.774,0.004366,3.3e-05,0.003937,0.000115,0.008304,3.68,4.081,1152895,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +graph,1000,compas_msgpack,53875,1.424,0.000606,1.3e-05,0.017286,0.002833,0.017893,88.838,3.117,2548305,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +graph,1000,compas_msgpack_zstd,17743,4.324,0.001223,1.5e-05,0.016902,0.002493,0.018125,14.503,1.05,2600925,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +graph,100000,json,8483783,1.0,0.332398,0.027313,1.439306,0.044717,1.771703,25.523,5.894,204954762,True,True,True,0.0,0.0,"compact text, lossless" +graph,100000,json_zip,2308218,3.675,0.473422,0.02583,1.432786,0.061999,1.906208,4.876,1.611,204957379,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +graph,100000,compas_pb,4108305,2.065,0.439747,0.003477,0.471499,0.01639,0.911245,9.342,8.713,117513572,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +graph,100000,compas_pb_zip,1382755,6.135,0.505525,0.000913,0.466193,0.021047,0.971718,2.735,2.966,121623273,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +graph,100000,compas_pb_zstd,1264473,6.709,0.489068,0.024084,0.444738,0.010886,0.933806,2.585,2.843,121621910,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +graph,100000,compas_msgpack,6043358,1.404,0.113682,0.001766,1.837127,0.022641,1.950809,53.16,3.29,269977086,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +graph,100000,compas_msgpack_zstd,1411522,6.01,0.192514,0.00127,1.872703,0.099241,2.065217,7.332,0.754,276020309,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +graph,1000000,json,89478080,1.0,3.885734,0.281012,15.773096,0.453129,19.658831,23.027,5.673,1995674835,True,True,True,0.0,0.0,"compact text, lossless" +graph,1000000,json_zip,22390927,3.996,4.924488,0.190411,15.561772,0.586597,20.48626,4.547,1.439,1995677452,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +graph,1000000,compas_pb,41906767,2.135,4.646358,0.06462,4.916721,0.120458,9.563079,9.019,8.523,1147172668,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +graph,1000000,compas_pb_zip,14058479,6.365,5.357445,0.044157,4.987806,0.117503,10.345251,2.624,2.819,1189080831,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +graph,1000000,compas_pb_zstd,13732328,6.516,4.797572,0.040191,5.197863,0.092214,9.995435,2.862,2.642,1189079468,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +graph,1000000,compas_msgpack,64543009,1.386,1.731121,0.041981,19.520213,0.714578,21.251333,37.284,3.306,2648860138,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +graph,1000000,compas_msgpack_zstd,10528231,8.499,2.272619,0.147677,19.818832,0.568671,22.091451,4.633,0.531,2713287201,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" diff --git a/benchmarks/serialization/results/baseline_full.html b/benchmarks/serialization/results/baseline_full.html new file mode 100644 index 000000000000..e76db6ab8102 --- /dev/null +++ b/benchmarks/serialization/results/baseline_full.html @@ -0,0 +1,175 @@ +COMPAS serialization benchmark

COMPAS serialization benchmark

generated 2026-07-10 19:56 · preset full · repeat 2 · seed 42 · compas 2.15.0-f951f1e4

Show formats
json · compact text, losslessjson_zip · zip-compressed json, size baselinecompas_pb · protobuf binary, double + flat arrays (optimized)compas_pb_zip · protobuf binary, zip-compressedcompas_pb_zstd · protobuf binary, zstandard-compressedcompas_msgpack · msgpack over the JSON-shape tree (Kumiki-style)compas_msgpack_zstd · msgpack, zstandard-compressed

mesh

Round-trip time
1,000 elements
json
5.2 ms
json_zip
7.4 ms
compas_pb
24.8 ms
compas_pb_zip
5.7 ms
compas_pb_zstd
5.5 ms
compas_msgpack
8.0 ms
compas_msgpack_zstd
8.7 ms
100,000 elements
json
756.2 ms
json_zip
998.4 ms
compas_pb
589.3 ms
compas_pb_zip
652.8 ms
compas_pb_zstd
629.6 ms
compas_msgpack
970.6 ms
compas_msgpack_zstd
1.082 s
1,000,000 elements
json
8.591 s
json_zip
10.952 s
compas_pb
5.925 s
compas_pb_zip
6.738 s
compas_pb_zstd
6.468 s
compas_msgpack
10.304 s
compas_msgpack_zstd
11.172 s
Wire size
1,000 elements
json
80.2 KB
json_zip
27.4 KB
compas_pb
32.1 KB
compas_pb_zip
15.9 KB
compas_pb_zstd
15.8 KB
compas_msgpack
57.3 KB
compas_msgpack_zstd
24.5 KB
100,000 elements
json
9.3 MB
json_zip
2.6 MB
compas_pb
3.5 MB
compas_pb_zip
1.5 MB
compas_pb_zstd
1.4 MB
compas_msgpack
6.5 MB
compas_msgpack_zstd
2.2 MB
1,000,000 elements
json
100.2 MB
json_zip
24.4 MB
compas_pb
35.2 MB
compas_pb_zip
15.4 MB
compas_pb_zstd
14.4 MB
compas_msgpack
72.6 MB
compas_msgpack_zstd
15.5 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json80.2 KB1.0×1.6 ms3.6 ms5.2 ms22.8391.5 MB0✓ yes
1,000json_zip27.4 KB2.928×3.6 ms3.8 ms7.4 ms7.3541.5 MB0✓ yes
1,000compas_pb32.1 KB2.496×18.6 ms6.1 ms24.8 ms5.388857.4 KB0✓ yes
1,000compas_pb_zip15.9 KB5.031×2.3 ms3.5 ms5.7 ms4.718890.9 KB0✓ yes
1,000compas_pb_zstd15.8 KB5.07×2.1 ms3.4 ms5.5 ms4.758889.6 KB0✓ yes
1,000compas_msgpack57.3 KB1.4×0.5 ms7.5 ms8.0 ms7.7822.0 MB0✓ yes
1,000compas_msgpack_zstd24.5 KB3.269×1.0 ms7.6 ms8.7 ms3.2912.0 MB0✓ yes
100,000json9.3 MB1.0×245.1 ms511.1 ms756.2 ms19.034172.2 MB0✓ yes
100,000json_zip2.6 MB3.557×505.5 ms492.9 ms998.4 ms5.55172.2 MB0✓ yes
100,000compas_pb3.5 MB2.686×174.1 ms415.2 ms589.3 ms8.72492.2 MB0✓ yes
100,000compas_pb_zip1.5 MB6.105×244.9 ms407.8 ms652.8 ms3.90795.7 MB0✓ yes
100,000compas_pb_zstd1.4 MB6.436×208.2 ms421.4 ms629.6 ms3.58795.7 MB0✓ yes
100,000compas_msgpack6.5 MB1.428×53.0 ms917.6 ms970.6 ms7.425219.6 MB0✓ yes
100,000compas_msgpack_zstd2.2 MB4.199×130.8 ms950.8 ms1.082 s2.437226.1 MB0✓ yes
1,000,000json100.2 MB1.0×3.323 s5.268 s8.591 s19.9431.6 GB0✓ yes
1,000,000json_zip24.4 MB4.099×5.382 s5.571 s10.952 s4.6011.6 GB0✓ yes
1,000,000compas_pb35.2 MB2.847×1.731 s4.195 s5.925 s8.8885.3 MB0✓ yes
1,000,000compas_pb_zip15.4 MB6.503×2.457 s4.281 s6.738 s3.774920.5 MB0✓ yes
1,000,000compas_pb_zstd14.4 MB6.951×2.225 s4.243 s6.468 s3.563920.5 MB0✓ yes
1,000,000compas_msgpack72.6 MB1.381×586.8 ms9.717 s10.304 s7.8292.1 GB0✓ yes
1,000,000compas_msgpack_zstd15.5 MB6.469×1.305 s9.867 s11.172 s1.6462.1 GB0✓ yes

mesh_attrs

Round-trip time
1,000 elements
json
5.8 ms
json_zip
8.4 ms
compas_pb
5.4 ms
compas_pb_zip
6.2 ms
compas_pb_zstd
5.8 ms
compas_msgpack
8.4 ms
compas_msgpack_zstd
9.3 ms
100,000 elements
json
760.7 ms
json_zip
1.108 s
compas_pb
642.4 ms
compas_pb_zip
719.9 ms
compas_pb_zstd
662.9 ms
compas_msgpack
1.010 s
compas_msgpack_zstd
1.142 s
1,000,000 elements
json
9.414 s
json_zip
17.827 s
compas_pb
6.449 s
compas_pb_zip
7.268 s
compas_pb_zstd
6.971 s
compas_msgpack
10.940 s
compas_msgpack_zstd
11.935 s
Wire size
1,000 elements
json
109.5 KB
json_zip
37.2 KB
compas_pb
40.2 KB
compas_pb_zip
23.7 KB
compas_pb_zstd
23.7 KB
compas_msgpack
74.3 KB
compas_msgpack_zstd
33.0 KB
100,000 elements
json
12.1 MB
json_zip
3.6 MB
compas_pb
4.2 MB
compas_pb_zip
2.2 MB
compas_pb_zstd
2.2 MB
compas_msgpack
8.1 MB
compas_msgpack_zstd
2.9 MB
1,000,000 elements
json
128.1 MB
json_zip
34.3 MB
compas_pb
42.8 MB
compas_pb_zip
22.6 MB
compas_pb_zstd
21.6 MB
compas_msgpack
88.8 MB
compas_msgpack_zstd
22.9 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json109.5 KB1.0×1.9 ms3.9 ms5.8 ms28.8341.6 MB0✓ yes
1,000json_zip37.2 KB2.942×4.4 ms3.9 ms8.4 ms9.7011.6 MB0✓ yes
1,000compas_pb40.2 KB2.726×2.0 ms3.4 ms5.4 ms12.054945.2 KB0✓ yes
1,000compas_pb_zip23.7 KB4.611×2.6 ms3.5 ms6.2 ms6.897986.7 KB0✓ yes
1,000compas_pb_zstd23.7 KB4.617×2.3 ms3.5 ms5.8 ms7.009985.4 KB0✓ yes
1,000compas_msgpack74.3 KB1.474×0.5 ms8.0 ms8.4 ms9.5552.0 MB0✓ yes
1,000compas_msgpack_zstd33.0 KB3.317×1.3 ms8.1 ms9.3 ms4.1882.1 MB0✓ yes
100,000json12.1 MB1.0×262.5 ms498.2 ms760.7 ms25.395177.3 MB0✓ yes
100,000json_zip3.6 MB3.372×595.6 ms512.1 ms1.108 s7.326177.3 MB0✓ yes
100,000compas_pb4.2 MB2.862×210.6 ms431.9 ms642.4 ms10.236100.6 MB0✓ yes
100,000compas_pb_zip2.2 MB5.39×291.4 ms428.4 ms719.9 ms5.479104.8 MB0✓ yes
100,000compas_pb_zstd2.2 MB5.596×240.3 ms422.6 ms662.9 ms5.349104.8 MB0✓ yes
100,000compas_msgpack8.1 MB1.487×53.8 ms956.0 ms1.010 s8.902221.9 MB0✓ yes
100,000compas_msgpack_zstd2.9 MB4.192×153.7 ms987.9 ms1.142 s3.055230.0 MB0✓ yes
1,000,000json128.1 MB1.0×3.630 s5.784 s9.414 s23.2261.7 GB0✓ yes
1,000,000json_zip34.3 MB3.739×8.310 s9.517 s17.827 s3.7761.7 GB0✓ yes
1,000,000compas_pb42.8 MB2.991×2.158 s4.291 s6.449 s10.465969.2 MB0✓ yes
1,000,000compas_pb_zip22.6 MB5.668×2.990 s4.279 s7.268 s5.541012.0 MB0✓ yes
1,000,000compas_pb_zstd21.6 MB5.937×2.605 s4.367 s6.971 s5.1821012.0 MB0✓ yes
1,000,000compas_msgpack88.8 MB1.443×646.6 ms10.293 s10.940 s9.0422.1 GB0✓ yes
1,000,000compas_msgpack_zstd22.9 MB5.587×1.560 s10.375 s11.935 s2.3172.2 GB0✓ yes

pointcloud

Round-trip time
10,000 elements
json
30.6 ms
json_zip
51.5 ms
compas_pb
17.0 ms
compas_pb_zip
23.8 ms
compas_pb_zstd
16.7 ms
compas_msgpack
34.2 ms
compas_msgpack_zstd
34.8 ms
100,000 elements
json
320.3 ms
json_zip
540.3 ms
compas_pb
187.0 ms
compas_pb_zip
246.3 ms
compas_pb_zstd
186.7 ms
compas_msgpack
383.7 ms
compas_msgpack_zstd
423.5 ms
1,000,000 elements
json
3.536 s
json_zip
5.540 s
compas_pb
1.866 s
compas_pb_zip
2.524 s
compas_pb_zstd
1.970 s
compas_msgpack
4.121 s
compas_msgpack_zstd
4.476 s
Wire size
10,000 elements
json
567.0 KB
json_zip
274.2 KB
compas_pb
234.4 KB
compas_pb_zip
223.4 KB
compas_pb_zstd
222.4 KB
compas_msgpack
273.5 KB
compas_msgpack_zstd
244.9 KB
100,000 elements
json
5.5 MB
json_zip
2.7 MB
compas_pb
2.3 MB
compas_pb_zip
2.2 MB
compas_pb_zstd
2.2 MB
compas_msgpack
2.7 MB
compas_msgpack_zstd
2.3 MB
1,000,000 elements
json
55.4 MB
json_zip
26.6 MB
compas_pb
22.9 MB
compas_pb_zip
21.8 MB
compas_pb_zstd
21.7 MB
compas_msgpack
26.7 MB
compas_msgpack_zstd
23.2 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
10,000json567.0 KB1.0×13.4 ms17.1 ms30.6 ms33.8934.1 MB0✓ yes
10,000json_zip274.2 KB2.068×33.5 ms18.1 ms51.5 ms15.5244.1 MB0✓ yes
10,000compas_pb234.4 KB2.419×3.1 ms13.9 ms17.0 ms17.2793.4 MB0✓ yes
10,000compas_pb_zip223.4 KB2.538×9.4 ms14.4 ms23.8 ms15.9353.7 MB0✓ yes
10,000compas_pb_zstd222.4 KB2.549×3.5 ms13.2 ms16.7 ms17.273.7 MB0✓ yes
10,000compas_msgpack273.5 KB2.073×3.6 ms30.6 ms34.2 ms9.1614.4 MB0✓ yes
10,000compas_msgpack_zstd244.9 KB2.315×4.9 ms29.9 ms34.8 ms8.4014.6 MB0✓ yes
100,000json5.5 MB1.0×143.3 ms177.0 ms320.3 ms32.840.6 MB0✓ yes
100,000json_zip2.7 MB2.079×345.9 ms194.4 ms540.3 ms14.36640.6 MB0✓ yes
100,000compas_pb2.3 MB2.419×30.7 ms156.3 ms187.0 ms15.35434.3 MB0✓ yes
100,000compas_pb_zip2.2 MB2.539×94.6 ms151.7 ms246.3 ms15.07136.6 MB0✓ yes
100,000compas_pb_zstd2.2 MB2.549×33.5 ms153.1 ms186.7 ms14.86836.6 MB0✓ yes
100,000compas_msgpack2.7 MB2.073×47.5 ms336.2 ms383.7 ms8.32843.5 MB0✓ yes
100,000compas_msgpack_zstd2.3 MB2.362×80.4 ms343.1 ms423.5 ms7.16246.2 MB0✓ yes
1,000,000json55.4 MB1.0×1.605 s1.931 s3.536 s30.063407.2 MB0✓ yes
1,000,000json_zip26.6 MB2.08×3.580 s1.960 s5.540 s14.242407.2 MB0✓ yes
1,000,000compas_pb22.9 MB2.419×309.8 ms1.556 s1.866 s15.42344.2 MB0✓ yes
1,000,000compas_pb_zip21.8 MB2.54×934.6 ms1.589 s2.524 s14.382367.1 MB0✓ yes
1,000,000compas_pb_zstd21.7 MB2.55×337.0 ms1.633 s1.970 s13.941367.1 MB0✓ yes
1,000,000compas_msgpack26.7 MB2.073×560.3 ms3.561 s4.121 s7.863436.2 MB0✓ yes
1,000,000compas_msgpack_zstd23.2 MB2.384×986.8 ms3.489 s4.476 s6.979462.9 MB0✓ yes

graph

Round-trip time
1,000 elements
json
13.6 ms
json_zip
15.0 ms
compas_pb
10.6 ms
compas_pb_zip
8.4 ms
compas_pb_zstd
8.3 ms
compas_msgpack
17.9 ms
compas_msgpack_zstd
18.1 ms
100,000 elements
json
1.772 s
json_zip
1.906 s
compas_pb
911.2 ms
compas_pb_zip
971.7 ms
compas_pb_zstd
933.8 ms
compas_msgpack
1.951 s
compas_msgpack_zstd
2.065 s
1,000,000 elements
json
19.659 s
json_zip
20.486 s
compas_pb
9.563 s
compas_pb_zip
10.345 s
compas_pb_zstd
9.995 s
compas_msgpack
21.251 s
compas_msgpack_zstd
22.091 s
Wire size
1,000 elements
json
74.9 KB
json_zip
23.8 KB
compas_pb
36.3 KB
compas_pb_zip
15.1 KB
compas_pb_zstd
15.7 KB
compas_msgpack
52.6 KB
compas_msgpack_zstd
17.3 KB
100,000 elements
json
8.1 MB
json_zip
2.2 MB
compas_pb
3.9 MB
compas_pb_zip
1.3 MB
compas_pb_zstd
1.2 MB
compas_msgpack
5.8 MB
compas_msgpack_zstd
1.3 MB
1,000,000 elements
json
85.3 MB
json_zip
21.4 MB
compas_pb
40.0 MB
compas_pb_zip
13.4 MB
compas_pb_zstd
13.1 MB
compas_msgpack
61.6 MB
compas_msgpack_zstd
10.0 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json74.9 KB1.0×1.7 ms11.9 ms13.6 ms6.441.9 MB0✓ yes
1,000json_zip23.8 KB3.154×3.4 ms11.6 ms15.0 ms2.0951.9 MB0✓ yes
1,000compas_pb36.3 KB2.064×4.2 ms6.4 ms10.6 ms5.8531.1 MB0✓ yes
1,000compas_pb_zip15.1 KB4.961×4.6 ms3.8 ms8.4 ms4.0561.1 MB0✓ yes
1,000compas_pb_zstd15.7 KB4.774×4.4 ms3.9 ms8.3 ms4.0811.1 MB0✓ yes
1,000compas_msgpack52.6 KB1.424×0.6 ms17.3 ms17.9 ms3.1172.4 MB0✓ yes
1,000compas_msgpack_zstd17.3 KB4.324×1.2 ms16.9 ms18.1 ms1.052.5 MB0✓ yes
100,000json8.1 MB1.0×332.4 ms1.439 s1.772 s5.894195.5 MB0✓ yes
100,000json_zip2.2 MB3.675×473.4 ms1.433 s1.906 s1.611195.5 MB0✓ yes
100,000compas_pb3.9 MB2.065×439.7 ms471.5 ms911.2 ms8.713112.1 MB0✓ yes
100,000compas_pb_zip1.3 MB6.135×505.5 ms466.2 ms971.7 ms2.966116.0 MB0✓ yes
100,000compas_pb_zstd1.2 MB6.709×489.1 ms444.7 ms933.8 ms2.843116.0 MB0✓ yes
100,000compas_msgpack5.8 MB1.404×113.7 ms1.837 s1.951 s3.29257.5 MB0✓ yes
100,000compas_msgpack_zstd1.3 MB6.01×192.5 ms1.873 s2.065 s0.754263.2 MB0✓ yes
1,000,000json85.3 MB1.0×3.886 s15.773 s19.659 s5.6731.9 GB0✓ yes
1,000,000json_zip21.4 MB3.996×4.924 s15.562 s20.486 s1.4391.9 GB0✓ yes
1,000,000compas_pb40.0 MB2.135×4.646 s4.917 s9.563 s8.5231.1 GB0✓ yes
1,000,000compas_pb_zip13.4 MB6.365×5.357 s4.988 s10.345 s2.8191.1 GB0✓ yes
1,000,000compas_pb_zstd13.1 MB6.516×4.798 s5.198 s9.995 s2.6421.1 GB0✓ yes
1,000,000compas_msgpack61.6 MB1.386×1.731 s19.520 s21.251 s3.3062.5 GB0✓ yes
1,000,000compas_msgpack_zstd10.0 MB8.499×2.273 s19.819 s22.091 s0.5312.5 GB0✓ yes
\ No newline at end of file diff --git a/benchmarks/serialization/results/baseline_quick.csv b/benchmarks/serialization/results/baseline_quick.csv new file mode 100644 index 000000000000..1f63b1e97a29 --- /dev/null +++ b/benchmarks/serialization/results/baseline_quick.csv @@ -0,0 +1,449 @@ +subject,size,format,size_bytes,compression_vs_json,dump_median_s,dump_stdev_s,load_median_s,load_stdev_s,roundtrip_median_s,dump_mb_s,load_mb_s,peak_mem_bytes,lossless,data_equal,canonical_hash_equal,max_abs_error,rms_error,note +arcs,1000,json,268295,1.0,0.006776,0.001842,0.037783,0.000417,0.044559,39.594,7.101,1621970,True,True,True,0.0,0.0,"compact text, lossless" +arcs,1000,json_zip,68479,3.918,0.009425,0.001499,0.037699,0.000339,0.047125,7.266,1.816,1624475,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +arcs,1000,compas_pb,181015,1.482,0.029826,0.008729,0.062544,0.00317,0.092371,6.069,2.894,1252237,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +arcs,1000,compas_pb_zip,63777,4.207,0.033413,0.000123,0.064466,0.003058,0.097879,1.909,0.989,1435212,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +arcs,1000,compas_pb_zstd,62165,4.316,0.033886,0.001151,0.064882,0.000667,0.098767,1.835,0.958,1433285,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +arcs,1000,compas_msgpack,241003,1.113,0.00341,0.00144,0.045567,0.000156,0.048977,70.672,5.289,2576504,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +arcs,1000,compas_msgpack_zstd,62524,4.291,0.005082,0.001959,0.045472,0.000332,0.050553,12.304,1.375,2819540,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +arcs,10000,json,2682697,1.0,0.070373,0.015965,0.409314,0.012586,0.479687,38.121,6.554,16245076,True,True,True,0.0,0.0,"compact text, lossless" +arcs,10000,json_zip,679248,3.95,0.098682,0.013478,0.407725,0.004159,0.506407,6.883,1.666,16247581,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +arcs,10000,compas_pb,1810015,1.482,0.318055,0.002134,0.670987,0.008916,0.989043,5.691,2.698,12560997,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +arcs,10000,compas_pb_zip,633329,4.236,0.33935,0.002203,0.673582,0.000745,1.012933,1.866,0.94,14372240,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +arcs,10000,compas_pb_zstd,608224,4.411,0.333972,0.000319,0.669908,0.007434,1.00388,1.821,0.908,14371045,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +arcs,10000,compas_msgpack,2410003,1.113,0.035036,0.019062,0.503614,0.012116,0.53865,68.787,4.785,25961704,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +arcs,10000,compas_msgpack_zstd,612284,4.381,0.05262,0.014411,0.497921,0.01134,0.550541,11.636,1.23,28371740,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +beziers,1000,json,333223,1.0,0.008523,0.001486,0.009504,0.002981,0.018027,39.096,35.061,1768957,True,True,True,0.0,0.0,"compact text, lossless" +beziers,1000,json_zip,145233,2.294,0.015979,0.00156,0.010095,0.00262,0.026074,9.089,14.386,1772174,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +beziers,1000,compas_pb,155015,2.15,0.004761,8e-05,0.010218,0.000149,0.014979,32.56,15.171,1334757,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +beziers,1000,compas_pb_zip,93529,3.563,0.007018,0.000101,0.010284,0.000333,0.017302,13.326,9.095,1490728,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +beziers,1000,compas_pb_zstd,92304,3.61,0.005682,7e-05,0.010057,0.000153,0.01574,16.244,9.178,1489533,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +beziers,1000,compas_msgpack,199003,1.674,0.00307,0.001442,0.016178,0.000136,0.019248,64.823,12.301,2458003,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +beziers,1000,compas_msgpack_zstd,128824,2.587,0.005106,0.001436,0.01637,0.000129,0.021476,25.228,7.87,2658967,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +beziers,10000,json,3332149,1.0,0.086307,0.01456,0.117002,0.011846,0.203308,38.608,28.48,17768475,True,True,True,0.0,0.0,"compact text, lossless" +beziers,10000,json_zip,1443367,2.309,0.162545,0.014994,0.117021,0.010308,0.279566,8.88,12.334,17770980,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +beziers,10000,compas_pb,1550015,2.15,0.047686,0.000489,0.11919,0.009647,0.166876,32.505,13.005,13435077,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +beziers,10000,compas_pb_zip,933499,3.57,0.072999,0.000415,0.123717,0.002418,0.196716,12.788,7.545,14986264,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +beziers,10000,compas_pb_zstd,926258,3.597,0.057406,0.000735,0.121068,0.008199,0.178474,16.135,7.651,14985125,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +beziers,10000,compas_msgpack,1990003,1.674,0.030358,0.013945,0.190999,0.008183,0.221357,65.551,10.419,24790043,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +beziers,10000,compas_msgpack_zstd,1290950,2.581,0.046477,0.014242,0.197121,0.006636,0.243598,27.776,6.549,26780079,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +boxes,1000,json,285582,1.0,0.007245,0.001631,0.038362,7.9e-05,0.045606,39.419,7.444,1727246,True,True,True,0.0,0.0,"compact text, lossless" +boxes,1000,json_zip,89805,3.18,0.011507,0.001624,0.038595,3.5e-05,0.050103,7.804,2.327,1729959,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +boxes,1000,compas_pb,131015,2.18,0.006912,3.4e-05,0.044874,0.000433,0.051786,18.956,2.92,1339834,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +boxes,1000,compas_pb_zip,52308,5.46,0.008227,0.000151,0.044965,0.000127,0.053192,6.358,1.163,1472077,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +boxes,1000,compas_pb_zstd,50046,5.706,0.007728,8.1e-05,0.044532,0.0035,0.05226,6.476,1.124,1470882,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +boxes,1000,compas_msgpack,230003,1.242,0.003331,0.001596,0.045762,0.002947,0.049092,69.057,5.026,2664336,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +boxes,1000,compas_msgpack_zstd,79492,3.593,0.005403,0.001511,0.04547,0.004019,0.050873,14.713,1.748,2894372,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +boxes,10000,json,2855355,1.0,0.074699,0.01584,0.411866,0.003344,0.486565,38.225,6.933,17297555,True,True,True,0.0,0.0,"compact text, lossless" +boxes,10000,json_zip,891901,3.201,0.114961,0.020308,0.407944,0.003479,0.522905,7.758,2.186,17300052,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +boxes,10000,compas_pb,1310015,2.18,0.066774,0.000522,0.461801,0.01186,0.528575,19.619,2.837,13440386,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +boxes,10000,compas_pb_zip,520863,5.482,0.080396,0.000248,0.46129,0.0067,0.541686,6.479,1.129,14751629,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +boxes,10000,compas_pb_zstd,497195,5.743,0.077045,0.000227,0.460747,0.006764,0.537792,6.453,1.079,14750434,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +boxes,10000,compas_msgpack,2300003,1.241,0.034776,0.020437,0.500339,0.012225,0.535115,66.137,4.597,26841536,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +boxes,10000,compas_msgpack_zstd,795154,3.591,0.057588,0.016341,0.491768,0.009333,0.549356,13.808,1.617,29141572,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +capsules,1000,json,265867,1.0,0.007144,0.002099,0.038046,0.002945,0.04519,37.217,6.988,1683479,True,True,True,0.0,0.0,"compact text, lossless" +capsules,1000,json_zip,79072,3.362,0.010486,0.001665,0.038549,7.1e-05,0.049035,7.541,2.051,1686200,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +capsules,1000,compas_pb,125015,2.127,0.006613,5.8e-05,0.043992,0.00012,0.050606,18.903,2.842,1315830,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +capsules,1000,compas_pb_zip,43554,6.104,0.007865,0.0001,0.044004,0.000217,0.051869,5.537,0.99,1442073,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +capsules,1000,compas_pb_zstd,41512,6.405,0.007413,0.000159,0.044525,0.003015,0.051939,5.6,0.932,1440878,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +capsules,1000,compas_msgpack,221003,1.203,0.003478,0.00145,0.044456,0.003156,0.047934,63.545,4.971,2644332,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +capsules,1000,compas_msgpack_zstd,70853,3.752,0.00534,0.001492,0.044974,0.002902,0.050314,13.269,1.575,2865368,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +capsules,10000,json,2658207,1.0,0.071415,0.015963,0.402879,0.00806,0.474294,37.222,6.598,16860355,True,True,True,0.0,0.0,"compact text, lossless" +capsules,10000,json_zip,783369,3.393,0.107471,0.014711,0.415039,0.006027,0.52251,7.289,1.887,16862860,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +capsules,10000,compas_pb,1250015,2.127,0.066691,0.000434,0.451153,0.002166,0.517845,18.743,2.771,13200310,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +capsules,10000,compas_pb_zip,432880,6.141,0.078943,0.000304,0.457676,0.004053,0.536619,5.483,0.946,14451553,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +capsules,10000,compas_pb_zstd,417417,6.368,0.075699,0.000367,0.45465,0.004257,0.530349,5.514,0.918,14450422,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +capsules,10000,compas_msgpack,2210003,1.203,0.0352,0.020013,0.491376,0.018277,0.526576,62.785,4.498,26641972,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +capsules,10000,compas_msgpack_zstd,694885,3.825,0.057023,0.014677,0.491559,0.007868,0.548583,12.186,1.414,28852008,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +circles,1000,json,237208,1.0,0.006412,0.001573,0.037604,0.002826,0.044016,36.994,6.308,1542803,True,True,True,0.0,0.0,"compact text, lossless" +circles,1000,json_zip,68311,3.472,0.009215,0.00171,0.038069,0.000189,0.047284,7.413,1.794,1545644,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +circles,1000,compas_pb,163015,1.455,0.007527,0.001606,0.045211,0.000193,0.052738,21.657,3.606,1358721,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +circles,1000,compas_pb_zip,63541,3.733,0.009582,0.00169,0.0453,0.000399,0.054881,6.631,1.403,1522964,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +circles,1000,compas_pb_zstd,61987,3.827,0.008871,0.001783,0.045181,0.002776,0.054051,6.988,1.372,1521769,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +circles,1000,compas_msgpack,204003,1.163,0.003195,0.001438,0.044688,0.004323,0.047883,63.854,4.565,2531491,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +circles,1000,compas_msgpack_zstd,62187,3.814,0.005814,0.001316,0.043957,0.002942,0.04977,10.696,1.415,2735527,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +circles,10000,json,2372279,1.0,0.06526,0.015763,0.396625,0.002979,0.461885,36.351,5.981,15454530,True,True,True,0.0,0.0,"compact text, lossless" +circles,10000,json_zip,677389,3.502,0.09275,0.015437,0.404825,0.002552,0.497575,7.303,1.673,15457035,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +circles,10000,compas_pb,1630015,1.455,0.07489,0.021485,0.464606,0.00374,0.539496,21.765,3.508,13630497,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +circles,10000,compas_pb_zip,631627,3.756,0.097199,0.016366,0.474046,0.006816,0.571245,6.498,1.332,15261684,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +circles,10000,compas_pb_zstd,606475,3.912,0.08786,0.017204,0.471781,0.001731,0.559641,6.903,1.286,15260545,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +circles,10000,compas_msgpack,2040003,1.163,0.031953,0.020064,0.485146,0.014678,0.517099,63.843,4.205,25511619,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +circles,10000,compas_msgpack_zstd,608973,3.896,0.048497,0.014612,0.486942,0.010724,0.535438,12.557,1.251,27552103,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +cones,1000,json,262665,1.0,0.006794,0.001637,0.038038,0.003089,0.044831,38.663,6.905,1680271,True,True,True,0.0,0.0,"compact text, lossless" +cones,1000,json_zip,79065,3.322,0.010303,0.001658,0.03834,0.000302,0.048643,7.674,2.062,1682992,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +cones,1000,compas_pb,122015,2.153,0.006566,0.000191,0.044847,0.000984,0.051413,18.582,2.721,1315827,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +cones,1000,compas_pb_zip,43545,6.032,0.007827,9.4e-05,0.044428,0.000294,0.052255,5.564,0.98,1439070,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +cones,1000,compas_pb_zstd,41447,6.337,0.007387,2.8e-05,0.044338,0.002915,0.051725,5.611,0.935,1437875,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +cones,1000,compas_msgpack,218003,1.205,0.003257,0.001581,0.044638,0.003058,0.047895,66.936,4.884,2641329,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +cones,1000,compas_msgpack_zstd,70824,3.709,0.005163,0.00147,0.044855,0.003155,0.050018,13.717,1.579,2859365,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +cones,10000,json,2626398,1.0,0.069477,0.016165,0.406022,0.00326,0.475499,37.802,6.469,16828540,True,True,True,0.0,0.0,"compact text, lossless" +cones,10000,json_zip,783621,3.352,0.117345,0.016991,0.407778,0.016755,0.525123,6.678,1.922,16831045,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +cones,10000,compas_pb,1220015,2.153,0.066611,0.002066,0.452742,0.005665,0.519353,18.316,2.695,13200307,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +cones,10000,compas_pb_zip,432919,6.067,0.078031,0.000929,0.459091,0.003134,0.537122,5.548,0.943,14421550,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +cones,10000,compas_pb_zstd,411478,6.383,0.074865,0.000473,0.457869,0.002609,0.532734,5.496,0.899,14420419,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +cones,10000,compas_msgpack,2180003,1.205,0.032869,0.019771,0.492234,0.01585,0.525103,66.324,4.429,26611969,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +cones,10000,compas_msgpack_zstd,694483,3.782,0.053995,0.015546,0.483208,0.010125,0.537203,12.862,1.437,28792005,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +cylinders,1000,json,266665,1.0,0.006784,0.002032,0.038528,0.00288,0.045313,39.307,6.921,1684327,True,True,True,0.0,0.0,"compact text, lossless" +cylinders,1000,json_zip,79254,3.365,0.010347,0.001737,0.038181,4.4e-05,0.048528,7.66,2.076,1687000,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +cylinders,1000,compas_pb,126015,2.116,0.006566,7.2e-05,0.043758,0.000495,0.050325,19.191,2.88,1315831,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +cylinders,1000,compas_pb_zip,43566,6.121,0.007816,0.000102,0.044451,0.000609,0.052267,5.574,0.98,1443074,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +cylinders,1000,compas_pb_zstd,41449,6.434,0.007377,3.3e-05,0.043701,0.002971,0.051078,5.619,0.948,1441879,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +cylinders,1000,compas_msgpack,222003,1.201,0.003235,0.001677,0.044573,0.002951,0.047807,68.632,4.981,2645797,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +cylinders,1000,compas_msgpack_zstd,70805,3.766,0.005125,0.002211,0.045136,0.003108,0.050261,13.815,1.569,2867369,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +cylinders,10000,json,2666398,1.0,0.069126,0.016037,0.405884,0.003224,0.47501,38.573,6.569,16868548,True,True,True,0.0,0.0,"compact text, lossless" +cylinders,10000,json_zip,786016,3.392,0.103394,0.015638,0.409367,0.011098,0.51276,7.602,1.92,16871045,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +cylinders,10000,compas_pb,1260015,2.116,0.066611,0.001026,0.450326,0.003237,0.516937,18.916,2.798,13200311,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +cylinders,10000,compas_pb_zip,433106,6.156,0.07833,0.00042,0.458025,0.001138,0.536355,5.529,0.946,14461554,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +cylinders,10000,compas_pb_zstd,411480,6.48,0.073199,0.000606,0.454802,0.004474,0.528002,5.621,0.905,14460423,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +cylinders,10000,compas_msgpack,2220003,1.201,0.03289,0.019596,0.492434,0.015408,0.525324,67.498,4.508,26651973,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +cylinders,10000,compas_msgpack_zstd,694959,3.837,0.048313,0.015376,0.482293,0.010804,0.530605,14.385,1.441,28872009,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +ellipses,1000,json,263769,1.0,0.00682,0.001639,0.037923,0.000123,0.044743,38.676,6.955,1593379,True,True,True,0.0,0.0,"compact text, lossless" +ellipses,1000,json_zip,79000,3.339,0.010386,0.001589,0.03829,0.000282,0.048676,7.606,2.063,1596268,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +ellipses,1000,compas_pb,125015,2.11,0.006604,5.2e-05,0.043818,0.000115,0.050422,18.93,2.853,1227830,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +ellipses,1000,compas_pb_zip,43449,6.071,0.008439,0.000181,0.044554,0.000391,0.052993,5.149,0.975,1354073,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +ellipses,1000,compas_pb_zstd,41980,6.283,0.00756,7.1e-05,0.044125,0.002799,0.051685,5.553,0.951,1352878,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +ellipses,1000,compas_msgpack,219003,1.204,0.003291,0.001434,0.046161,0.002938,0.049452,66.549,4.744,2556964,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +ellipses,1000,compas_msgpack_zstd,71247,3.702,0.005232,0.001437,0.044957,0.003159,0.050189,13.617,1.585,2775536,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +ellipses,10000,json,2637716,1.0,0.069959,0.015531,0.402999,0.003544,0.472958,37.704,6.545,15960030,True,True,True,0.0,0.0,"compact text, lossless" +ellipses,10000,json_zip,782788,3.37,0.103737,0.018401,0.407185,0.004225,0.510922,7.546,1.922,15962535,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +ellipses,10000,compas_pb,1250015,2.11,0.067635,0.000703,0.453423,0.002579,0.521058,18.482,2.757,12320478,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +ellipses,10000,compas_pb_zip,431885,6.107,0.079155,0.000316,0.458588,0.004621,0.537743,5.456,0.942,13571717,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +ellipses,10000,compas_pb_zstd,422121,6.249,0.076108,0.00062,0.460914,0.005781,0.537021,5.546,0.916,13570590,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +ellipses,10000,compas_msgpack,2190003,1.204,0.032661,0.01935,0.491476,0.012884,0.524137,67.052,4.456,25762140,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +ellipses,10000,compas_msgpack_zstd,697854,3.78,0.048094,0.015521,0.485464,0.010712,0.533558,14.51,1.437,27952176,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +frames,1000,json,295873,1.0,0.006916,0.001551,0.019369,0.000131,0.026285,42.782,15.275,1384493,False,False,False,4.440892098500626e-16,6.299702658387308e-17,"compact text, lossless" +frames,1000,json_zip,109780,2.695,0.013268,0.001516,0.019889,0.000158,0.033157,8.274,5.52,1386774,False,False,False,4.440892098500626e-16,6.299702658387308e-17,"zip-compressed json, size baseline" +frames,1000,compas_pb,141015,2.098,0.005508,2.6e-05,0.025034,0.002378,0.030542,25.601,5.633,987572,False,False,False,4.440892098500626e-16,6.299702658387308e-17,"protobuf binary, double + flat arrays (optimized)" +frames,1000,compas_pb_zip,71467,4.14,0.00811,8.5e-05,0.025303,0.000111,0.033414,8.812,2.824,1129815,False,False,False,4.440892098500626e-16,6.299702658387308e-17,"protobuf binary, zip-compressed" +frames,1000,compas_pb_zstd,69763,4.241,0.00665,6e-05,0.025246,0.000103,0.031896,10.491,2.763,1128620,False,False,False,4.440892098500626e-16,6.299702658387308e-17,"protobuf binary, zstandard-compressed" +frames,1000,compas_msgpack,180003,1.644,0.002825,0.001405,0.024411,0.003055,0.027236,63.721,7.374,2081122,False,False,False,4.440892098500626e-16,6.299702658387308e-17,msgpack over the JSON-shape tree (Kumiki-style) +frames,1000,compas_msgpack_zstd,96696,3.06,0.004636,0.001425,0.024591,0.002939,0.029227,20.859,3.932,2261158,False,False,False,4.440892098500626e-16,6.299702658387308e-17,"msgpack, zstandard-compressed" +frames,10000,json,2959373,1.0,0.071789,0.016105,0.209698,0.002182,0.281487,41.223,14.113,13880313,False,False,False,5.551115123125783e-16,6.326276205293956e-17,"compact text, lossless" +frames,10000,json_zip,1089865,2.715,0.13484,0.015142,0.21112,0.002258,0.34596,8.083,5.162,13882818,False,False,False,5.551115123125783e-16,6.326276205293956e-17,"zip-compressed json, size baseline" +frames,10000,compas_pb,1410015,2.099,0.055357,0.00048,0.260196,0.001768,0.315553,25.471,5.419,9920052,False,False,False,5.551115123125783e-16,6.326276205293956e-17,"protobuf binary, double + flat arrays (optimized)" +frames,10000,compas_pb_zip,712585,4.153,0.081649,0.000418,0.261925,0.00266,0.343573,8.727,2.721,11331295,False,False,False,5.551115123125783e-16,6.326276205293956e-17,"protobuf binary, zip-compressed" +frames,10000,compas_pb_zstd,707041,4.186,0.066415,0.000378,0.25945,0.004207,0.325864,10.646,2.725,11330100,False,False,False,5.551115123125783e-16,6.326276205293956e-17,"protobuf binary, zstandard-compressed" +frames,10000,compas_msgpack,1800003,1.644,0.028236,0.014364,0.27054,0.006224,0.298776,63.75,6.653,21017794,False,False,False,5.551115123125783e-16,6.326276205293956e-17,msgpack over the JSON-shape tree (Kumiki-style) +frames,10000,compas_msgpack_zstd,964244,3.069,0.04542,0.017255,0.265149,0.006293,0.310569,21.229,3.637,22817830,False,False,False,5.551115123125783e-16,6.326276205293956e-17,"msgpack, zstandard-compressed" +graph,1000,json,76719,1.0,0.001582,5.4e-05,0.01149,4.4e-05,0.013072,48.491,6.677,1938217,True,True,True,0.0,0.0,"compact text, lossless" +graph,1000,json_zip,24330,3.153,0.003257,9.8e-05,0.011641,3.5e-05,0.014898,7.47,2.09,1940018,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +graph,1000,compas_pb,37178,2.064,0.004084,7e-05,0.003766,2.5e-05,0.007849,9.104,9.873,1115572,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +graph,1000,compas_pb_zip,15463,4.961,0.004599,8.5e-05,0.003876,1.4e-05,0.008475,3.362,3.989,1154146,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +graph,1000,compas_pb_zstd,16070,4.774,0.004241,2.5e-05,0.003825,6.5e-05,0.008067,3.789,4.201,1152783,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +graph,1000,compas_msgpack,53875,1.424,0.000599,1.5e-05,0.014409,2.4e-05,0.015008,89.904,3.739,2544105,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +graph,1000,compas_msgpack_zstd,17746,4.323,0.001195,2.8e-05,0.01436,0.002791,0.015555,14.853,1.236,2614349,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +graph,10000,json,793664,1.0,0.01665,0.000192,0.125074,0.003317,0.141725,47.666,6.346,19057914,True,True,True,0.0,0.0,"compact text, lossless" +graph,10000,json_zip,213390,3.719,0.03316,0.003647,0.123859,0.004402,0.157019,6.435,1.723,19020672,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +graph,10000,compas_pb,368817,2.152,0.039199,0.000359,0.037315,0.00414,0.076515,9.409,9.884,11049444,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +graph,10000,compas_pb_zip,142716,5.561,0.047139,0.000348,0.037849,0.004447,0.084988,3.028,3.771,11419657,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +graph,10000,compas_pb_zstd,185816,4.271,0.040746,0.003823,0.037406,0.004429,0.078152,4.56,4.968,11418294,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +graph,10000,compas_msgpack,564802,1.405,0.006094,7e-05,0.158664,0.00202,0.164758,92.681,3.56,25139034,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +graph,10000,compas_msgpack_zstd,118957,6.672,0.010001,0.000542,0.161961,0.004411,0.171962,11.895,0.734,25684514,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +hyperbolas,1000,json,265769,1.0,0.00775,0.001974,0.038002,0.000557,0.045752,34.293,6.994,1595383,True,True,True,0.0,0.0,"compact text, lossless" +hyperbolas,1000,json_zip,79149,3.358,0.010334,0.001536,0.038793,0.000404,0.049127,7.659,2.04,1597888,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +hyperbolas,1000,compas_pb,127015,2.092,0.006453,4.1e-05,0.043989,0.002585,0.050442,19.682,2.887,1227832,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +hyperbolas,1000,compas_pb_zip,43453,6.116,0.008091,0.000867,0.0443,0.00039,0.05239,5.371,0.981,1356399,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +hyperbolas,1000,compas_pb_zstd,41986,6.33,0.007321,5.5e-05,0.043928,0.000218,0.051249,5.735,0.956,1354880,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +hyperbolas,1000,compas_msgpack,221003,1.203,0.003267,0.001487,0.04499,0.000122,0.048257,67.655,4.912,2558502,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +hyperbolas,1000,compas_msgpack_zstd,71166,3.734,0.00505,0.001555,0.044973,0.0001,0.050023,14.092,1.582,2779538,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +hyperbolas,10000,json,2657716,1.0,0.070089,0.015748,0.397164,0.002141,0.467253,37.919,6.692,15980034,True,True,True,0.0,0.0,"compact text, lossless" +hyperbolas,10000,json_zip,784497,3.388,0.103674,0.016224,0.400107,0.011295,0.503781,7.567,1.961,15982539,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +hyperbolas,10000,compas_pb,1270015,2.093,0.067081,0.001105,0.457713,0.002109,0.524794,18.933,2.775,12320480,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +hyperbolas,10000,compas_pb_zip,431927,6.153,0.0788,0.000703,0.455002,0.00895,0.533801,5.481,0.949,13591723,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +hyperbolas,10000,compas_pb_zstd,416781,6.377,0.073621,0.000657,0.455581,0.009929,0.529202,5.661,0.915,13590592,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +hyperbolas,10000,compas_msgpack,2210003,1.203,0.032332,0.019877,0.492413,0.013595,0.524745,68.353,4.488,25782142,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +hyperbolas,10000,compas_msgpack_zstd,697543,3.81,0.050162,0.016408,0.476355,0.012114,0.526517,13.906,1.464,27992178,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +lines,1000,json,218115,1.0,0.007602,0.001652,0.007974,5.4e-05,0.015576,28.693,27.352,1041947,True,True,True,0.0,0.0,"compact text, lossless" +lines,1000,json_zip,87013,2.507,0.011427,0.001612,0.00823,2.8e-05,0.019656,7.615,10.573,1044452,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +lines,1000,compas_pb,109015,2.001,0.006943,5.2e-05,0.011526,1.7e-05,0.018468,15.703,9.459,722835,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +lines,1000,compas_pb_zip,51798,4.211,0.008586,7.3e-05,0.011821,7e-05,0.020407,6.033,4.382,833078,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +lines,1000,compas_pb_zstd,50170,4.348,0.007802,9e-06,0.011593,7.7e-05,0.019395,6.43,4.328,831883,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +lines,1000,compas_msgpack,143003,1.525,0.004497,0.001458,0.011456,0.002429,0.015953,31.801,12.482,1663713,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +lines,1000,compas_msgpack_zstd,78585,2.776,0.006055,0.001448,0.01139,2.6e-05,0.017445,12.978,6.899,1807901,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +lines,10000,json,2181230,1.0,0.077318,0.021174,0.090005,0.001984,0.167324,28.211,24.234,10461430,True,True,True,0.0,0.0,"compact text, lossless" +lines,10000,json_zip,863029,2.527,0.11595,0.015437,0.093069,0.002131,0.209019,7.443,9.273,10464647,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +lines,10000,compas_pb,1090015,2.001,0.069683,0.000381,0.122853,0.004976,0.192536,15.643,8.873,7279619,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +lines,10000,compas_pb_zip,515699,4.23,0.087514,0.000362,0.123334,0.005357,0.210848,5.893,4.181,8370862,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +lines,10000,compas_pb_zstd,498282,4.378,0.079043,0.000835,0.122799,0.004302,0.201842,6.304,4.058,8369667,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +lines,10000,compas_msgpack,1430003,1.525,0.046671,0.014513,0.126468,0.001482,0.173139,30.64,11.307,16848977,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +lines,10000,compas_msgpack_zstd,769118,2.836,0.058913,0.014483,0.135247,0.002638,0.19416,13.055,5.687,18277541,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh,1000,json,82154,1.0,0.001495,4.7e-05,0.003564,4.1e-05,0.00506,54.94,23.049,1611855,True,True,True,0.0,0.0,"compact text, lossless" +mesh,1000,json_zip,28053,2.929,0.003527,7.9e-05,0.003692,6.5e-05,0.007219,7.953,7.598,1614528,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh,1000,compas_pb,32913,2.496,0.001688,2.4e-05,0.003259,6e-05,0.004947,19.5,10.099,878003,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh,1000,compas_pb_zip,16331,5.031,0.002225,5.8e-05,0.003352,0.002534,0.005576,7.341,4.872,912312,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh,1000,compas_pb_zstd,16203,5.07,0.002069,1.4e-05,0.003269,6.7e-05,0.005338,7.832,4.957,910949,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh,1000,compas_msgpack,58668,1.4,0.000426,1.7e-05,0.00746,1e-05,0.007886,137.705,7.865,2063130,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh,1000,compas_msgpack_zstd,25138,3.268,0.000989,2.4e-05,0.007559,1.9e-05,0.008548,25.43,3.326,2121831,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh,10000,json,884888,1.0,0.015874,0.000292,0.043449,0.001366,0.059323,55.744,20.366,16027677,True,True,True,0.0,0.0,"compact text, lossless" +mesh,10000,json_zip,243865,3.629,0.039599,0.00252,0.045628,0.001935,0.085227,6.158,5.345,16030462,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh,10000,compas_pb,328024,2.698,0.016801,0.000136,0.03271,0.003338,0.049511,19.524,10.028,8717491,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh,10000,compas_pb_zip,162262,5.453,0.02375,9.6e-05,0.033289,0.00347,0.057039,6.832,4.874,9048007,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh,10000,compas_pb_zstd,172651,5.125,0.019293,0.000115,0.03807,0.002708,0.057363,8.949,4.535,9045548,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh,10000,compas_msgpack,621054,1.425,0.004339,6.5e-05,0.082042,0.000981,0.086381,143.121,7.57,20581152,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh,10000,compas_msgpack_zstd,203524,4.348,0.007788,7.6e-05,0.082168,0.000666,0.089956,26.133,2.477,21202239,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh_attrs,1000,json,112098,1.0,0.001896,2.5e-05,0.004028,0.002386,0.005924,59.125,27.828,1666239,True,True,True,0.0,0.0,"compact text, lossless" +mesh_attrs,1000,json_zip,38107,2.942,0.004407,8.8e-05,0.003914,3.4e-05,0.008321,8.647,9.735,1669096,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh_attrs,1000,compas_pb,41120,2.726,0.001962,1.7e-05,0.003368,6.3e-05,0.005331,20.957,12.207,967891,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh_attrs,1000,compas_pb_zip,24312,4.611,0.002628,8.8e-05,0.003499,1.8e-05,0.006127,9.25,6.948,1010403,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh_attrs,1000,compas_pb_zstd,24277,4.617,0.002303,2.5e-05,0.003455,1e-05,0.005758,10.541,7.028,1009044,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh_attrs,1000,compas_msgpack,76076,1.474,0.00043,1.4e-05,0.00794,4.5e-05,0.00837,176.75,9.582,2088138,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh_attrs,1000,compas_msgpack_zstd,33792,3.317,0.001251,2.8e-05,0.008006,1.8e-05,0.009257,27.008,4.221,2164031,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +mesh_attrs,10000,json,1177508,1.0,0.019438,0.002726,0.045837,0.004085,0.065275,60.578,25.689,16560353,True,True,True,0.0,0.0,"compact text, lossless" +mesh_attrs,10000,json_zip,347077,3.393,0.047523,0.003006,0.049555,0.004523,0.097077,7.303,7.004,16563314,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +mesh_attrs,10000,compas_pb,408041,2.886,0.01955,4.8e-05,0.034127,0.003879,0.053677,20.872,11.956,9597267,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +mesh_attrs,10000,compas_pb_zip,238533,4.936,0.028441,0.002674,0.035143,0.003945,0.063584,8.387,6.787,10007512,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +mesh_attrs,10000,compas_pb_zstd,238887,4.929,0.022132,0.000178,0.03932,0.002896,0.061452,10.794,6.075,10005341,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +mesh_attrs,10000,compas_msgpack,791054,1.489,0.004342,4.5e-05,0.089423,0.002225,0.093765,182.178,8.846,20820088,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +mesh_attrs,10000,compas_msgpack_zstd,282068,4.175,0.009957,4.7e-05,0.089461,0.00118,0.099418,28.327,3.153,21610719,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +parabolas,1000,json,238538,1.0,0.006436,0.001608,0.037679,0.002465,0.044115,37.064,6.331,1544088,True,True,True,0.0,0.0,"compact text, lossless" +parabolas,1000,json_zip,68216,3.497,0.009151,0.001633,0.037756,0.003323,0.046907,7.455,1.807,1547353,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +parabolas,1000,compas_pb,117015,2.039,0.006338,4.8e-05,0.044057,0.000604,0.050395,18.463,2.656,1203823,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +parabolas,1000,compas_pb_zip,35047,6.806,0.007455,9.2e-05,0.043815,6.9e-05,0.05127,4.701,0.8,1322062,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +parabolas,1000,compas_pb_zstd,33413,7.139,0.007183,3.3e-05,0.043281,0.002545,0.050464,4.652,0.772,1320871,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +parabolas,1000,compas_msgpack,205003,1.164,0.003154,0.001475,0.043875,0.002921,0.047029,65.006,4.672,2533493,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +parabolas,1000,compas_msgpack_zstd,62283,3.83,0.004674,0.001523,0.04449,0.002659,0.049163,13.326,1.4,2738993,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +parabolas,10000,json,2385128,1.0,0.069934,0.016968,0.39528,0.002801,0.465215,34.105,6.034,15467382,True,True,True,0.0,0.0,"compact text, lossless" +parabolas,10000,json_zip,676881,3.524,0.092451,0.01571,0.405979,0.009572,0.49843,7.322,1.667,15469887,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +parabolas,10000,compas_pb,1170015,2.039,0.064282,0.002265,0.449268,0.011774,0.51355,18.201,2.604,12080599,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +parabolas,10000,compas_pb_zip,347980,6.854,0.076305,0.003518,0.455227,0.001472,0.531532,4.56,0.764,13251842,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +parabolas,10000,compas_pb_zstd,332702,7.169,0.073698,0.001385,0.451365,0.00422,0.525063,4.514,0.737,13250647,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +parabolas,10000,compas_msgpack,2050003,1.163,0.031617,0.0151,0.472425,0.007758,0.504041,64.839,4.339,25531621,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +parabolas,10000,compas_msgpack_zstd,622522,3.831,0.052578,0.016948,0.470794,0.014656,0.523372,11.84,1.322,27582105,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +planes,1000,json,225141,1.0,0.005426,0.001459,0.008143,7.4e-05,0.013569,41.491,27.65,1048806,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,"compact text, lossless" +planes,1000,json_zip,87428,2.575,0.009797,0.001549,0.008544,8.2e-05,0.018341,8.924,10.233,1051503,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,"zip-compressed json, size baseline" +planes,1000,compas_pb,110015,2.046,0.004667,5.3e-05,0.012276,9.9e-05,0.016944,23.572,8.962,722652,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,"protobuf binary, double + flat arrays (optimized)" +planes,1000,compas_pb_zip,52297,4.305,0.006211,7.9e-05,0.012491,0.002675,0.018703,8.419,4.187,834631,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,"protobuf binary, zip-compressed" +planes,1000,compas_pb_zstd,50611,4.448,0.005435,6.4e-05,0.012342,3.4e-05,0.017776,9.313,4.101,832700,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,"protobuf binary, zstandard-compressed" +planes,1000,compas_msgpack,147003,1.532,0.002377,0.001376,0.011769,0.002509,0.014147,61.837,12.49,1664479,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,msgpack over the JSON-shape tree (Kumiki-style) +planes,1000,compas_msgpack_zstd,79313,2.839,0.003866,0.001414,0.011758,1.9e-05,0.015624,20.518,6.745,1811515,False,False,False,2.220446049250313e-16,3.8070810520916334e-17,"msgpack, zstandard-compressed" +planes,10000,json,2251968,1.0,0.056872,0.014011,0.088975,0.006179,0.145847,39.597,25.31,10532425,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,"compact text, lossless" +planes,10000,json_zip,866604,2.599,0.098514,0.015044,0.092792,0.004213,0.191307,8.797,9.339,10534930,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,"zip-compressed json, size baseline" +planes,10000,compas_pb,1100015,2.047,0.047321,0.000546,0.130571,0.004437,0.177892,23.246,8.425,7279204,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,"protobuf binary, double + flat arrays (optimized)" +planes,10000,compas_pb_zip,520659,4.325,0.062131,2.9e-05,0.129411,0.003829,0.191542,8.38,4.023,8380447,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,"protobuf binary, zip-compressed" +planes,10000,compas_pb_zstd,508937,4.425,0.055251,0.000458,0.123283,0.00467,0.178534,9.211,4.128,8379436,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,"protobuf binary, zstandard-compressed" +planes,10000,compas_msgpack,1470003,1.532,0.02525,0.013717,0.130598,0.002285,0.155848,58.218,11.256,16858631,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,msgpack over the JSON-shape tree (Kumiki-style) +planes,10000,compas_msgpack_zstd,779985,2.887,0.037016,0.014637,0.131034,0.002913,0.16805,21.072,5.953,18327187,False,False,False,2.220446049250313e-16,3.6521254613811506e-17,"msgpack, zstandard-compressed" +pointcloud,10000,json,580633,1.0,0.013694,0.00267,0.013693,0.002721,0.027388,42.4,42.403,4261476,True,True,True,0.0,0.0,"compact text, lossless" +pointcloud,10000,json_zip,280732,2.068,0.033118,0.000193,0.014547,0.002352,0.047665,8.477,19.298,4264029,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +pointcloud,10000,compas_pb,240074,2.419,0.003062,7.2e-05,0.010534,0.003194,0.013595,78.413,22.791,3599350,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +pointcloud,10000,compas_pb_zip,228795,2.538,0.008991,0.000101,0.011118,0.002635,0.020109,25.446,20.579,3840652,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +pointcloud,10000,compas_pb_zstd,227771,2.549,0.003265,3.6e-05,0.010884,0.002228,0.014149,69.771,20.927,3839561,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +pointcloud,10000,compas_msgpack,280093,2.073,0.003654,0.0001,0.027499,0.002735,0.031152,76.656,10.186,4564763,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +pointcloud,10000,compas_msgpack_zstd,250823,2.315,0.004586,5.5e-05,0.02687,0.002712,0.031455,54.695,9.335,4844889,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +pointcloud,100000,json,5804860,1.0,0.144451,0.001177,0.187996,0.015721,0.332447,40.186,30.878,42597367,True,True,True,0.0,0.0,"compact text, lossless" +pointcloud,100000,json_zip,2792759,2.079,0.344421,0.000465,0.189996,0.011932,0.534417,8.109,14.699,42599872,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +pointcloud,100000,compas_pb,2400078,2.419,0.031122,1.8e-05,0.148575,0.002468,0.179698,77.117,16.154,35996478,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +pointcloud,100000,compas_pb_zip,2286030,2.539,0.094084,0.001811,0.156799,0.003177,0.250883,24.298,14.579,38397656,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +pointcloud,100000,compas_pb_zstd,2276982,2.549,0.0334,0.00045,0.15907,0.004187,0.192469,68.174,14.314,38396589,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +pointcloud,100000,compas_msgpack,2800095,2.073,0.045605,0.000474,0.330859,0.011634,0.376464,61.399,8.463,45598691,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +pointcloud,100000,compas_msgpack_zstd,2457513,2.362,0.073112,0.004536,0.35501,0.014905,0.428121,33.613,6.922,48398587,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +points,1000,json,145071,1.0,0.003526,0.001471,0.003505,0.000114,0.007031,41.141,41.393,512764,True,True,True,0.0,0.0,"compact text, lossless" +points,1000,json_zip,55952,2.593,0.00577,0.001485,0.003745,0.000105,0.009515,9.697,14.939,515269,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +points,1000,compas_pb,79015,1.836,0.003226,2.2e-05,0.004816,2e-06,0.008042,24.493,16.407,266342,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +points,1000,compas_pb_zip,26095,5.559,0.004004,9e-06,0.005075,3.7e-05,0.009079,6.517,5.142,346581,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +points,1000,compas_pb_zstd,24960,5.812,0.00369,5.3e-05,0.004865,6.7e-05,0.008554,6.765,5.131,345390,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +points,1000,compas_msgpack,105003,1.382,0.001721,0.001395,0.005011,9.3e-05,0.006732,61.014,20.955,824071,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +points,1000,compas_msgpack_zstd,51862,2.797,0.002816,0.001389,0.005204,6.4e-05,0.00802,18.416,9.967,929107,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +points,10000,json,1450530,1.0,0.034624,0.013854,0.036246,0.003619,0.070871,41.893,40.019,5170927,True,True,True,0.0,0.0,"compact text, lossless" +points,10000,json_zip,552645,2.625,0.058578,0.014038,0.038519,0.003419,0.097098,9.434,14.347,5173432,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +points,10000,compas_pb,790015,1.836,0.033286,0.000476,0.047972,0.002579,0.081259,23.734,16.468,2718662,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +points,10000,compas_pb_zip,258617,5.609,0.039242,0.000338,0.048318,0.002877,0.08756,6.59,5.352,3509905,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +points,10000,compas_pb_zstd,247395,5.863,0.035692,0.000231,0.048347,0.001579,0.084039,6.931,5.117,3509238,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +points,10000,compas_msgpack,1050003,1.381,0.016711,0.013502,0.059466,0.004223,0.076176,62.835,17.657,8456223,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +points,10000,compas_msgpack_zstd,511738,2.835,0.030761,0.013135,0.059136,0.003491,0.089897,16.636,8.654,9506259,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +polygons,1000,json,450157,1.0,0.010781,0.001539,0.018319,0.002966,0.0291,41.756,24.573,2558294,True,True,True,0.0,0.0,"compact text, lossless" +polygons,1000,json_zip,202548,2.222,0.022418,0.00164,0.018931,4.5e-05,0.041349,9.035,10.699,2561195,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +polygons,1000,compas_pb,204015,2.206,0.005363,2.8e-05,0.018415,0.000231,0.023778,38.04,11.079,2007222,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +polygons,1000,compas_pb_zip,139427,3.229,0.008735,4.9e-05,0.018874,0.002938,0.027608,15.962,7.387,2212361,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +polygons,1000,compas_pb_zstd,138167,3.258,0.006624,0.000101,0.018599,0.002531,0.025223,20.86,7.429,2211166,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +polygons,1000,compas_msgpack,256003,1.758,0.003745,0.001402,0.028236,0.003273,0.031981,68.362,9.066,3307484,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +polygons,1000,compas_msgpack_zstd,178147,2.527,0.006543,0.001472,0.028043,0.002536,0.034586,27.228,6.353,3565392,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +polygons,10000,json,4502991,1.0,0.111414,0.015085,0.21035,0.011836,0.321764,40.417,21.407,25659552,True,True,True,0.0,0.0,"compact text, lossless" +polygons,10000,json_zip,2017113,2.232,0.235235,0.020524,0.21718,0.002514,0.452414,8.575,9.288,25662049,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +polygons,10000,compas_pb,2040015,2.207,0.054622,0.001483,0.21066,0.008493,0.265282,37.348,9.684,20155542,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +polygons,10000,compas_pb_zip,1392267,3.234,0.094134,0.000663,0.213334,0.008365,0.307468,14.79,6.526,22196785,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +polygons,10000,compas_pb_zstd,1377765,3.268,0.066025,0.002435,0.210553,0.008266,0.276578,20.867,6.544,22195590,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +polygons,10000,compas_msgpack,2560003,1.759,0.036913,0.014573,0.333672,0.001047,0.370586,69.352,7.672,33279516,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +polygons,10000,compas_msgpack_zstd,1793839,2.51,0.059531,0.01502,0.332605,0.003707,0.392137,30.133,5.393,35840672,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +polyhedrons,1000,json,381223,1.0,0.007696,0.001592,0.006335,9.6e-05,0.014031,49.536,60.18,1840795,True,True,True,0.0,0.0,"compact text, lossless" +polyhedrons,1000,json_zip,146995,2.593,0.015158,0.001514,0.006894,0.00273,0.022052,9.698,21.321,1843300,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +polyhedrons,1000,compas_pb,185015,2.06,0.007735,8.5e-05,0.008482,0.002417,0.016218,23.918,21.812,1294035,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +polyhedrons,1000,compas_pb_zip,93696,4.069,0.010012,6.2e-05,0.008402,0.002948,0.018414,9.358,11.152,1480278,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +polyhedrons,1000,compas_pb_zstd,92462,4.123,0.008668,5.9e-05,0.008211,0.002981,0.016879,10.667,11.261,1479083,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +polyhedrons,1000,compas_msgpack,228003,1.672,0.001915,0.001488,0.020908,0.002887,0.022823,119.031,10.905,2893967,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +polyhedrons,1000,compas_msgpack_zstd,129273,2.949,0.003782,0.001383,0.020062,0.000137,0.023844,34.182,6.444,3124947,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +polyhedrons,10000,json,3812149,1.0,0.07593,0.014109,0.084785,0.002561,0.160715,50.206,44.963,18488497,True,True,True,0.0,0.0,"compact text, lossless" +polyhedrons,10000,json_zip,1460785,2.61,0.152455,0.014287,0.09246,0.003367,0.244915,9.582,15.799,18491002,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +polyhedrons,10000,compas_pb,1850015,2.061,0.077007,0.000186,0.103005,0.011107,0.180012,24.024,17.96,13034707,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +polyhedrons,10000,compas_pb_zip,934857,4.078,0.100396,0.000291,0.111318,0.002426,0.211714,9.312,8.398,14885702,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +polyhedrons,10000,compas_pb_zstd,929108,4.103,0.089569,0.000307,0.103046,0.011237,0.192615,10.373,9.016,14884803,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +polyhedrons,10000,compas_msgpack,2280003,1.672,0.017548,0.014773,0.250159,0.018672,0.267707,129.926,9.114,29147239,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +polyhedrons,10000,compas_msgpack_zstd,1293941,2.946,0.033451,0.014638,0.258848,0.005176,0.292298,38.682,4.999,31429587,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +polylines,1000,json,567347,1.0,0.013583,0.001579,0.014844,9.6e-05,0.028428,41.768,38.219,3091363,True,True,True,0.0,0.0,"compact text, lossless" +polylines,1000,json_zip,259627,2.185,0.029792,0.001365,0.015661,0.002987,0.045453,8.715,16.578,3094572,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +polylines,1000,compas_pb,253015,2.242,0.006066,7.5e-05,0.014292,0.000186,0.020358,41.709,17.704,2422895,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +polylines,1000,compas_pb_zip,185252,3.063,0.010647,9.6e-05,0.014689,0.002862,0.025336,17.399,12.612,2677942,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +polylines,1000,compas_pb_zstd,183803,3.087,0.007665,6.3e-05,0.014235,0.000101,0.0219,23.979,12.912,2675943,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +polylines,1000,compas_msgpack,313003,1.813,0.00441,0.001417,0.027252,0.003287,0.031662,70.977,11.485,3900909,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +polylines,1000,compas_msgpack_zstd,234131,2.423,0.006587,0.001428,0.027358,0.003176,0.033945,35.545,8.558,4213481,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +polylines,10000,json,5673763,1.0,0.135614,0.01551,0.177864,0.009731,0.313478,41.838,31.899,30990371,True,True,True,0.0,0.0,"compact text, lossless" +polylines,10000,json_zip,2584760,2.195,0.292215,0.014838,0.182546,0.012443,0.474761,8.845,14.16,30992876,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +polylines,10000,compas_pb,2530017,2.243,0.060882,0.000422,0.170518,0.007331,0.2314,41.556,14.837,24315487,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +polylines,10000,compas_pb_zip,1850649,3.066,0.108709,0.000323,0.171855,0.009296,0.280564,17.024,10.769,26846728,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +polylines,10000,compas_pb_zstd,1833306,3.095,0.076223,0.000826,0.168679,0.008445,0.244902,24.052,10.869,26845713,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +polylines,10000,compas_msgpack,3130003,1.813,0.04396,0.014226,0.345827,0.016407,0.389788,71.201,9.051,39209013,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +polylines,10000,compas_msgpack_zstd,2289805,2.478,0.070513,0.015083,0.324067,0.023249,0.39458,32.474,7.066,42340521,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +projections,1000,json,192652,1.0,0.003902,0.001439,0.004253,2.4e-05,0.008154,49.377,45.302,1268288,True,True,True,0.0,0.0,"compact text, lossless" +projections,1000,json_zip,36485,5.28,0.005477,0.001418,0.004397,2.8e-05,0.009874,6.662,8.298,1270721,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +projections,1000,compas_pb,191015,1.009,0.005298,4.6e-05,0.006903,0.002399,0.012201,36.057,27.671,974009,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +projections,1000,compas_pb_zip,11957,16.112,0.006405,6.6e-05,0.007,0.000165,0.013405,1.867,1.708,1166375,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +projections,1000,compas_pb_zstd,8993,21.422,0.006231,0.000306,0.007555,0.002763,0.013785,1.443,1.19,1165057,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +projections,1000,compas_msgpack,239003,0.806,0.001482,0.001447,0.013642,0.002821,0.015124,161.248,17.52,2133543,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +projections,1000,compas_msgpack_zstd,34629,5.563,0.002676,0.001475,0.013658,0.000205,0.016334,12.94,2.535,2375931,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +projections,10000,json,1926699,1.0,0.038062,0.013777,0.054663,0.00601,0.092726,50.619,35.247,12762991,True,True,True,0.0,0.0,"compact text, lossless" +projections,10000,json_zip,361393,5.331,0.055386,0.013341,0.057325,0.006249,0.112711,6.525,6.304,12765160,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +projections,10000,compas_pb,1910015,1.009,0.052633,0.001139,0.079794,0.00656,0.132427,36.289,23.937,9834497,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +projections,10000,compas_pb_zip,117045,16.461,0.062544,0.000567,0.079222,0.005154,0.141766,1.871,1.477,11745740,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +projections,10000,compas_pb_zstd,88046,21.883,0.063079,0.001422,0.076837,0.005877,0.139916,1.396,1.146,11744545,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +projections,10000,compas_msgpack,2390003,0.806,0.014568,0.013445,0.169225,0.012012,0.183793,164.063,14.123,21547223,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +projections,10000,compas_msgpack_zstd,333840,5.771,0.026423,0.013646,0.168928,0.013533,0.195351,12.634,1.976,23937259,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +quaternions,1000,json,189913,1.0,0.003847,0.001451,0.004031,0.000116,0.007878,49.367,47.11,581797,True,True,True,0.0,0.0,"compact text, lossless" +quaternions,1000,json_zip,66879,2.84,0.007218,0.001473,0.004282,0.000124,0.0115,9.265,15.62,584302,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +quaternions,1000,compas_pb,93015,2.042,0.003382,4.5e-05,0.004912,1e-05,0.008294,27.504,18.935,290333,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +quaternions,1000,compas_pb_zip,34587,5.491,0.004131,6.6e-05,0.00505,1.3e-05,0.009182,8.372,6.849,384576,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +quaternions,1000,compas_pb_zstd,33804,5.618,0.003873,9e-06,0.004961,1.2e-05,0.008834,8.727,6.815,383381,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +quaternions,1000,compas_msgpack,127003,1.495,0.001627,0.001348,0.006015,9.9e-05,0.007642,78.05,21.115,1009551,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +quaternions,1000,compas_msgpack_zstd,59683,3.182,0.003205,0.00139,0.006082,9.5e-05,0.009287,18.621,9.814,1136587,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +quaternions,10000,json,1899463,1.0,0.038465,0.013986,0.041226,0.004322,0.079691,49.381,46.074,5859715,True,True,True,0.0,0.0,"compact text, lossless" +quaternions,10000,json_zip,661732,2.87,0.072205,0.015263,0.043317,0.005137,0.115522,9.165,15.276,5862172,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +quaternions,10000,compas_pb,930015,2.042,0.033313,0.001198,0.049509,0.002885,0.082822,27.918,18.785,2958653,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +quaternions,10000,compas_pb_zip,343792,5.525,0.042467,0.000271,0.051319,0.00048,0.093786,8.096,6.699,3890432,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +quaternions,10000,compas_pb_zstd,335374,5.664,0.038554,0.000314,0.050568,0.003064,0.089122,8.699,6.632,3888701,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +quaternions,10000,compas_msgpack,1270003,1.496,0.01638,0.013707,0.068915,0.00423,0.085295,77.534,18.429,10270191,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +quaternions,10000,compas_msgpack_zstd,587967,3.231,0.027762,0.014519,0.068463,0.003869,0.096225,21.179,8.588,11540227,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +reflections,1000,json,193892,1.0,0.003904,0.001426,0.004275,6e-05,0.008179,49.67,45.352,1269464,True,True,True,0.0,0.0,"compact text, lossless" +reflections,1000,json_zip,36492,5.313,0.005484,0.001456,0.00445,4.1e-05,0.009934,6.655,8.2,1271961,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +reflections,1000,compas_pb,191015,1.015,0.005344,0.000209,0.007513,0.000275,0.012857,35.747,25.423,974009,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +reflections,1000,compas_pb_zip,11925,16.259,0.006328,9e-05,0.006882,4.5e-05,0.01321,1.884,1.733,1166252,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +reflections,1000,compas_pb_zstd,8976,21.601,0.006138,2.4e-05,0.006872,5.7e-05,0.01301,1.462,1.306,1165057,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +reflections,1000,compas_msgpack,239003,0.811,0.001455,0.001371,0.015061,0.001136,0.016516,164.24,15.869,2133543,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +reflections,1000,compas_msgpack_zstd,34491,5.622,0.002687,0.001546,0.01405,0.000207,0.016737,12.835,2.455,2372579,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +reflections,10000,json,1939351,1.0,0.038513,0.013733,0.066551,0.005775,0.105064,50.356,29.141,12775643,True,True,True,0.0,0.0,"compact text, lossless" +reflections,10000,json_zip,361812,5.36,0.054617,0.013838,0.061333,0.003857,0.11595,6.625,5.899,12778508,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +reflections,10000,compas_pb,1910015,1.015,0.053051,0.000634,0.078232,0.005426,0.131283,36.004,24.415,9834497,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +reflections,10000,compas_pb_zip,116593,16.634,0.06348,0.000218,0.078229,0.005754,0.141709,1.837,1.49,11745740,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +reflections,10000,compas_pb_zstd,87875,22.069,0.060966,0.00035,0.080907,0.006299,0.141873,1.441,1.086,11744545,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +reflections,10000,compas_msgpack,2390003,0.811,0.014974,0.013233,0.160506,0.007254,0.17548,159.607,14.89,21549535,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +reflections,10000,compas_msgpack_zstd,332677,5.83,0.025732,0.014137,0.168354,0.011382,0.194086,12.928,1.976,23937259,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +rotations,1000,json,238876,1.0,0.004635,0.00145,0.004564,8.2e-05,0.009199,51.537,52.335,1314446,True,True,True,0.0,0.0,"compact text, lossless" +rotations,1000,json_zip,53049,4.503,0.007652,0.001487,0.004821,0.002905,0.012473,6.933,11.004,1316951,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +rotations,1000,compas_pb,189015,1.264,0.005334,2.3e-05,0.005891,1.7e-05,0.011225,35.436,32.086,974047,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +rotations,1000,compas_pb_zip,23010,10.381,0.006346,7.9e-05,0.006142,0.002441,0.012488,3.626,3.747,1164290,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +rotations,1000,compas_pb_zstd,22097,10.81,0.006374,6.6e-05,0.00592,4.1e-05,0.012293,3.467,3.733,1163095,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +rotations,1000,compas_msgpack,237003,1.008,0.001479,0.001331,0.013569,0.0001,0.015047,160.3,17.467,2131541,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +rotations,1000,compas_msgpack_zstd,51557,4.633,0.003033,0.001392,0.013885,0.002284,0.016919,16.997,3.713,2368577,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +rotations,10000,json,2388712,1.0,0.046461,0.013922,0.059982,0.002595,0.106443,51.414,39.824,13225002,True,True,True,0.0,0.0,"compact text, lossless" +rotations,10000,json_zip,527061,4.532,0.075696,0.015141,0.059501,0.006908,0.135197,6.963,8.858,13227171,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +rotations,10000,compas_pb,1890015,1.264,0.054056,0.000352,0.070883,0.00609,0.124939,34.964,26.664,9834487,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +rotations,10000,compas_pb_zip,227787,10.487,0.064095,0.001307,0.071936,0.005481,0.136031,3.554,3.167,11725730,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +rotations,10000,compas_pb_zstd,219350,10.89,0.065133,0.001724,0.068853,0.005728,0.133986,3.368,3.186,11724535,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +rotations,10000,compas_msgpack,2370003,1.008,0.016167,0.013274,0.176197,0.014072,0.192364,146.596,13.451,21527221,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +rotations,10000,compas_msgpack_zstd,501002,4.768,0.028178,0.013719,0.176267,0.015514,0.204445,17.78,2.842,23897257,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +scales,1000,json,216077,1.0,0.004319,0.00159,0.004491,0.000185,0.00881,50.027,48.111,1291644,True,True,True,0.0,0.0,"compact text, lossless" +scales,1000,json_zip,58010,3.725,0.008062,0.001693,0.00537,0.002791,0.013432,7.196,10.802,1294901,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +scales,1000,compas_pb,186015,1.162,0.005418,7.2e-05,0.006948,8.9e-05,0.012366,34.331,26.773,974004,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +scales,1000,compas_pb_zip,26602,8.123,0.006072,7.4e-05,0.007202,0.002691,0.013274,4.381,3.694,1161247,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +scales,1000,compas_pb_zstd,24855,8.694,0.006885,4.9e-05,0.006893,0.00015,0.013779,3.61,3.606,1160052,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +scales,1000,compas_msgpack,234003,0.923,0.001478,0.001434,0.013667,0.002273,0.015145,158.311,17.122,2128538,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +scales,1000,compas_msgpack_zstd,52832,4.09,0.003255,0.001476,0.013913,0.002381,0.017168,16.23,3.797,2362574,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +scales,10000,json,2160302,1.0,0.044433,0.013265,0.061604,0.0039,0.106037,48.62,35.067,12996253,True,True,True,0.0,0.0,"compact text, lossless" +scales,10000,json_zip,574639,3.759,0.071198,0.012599,0.058905,0.00546,0.130103,8.071,9.755,12999094,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +scales,10000,compas_pb,1860015,1.161,0.054072,0.000236,0.081653,0.007419,0.135725,34.399,22.779,9834492,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +scales,10000,compas_pb_zip,263513,8.198,0.063382,0.000266,0.081521,0.007816,0.144902,4.158,3.232,11695735,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +scales,10000,compas_pb_zstd,247439,8.731,0.064196,0.000669,0.078103,0.004666,0.1423,3.854,3.168,11694540,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +scales,10000,compas_msgpack,2340003,0.923,0.014673,0.013452,0.160246,0.009724,0.174919,159.473,14.603,21499530,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +scales,10000,compas_msgpack_zstd,515390,4.192,0.032245,0.013557,0.170898,0.012467,0.203143,15.984,3.016,23837254,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +shears,1000,json,190198,1.0,0.003839,0.001594,0.004264,0.000163,0.008102,49.55,44.61,1265765,True,True,True,0.0,0.0,"compact text, lossless" +shears,1000,json_zip,35178,5.407,0.005376,0.001448,0.004475,0.002806,0.009851,6.543,7.86,1269030,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +shears,1000,compas_pb,186015,1.022,0.005451,6.5e-05,0.006984,9.7e-05,0.012435,34.125,26.634,974004,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +shears,1000,compas_pb_zip,9282,20.491,0.005887,7.4e-05,0.007055,0.002537,0.012942,1.577,1.316,1161247,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +shears,1000,compas_pb_zstd,8084,23.528,0.006095,2.9e-05,0.007039,0.000139,0.013134,1.326,1.148,1160052,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +shears,1000,compas_msgpack,234003,0.813,0.001471,0.001433,0.013549,0.002175,0.01502,159.037,17.271,2128538,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +shears,1000,compas_msgpack_zstd,34264,5.551,0.002624,0.001472,0.013728,0.002151,0.016352,13.057,2.496,2362574,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +shears,10000,json,1902085,1.0,0.038161,0.013681,0.060186,0.001959,0.098347,49.844,31.603,12738372,True,True,True,0.0,0.0,"compact text, lossless" +shears,10000,json_zip,348656,5.455,0.054179,0.013809,0.055476,0.006017,0.109655,6.435,6.285,12740877,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +shears,10000,compas_pb,1860015,1.023,0.05437,0.001131,0.081187,0.007,0.135557,34.21,22.91,9834492,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +shears,10000,compas_pb_zip,90503,21.017,0.057673,0.000147,0.078865,0.005655,0.136537,1.569,1.148,11695731,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +shears,10000,compas_pb_zstd,79349,23.971,0.062775,0.002024,0.079133,0.006822,0.141909,1.264,1.003,11694540,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +shears,10000,compas_msgpack,2340003,0.813,0.014764,0.013373,0.17124,0.012241,0.186004,158.495,13.665,21497218,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +shears,10000,compas_msgpack_zstd,332075,5.728,0.025103,0.013849,0.172511,0.01352,0.197614,13.229,1.925,23837254,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +spheres,1000,json,237252,1.0,0.006924,0.001639,0.040144,0.003158,0.047069,34.263,5.91,1630799,True,True,True,0.0,0.0,"compact text, lossless" +spheres,1000,json_zip,68329,3.472,0.010702,0.001614,0.041615,0.004395,0.052317,6.385,1.642,1633304,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +spheres,1000,compas_pb,115015,2.063,0.007431,0.000452,0.046771,0.000705,0.054202,15.477,2.459,1291821,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +spheres,1000,compas_pb_zip,35118,6.756,0.008599,0.000247,0.04734,0.000669,0.055939,4.084,0.742,1408060,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +spheres,1000,compas_pb_zstd,33203,7.145,0.008197,0.000499,0.047847,0.00026,0.056044,4.051,0.694,1406869,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +spheres,1000,compas_msgpack,204003,1.163,0.004272,0.001124,0.051481,0.011313,0.055753,47.749,3.963,2619787,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +spheres,1000,compas_msgpack_zstd,62203,3.814,0.005948,0.156606,0.047017,0.008815,0.052965,10.458,1.323,2823359,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +spheres,10000,json,2372345,1.0,0.069951,0.017762,0.425011,0.002977,0.494962,33.915,5.582,16334428,True,True,True,0.0,0.0,"compact text, lossless" +spheres,10000,json_zip,677308,3.503,0.101277,0.016677,0.431513,0.01579,0.53279,6.688,1.57,16336925,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +spheres,10000,compas_pb,1150015,2.063,0.064224,0.000803,0.448842,0.015451,0.513066,17.906,2.562,12960429,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +spheres,10000,compas_pb_zip,348595,6.805,0.07805,0.002187,0.471415,0.001843,0.549465,4.466,0.739,14111616,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +spheres,10000,compas_pb_zstd,334774,7.086,0.074454,0.000951,0.47666,0.013185,0.551114,4.496,0.702,14110477,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +spheres,10000,compas_msgpack,2040003,1.163,0.033609,0.014721,0.481019,0.008684,0.514628,60.698,4.241,26391899,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +spheres,10000,compas_msgpack_zstd,609054,3.895,0.047088,0.016438,0.470622,0.009782,0.51771,12.934,1.294,28431935,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +toruses,1000,json,273906,1.0,0.007174,0.001706,0.040094,0.002672,0.047268,38.181,6.832,1691524,True,True,True,0.0,0.0,"compact text, lossless" +toruses,1000,json_zip,79022,3.466,0.01055,0.001813,0.039748,0.004487,0.050298,7.49,1.988,1694789,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +toruses,1000,compas_pb,123015,2.227,0.006848,0.000132,0.044894,0.000297,0.051742,17.964,2.74,1315828,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +toruses,1000,compas_pb_zip,43350,6.318,0.008098,0.000125,0.044959,0.000192,0.053057,5.353,0.964,1440071,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +toruses,1000,compas_pb_zstd,41395,6.617,0.007599,7.9e-05,0.045178,0.002527,0.052777,5.447,0.916,1438876,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +toruses,1000,compas_msgpack,229003,1.196,0.003375,0.00154,0.045782,0.003347,0.049157,67.849,5.002,2756794,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +toruses,1000,compas_msgpack_zstd,70695,3.874,0.005273,0.001525,0.046291,0.003214,0.051564,13.408,1.527,2985366,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +toruses,10000,json,2738988,1.0,0.069513,0.015587,0.400598,0.002355,0.470111,39.403,6.837,16941142,True,True,True,0.0,0.0,"compact text, lossless" +toruses,10000,json_zip,783943,3.494,0.1052,0.015702,0.409255,0.001848,0.514455,7.452,1.916,16943647,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +toruses,10000,compas_pb,1230015,2.227,0.067784,0.000112,0.454498,0.017398,0.522282,18.146,2.706,13200308,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +toruses,10000,compas_pb_zip,430957,6.356,0.081366,0.00104,0.454503,0.00205,0.535869,5.297,0.948,14431615,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +toruses,10000,compas_pb_zstd,410383,6.674,0.073413,0.000645,0.461301,0.002113,0.534714,5.59,0.89,14430420,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +toruses,10000,compas_msgpack,2290003,1.196,0.034217,0.014918,0.479986,0.013221,0.514202,66.926,4.771,27761522,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +toruses,10000,compas_msgpack_zstd,692927,3.953,0.049919,0.015676,0.492363,0.009429,0.542282,13.881,1.407,30050454,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +transformations,1000,json,225071,1.0,0.004552,0.001468,0.004508,6.3e-05,0.00906,49.448,49.926,1300647,True,True,True,0.0,0.0,"compact text, lossless" +transformations,1000,json_zip,59747,3.767,0.00707,0.001486,0.004745,0.002846,0.011815,8.451,12.592,1303912,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +transformations,1000,compas_pb,195015,1.154,0.005315,4.3e-05,0.006829,0.000122,0.012144,36.693,28.555,973949,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +transformations,1000,compas_pb_zip,28931,7.78,0.006702,0.00013,0.006941,0.002641,0.013643,4.317,4.168,1170192,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +transformations,1000,compas_pb_zstd,24131,9.327,0.006812,0.000173,0.007131,0.000124,0.013943,3.542,3.384,1168997,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +transformations,1000,compas_msgpack,243003,0.926,0.001478,0.003994,0.013472,0.00012,0.01495,164.372,18.038,2137547,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +transformations,1000,compas_msgpack_zstd,52835,4.26,0.003311,0.001365,0.013869,0.002972,0.01718,15.957,3.81,2380583,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +transformations,10000,json,2250530,1.0,0.044495,0.01358,0.060624,0.002991,0.105119,50.58,37.123,13086826,True,True,True,0.0,0.0,"compact text, lossless" +transformations,10000,json_zip,592470,3.799,0.070449,0.013727,0.059729,0.006587,0.130178,8.41,9.919,13089323,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +transformations,10000,compas_pb,1950015,1.154,0.053386,0.000184,0.079775,0.007078,0.133161,36.527,24.444,9834437,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +transformations,10000,compas_pb_zip,285055,7.895,0.065197,0.001129,0.078615,0.005742,0.143813,4.372,3.626,11785676,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +transformations,10000,compas_pb_zstd,248334,9.063,0.06603,0.000598,0.077514,0.006259,0.143544,3.761,3.204,11784485,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +transformations,10000,compas_msgpack,2430003,0.926,0.014954,0.013338,0.168526,0.012617,0.18348,162.5,14.419,21587227,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +transformations,10000,compas_msgpack_zstd,515093,4.369,0.032946,0.015257,0.171145,0.011489,0.204091,15.635,3.01,24017263,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +translations,1000,json,222071,1.0,0.004509,0.001388,0.004496,9.4e-05,0.009005,49.252,49.39,1297644,True,True,True,0.0,0.0,"compact text, lossless" +translations,1000,json_zip,59764,3.716,0.006921,0.001483,0.004881,0.002945,0.011802,8.635,12.244,1300141,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +translations,1000,compas_pb,87015,2.552,0.005424,5.3e-05,0.008544,5.2e-05,0.013968,16.041,10.184,662234,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +translations,1000,compas_pb_zip,26120,8.502,0.006221,4e-05,0.009055,0.003118,0.015276,4.199,2.885,750477,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +translations,1000,compas_pb_zstd,24965,8.895,0.005843,4.1e-05,0.008705,7e-05,0.014548,4.272,2.868,749282,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +translations,1000,compas_msgpack,240003,0.925,0.00151,0.001324,0.013436,0.000104,0.014946,158.964,17.863,2134544,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +translations,1000,compas_msgpack_zstd,52745,4.21,0.003336,0.001405,0.013933,0.002714,0.017269,15.81,3.786,2374580,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +translations,10000,json,2220530,1.0,0.044224,0.01544,0.061339,0.003448,0.105563,50.211,36.201,13056487,True,True,True,0.0,0.0,"compact text, lossless" +translations,10000,json_zip,592044,3.751,0.070049,0.013328,0.059607,0.00581,0.129656,8.452,9.932,13059328,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +translations,10000,compas_pb,870015,2.552,0.053354,0.001162,0.098205,0.00594,0.151558,16.307,8.859,6714962,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +translations,10000,compas_pb_zip,258794,8.58,0.061502,0.000426,0.097604,0.005698,0.159105,4.208,2.651,7586205,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +translations,10000,compas_pb_zstd,247452,8.974,0.057181,0.000359,0.097505,0.006043,0.154686,4.327,2.538,7585010,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +translations,10000,compas_msgpack,2400003,0.925,0.014707,0.013337,0.173291,0.013454,0.187997,163.192,13.85,21557224,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +translations,10000,compas_msgpack_zstd,514930,4.312,0.031455,0.014049,0.173792,0.016926,0.205247,16.37,2.963,23957260,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +vectors,1000,json,146071,1.0,0.003568,0.001499,0.00378,0.000107,0.007348,40.945,38.64,513708,True,True,True,0.0,0.0,"compact text, lossless" +vectors,1000,json_zip,55968,2.61,0.005829,0.001557,0.004454,0.000268,0.010283,9.602,12.567,516213,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +vectors,1000,compas_pb,80015,1.826,0.003231,5.5e-05,0.004962,1.3e-05,0.008193,24.763,16.125,266345,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +vectors,1000,compas_pb_zip,26096,5.597,0.003956,0.000124,0.00503,1.8e-05,0.008985,6.597,5.189,347588,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +vectors,1000,compas_pb_zstd,24962,5.852,0.003722,4.1e-05,0.004966,3.4e-05,0.008688,6.706,5.026,346393,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +vectors,1000,compas_msgpack,106003,1.378,0.001694,0.001407,0.005313,0.000101,0.007007,62.576,19.951,825035,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +vectors,1000,compas_msgpack_zstd,51819,2.819,0.002823,0.0014,0.005772,0.000153,0.008596,18.355,8.977,931071,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" +vectors,10000,json,1460530,1.0,0.034952,0.014146,0.038345,0.004012,0.073297,41.787,38.089,5180535,True,True,True,0.0,0.0,"compact text, lossless" +vectors,10000,json_zip,552995,2.641,0.05884,0.014337,0.04028,0.004245,0.09912,9.398,13.729,5182992,True,True,True,0.0,0.0,"zip-compressed json, size baseline" +vectors,10000,compas_pb,800015,1.826,0.032111,0.000131,0.049714,0.002757,0.081825,24.914,16.092,2718665,True,True,True,0.0,0.0,"protobuf binary, double + flat arrays (optimized)" +vectors,10000,compas_pb_zip,258630,5.647,0.039168,0.000194,0.050754,0.000817,0.089922,6.603,5.096,3519979,True,True,True,0.0,0.0,"protobuf binary, zip-compressed" +vectors,10000,compas_pb_zstd,249995,5.842,0.036376,0.000139,0.050413,0.003292,0.086789,6.873,4.959,3518713,True,True,True,0.0,0.0,"protobuf binary, zstandard-compressed" +vectors,10000,compas_msgpack,1060003,1.378,0.016817,0.013634,0.054181,0.003993,0.070997,63.033,19.564,8467667,True,True,True,0.0,0.0,msgpack over the JSON-shape tree (Kumiki-style) +vectors,10000,compas_msgpack_zstd,507000,2.881,0.025568,0.014683,0.055093,0.004235,0.080662,19.829,9.203,9527703,True,True,True,0.0,0.0,"msgpack, zstandard-compressed" diff --git a/benchmarks/serialization/results/baseline_quick.html b/benchmarks/serialization/results/baseline_quick.html new file mode 100644 index 000000000000..6f9c54ba3795 --- /dev/null +++ b/benchmarks/serialization/results/baseline_quick.html @@ -0,0 +1,179 @@ +COMPAS serialization benchmark

COMPAS serialization benchmark

generated 2026-07-11 08:57 · preset quick · repeat 3 · seed 42 · compas 2.15.0-d33431f6

Show formats
31/31 of compas_pb's serializable types are benchmarked. — full coverage.
json · compact text, losslessjson_zip · zip-compressed json, size baselinecompas_pb · protobuf binary, double + flat arrays (optimized)compas_pb_zip · protobuf binary, zip-compressedcompas_pb_zstd · protobuf binary, zstandard-compressedcompas_msgpack · msgpack over the JSON-shape tree (Kumiki-style)compas_msgpack_zstd · msgpack, zstandard-compressed

arcs

Round-trip time
1,000 elements
json
44.6 ms
json_zip
47.1 ms
compas_pb
92.4 ms
compas_pb_zip
97.9 ms
compas_pb_zstd
98.8 ms
compas_msgpack
49.0 ms
compas_msgpack_zstd
50.6 ms
10,000 elements
json
479.7 ms
json_zip
506.4 ms
compas_pb
989.0 ms
compas_pb_zip
1.013 s
compas_pb_zstd
1.004 s
compas_msgpack
538.6 ms
compas_msgpack_zstd
550.5 ms
Wire size
1,000 elements
json
262.0 KB
json_zip
66.9 KB
compas_pb
176.8 KB
compas_pb_zip
62.3 KB
compas_pb_zstd
60.7 KB
compas_msgpack
235.4 KB
compas_msgpack_zstd
61.1 KB
10,000 elements
json
2.6 MB
json_zip
663.3 KB
compas_pb
1.7 MB
compas_pb_zip
618.5 KB
compas_pb_zstd
594.0 KB
compas_msgpack
2.3 MB
compas_msgpack_zstd
597.9 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json262.0 KB1.0×6.8 ms37.8 ms44.6 ms7.1011.5 MB0✓ yes
1,000json_zip66.9 KB3.918×9.4 ms37.7 ms47.1 ms1.8161.5 MB0✓ yes
1,000compas_pb176.8 KB1.482×29.8 ms62.5 ms92.4 ms2.8941.2 MB0✓ yes
1,000compas_pb_zip62.3 KB4.207×33.4 ms64.5 ms97.9 ms0.9891.4 MB0✓ yes
1,000compas_pb_zstd60.7 KB4.316×33.9 ms64.9 ms98.8 ms0.9581.4 MB0✓ yes
1,000compas_msgpack235.4 KB1.113×3.4 ms45.6 ms49.0 ms5.2892.5 MB0✓ yes
1,000compas_msgpack_zstd61.1 KB4.291×5.1 ms45.5 ms50.6 ms1.3752.7 MB0✓ yes
10,000json2.6 MB1.0×70.4 ms409.3 ms479.7 ms6.55415.5 MB0✓ yes
10,000json_zip663.3 KB3.95×98.7 ms407.7 ms506.4 ms1.66615.5 MB0✓ yes
10,000compas_pb1.7 MB1.482×318.1 ms671.0 ms989.0 ms2.69812.0 MB0✓ yes
10,000compas_pb_zip618.5 KB4.236×339.3 ms673.6 ms1.013 s0.9413.7 MB0✓ yes
10,000compas_pb_zstd594.0 KB4.411×334.0 ms669.9 ms1.004 s0.90813.7 MB0✓ yes
10,000compas_msgpack2.3 MB1.113×35.0 ms503.6 ms538.6 ms4.78524.8 MB0✓ yes
10,000compas_msgpack_zstd597.9 KB4.381×52.6 ms497.9 ms550.5 ms1.2327.1 MB0✓ yes

beziers

Round-trip time
1,000 elements
json
18.0 ms
json_zip
26.1 ms
compas_pb
15.0 ms
compas_pb_zip
17.3 ms
compas_pb_zstd
15.7 ms
compas_msgpack
19.2 ms
compas_msgpack_zstd
21.5 ms
10,000 elements
json
203.3 ms
json_zip
279.6 ms
compas_pb
166.9 ms
compas_pb_zip
196.7 ms
compas_pb_zstd
178.5 ms
compas_msgpack
221.4 ms
compas_msgpack_zstd
243.6 ms
Wire size
1,000 elements
json
325.4 KB
json_zip
141.8 KB
compas_pb
151.4 KB
compas_pb_zip
91.3 KB
compas_pb_zstd
90.1 KB
compas_msgpack
194.3 KB
compas_msgpack_zstd
125.8 KB
10,000 elements
json
3.2 MB
json_zip
1.4 MB
compas_pb
1.5 MB
compas_pb_zip
911.6 KB
compas_pb_zstd
904.5 KB
compas_msgpack
1.9 MB
compas_msgpack_zstd
1.2 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json325.4 KB1.0×8.5 ms9.5 ms18.0 ms35.0611.7 MB0✓ yes
1,000json_zip141.8 KB2.294×16.0 ms10.1 ms26.1 ms14.3861.7 MB0✓ yes
1,000compas_pb151.4 KB2.15×4.8 ms10.2 ms15.0 ms15.1711.3 MB0✓ yes
1,000compas_pb_zip91.3 KB3.563×7.0 ms10.3 ms17.3 ms9.0951.4 MB0✓ yes
1,000compas_pb_zstd90.1 KB3.61×5.7 ms10.1 ms15.7 ms9.1781.4 MB0✓ yes
1,000compas_msgpack194.3 KB1.674×3.1 ms16.2 ms19.2 ms12.3012.3 MB0✓ yes
1,000compas_msgpack_zstd125.8 KB2.587×5.1 ms16.4 ms21.5 ms7.872.5 MB0✓ yes
10,000json3.2 MB1.0×86.3 ms117.0 ms203.3 ms28.4816.9 MB0✓ yes
10,000json_zip1.4 MB2.309×162.5 ms117.0 ms279.6 ms12.33416.9 MB0✓ yes
10,000compas_pb1.5 MB2.15×47.7 ms119.2 ms166.9 ms13.00512.8 MB0✓ yes
10,000compas_pb_zip911.6 KB3.57×73.0 ms123.7 ms196.7 ms7.54514.3 MB0✓ yes
10,000compas_pb_zstd904.5 KB3.597×57.4 ms121.1 ms178.5 ms7.65114.3 MB0✓ yes
10,000compas_msgpack1.9 MB1.674×30.4 ms191.0 ms221.4 ms10.41923.6 MB0✓ yes
10,000compas_msgpack_zstd1.2 MB2.581×46.5 ms197.1 ms243.6 ms6.54925.5 MB0✓ yes

boxes

Round-trip time
1,000 elements
json
45.6 ms
json_zip
50.1 ms
compas_pb
51.8 ms
compas_pb_zip
53.2 ms
compas_pb_zstd
52.3 ms
compas_msgpack
49.1 ms
compas_msgpack_zstd
50.9 ms
10,000 elements
json
486.6 ms
json_zip
522.9 ms
compas_pb
528.6 ms
compas_pb_zip
541.7 ms
compas_pb_zstd
537.8 ms
compas_msgpack
535.1 ms
compas_msgpack_zstd
549.4 ms
Wire size
1,000 elements
json
278.9 KB
json_zip
87.7 KB
compas_pb
127.9 KB
compas_pb_zip
51.1 KB
compas_pb_zstd
48.9 KB
compas_msgpack
224.6 KB
compas_msgpack_zstd
77.6 KB
10,000 elements
json
2.7 MB
json_zip
871.0 KB
compas_pb
1.2 MB
compas_pb_zip
508.7 KB
compas_pb_zstd
485.5 KB
compas_msgpack
2.2 MB
compas_msgpack_zstd
776.5 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json278.9 KB1.0×7.2 ms38.4 ms45.6 ms7.4441.6 MB0✓ yes
1,000json_zip87.7 KB3.18×11.5 ms38.6 ms50.1 ms2.3271.6 MB0✓ yes
1,000compas_pb127.9 KB2.18×6.9 ms44.9 ms51.8 ms2.921.3 MB0✓ yes
1,000compas_pb_zip51.1 KB5.46×8.2 ms45.0 ms53.2 ms1.1631.4 MB0✓ yes
1,000compas_pb_zstd48.9 KB5.706×7.7 ms44.5 ms52.3 ms1.1241.4 MB0✓ yes
1,000compas_msgpack224.6 KB1.242×3.3 ms45.8 ms49.1 ms5.0262.5 MB0✓ yes
1,000compas_msgpack_zstd77.6 KB3.593×5.4 ms45.5 ms50.9 ms1.7482.8 MB0✓ yes
10,000json2.7 MB1.0×74.7 ms411.9 ms486.6 ms6.93316.5 MB0✓ yes
10,000json_zip871.0 KB3.201×115.0 ms407.9 ms522.9 ms2.18616.5 MB0✓ yes
10,000compas_pb1.2 MB2.18×66.8 ms461.8 ms528.6 ms2.83712.8 MB0✓ yes
10,000compas_pb_zip508.7 KB5.482×80.4 ms461.3 ms541.7 ms1.12914.1 MB0✓ yes
10,000compas_pb_zstd485.5 KB5.743×77.0 ms460.7 ms537.8 ms1.07914.1 MB0✓ yes
10,000compas_msgpack2.2 MB1.241×34.8 ms500.3 ms535.1 ms4.59725.6 MB0✓ yes
10,000compas_msgpack_zstd776.5 KB3.591×57.6 ms491.8 ms549.4 ms1.61727.8 MB0✓ yes

capsules

Round-trip time
1,000 elements
json
45.2 ms
json_zip
49.0 ms
compas_pb
50.6 ms
compas_pb_zip
51.9 ms
compas_pb_zstd
51.9 ms
compas_msgpack
47.9 ms
compas_msgpack_zstd
50.3 ms
10,000 elements
json
474.3 ms
json_zip
522.5 ms
compas_pb
517.8 ms
compas_pb_zip
536.6 ms
compas_pb_zstd
530.3 ms
compas_msgpack
526.6 ms
compas_msgpack_zstd
548.6 ms
Wire size
1,000 elements
json
259.6 KB
json_zip
77.2 KB
compas_pb
122.1 KB
compas_pb_zip
42.5 KB
compas_pb_zstd
40.5 KB
compas_msgpack
215.8 KB
compas_msgpack_zstd
69.2 KB
10,000 elements
json
2.5 MB
json_zip
765.0 KB
compas_pb
1.2 MB
compas_pb_zip
422.7 KB
compas_pb_zstd
407.6 KB
compas_msgpack
2.1 MB
compas_msgpack_zstd
678.6 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json259.6 KB1.0×7.1 ms38.0 ms45.2 ms6.9881.6 MB0✓ yes
1,000json_zip77.2 KB3.362×10.5 ms38.5 ms49.0 ms2.0511.6 MB0✓ yes
1,000compas_pb122.1 KB2.127×6.6 ms44.0 ms50.6 ms2.8421.3 MB0✓ yes
1,000compas_pb_zip42.5 KB6.104×7.9 ms44.0 ms51.9 ms0.991.4 MB0✓ yes
1,000compas_pb_zstd40.5 KB6.405×7.4 ms44.5 ms51.9 ms0.9321.4 MB0✓ yes
1,000compas_msgpack215.8 KB1.203×3.5 ms44.5 ms47.9 ms4.9712.5 MB0✓ yes
1,000compas_msgpack_zstd69.2 KB3.752×5.3 ms45.0 ms50.3 ms1.5752.7 MB0✓ yes
10,000json2.5 MB1.0×71.4 ms402.9 ms474.3 ms6.59816.1 MB0✓ yes
10,000json_zip765.0 KB3.393×107.5 ms415.0 ms522.5 ms1.88716.1 MB0✓ yes
10,000compas_pb1.2 MB2.127×66.7 ms451.2 ms517.8 ms2.77112.6 MB0✓ yes
10,000compas_pb_zip422.7 KB6.141×78.9 ms457.7 ms536.6 ms0.94613.8 MB0✓ yes
10,000compas_pb_zstd407.6 KB6.368×75.7 ms454.6 ms530.3 ms0.91813.8 MB0✓ yes
10,000compas_msgpack2.1 MB1.203×35.2 ms491.4 ms526.6 ms4.49825.4 MB0✓ yes
10,000compas_msgpack_zstd678.6 KB3.825×57.0 ms491.6 ms548.6 ms1.41427.5 MB0✓ yes

circles

Round-trip time
1,000 elements
json
44.0 ms
json_zip
47.3 ms
compas_pb
52.7 ms
compas_pb_zip
54.9 ms
compas_pb_zstd
54.1 ms
compas_msgpack
47.9 ms
compas_msgpack_zstd
49.8 ms
10,000 elements
json
461.9 ms
json_zip
497.6 ms
compas_pb
539.5 ms
compas_pb_zip
571.2 ms
compas_pb_zstd
559.6 ms
compas_msgpack
517.1 ms
compas_msgpack_zstd
535.4 ms
Wire size
1,000 elements
json
231.6 KB
json_zip
66.7 KB
compas_pb
159.2 KB
compas_pb_zip
62.1 KB
compas_pb_zstd
60.5 KB
compas_msgpack
199.2 KB
compas_msgpack_zstd
60.7 KB
10,000 elements
json
2.3 MB
json_zip
661.5 KB
compas_pb
1.6 MB
compas_pb_zip
616.8 KB
compas_pb_zstd
592.3 KB
compas_msgpack
1.9 MB
compas_msgpack_zstd
594.7 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json231.6 KB1.0×6.4 ms37.6 ms44.0 ms6.3081.5 MB0✓ yes
1,000json_zip66.7 KB3.472×9.2 ms38.1 ms47.3 ms1.7941.5 MB0✓ yes
1,000compas_pb159.2 KB1.455×7.5 ms45.2 ms52.7 ms3.6061.3 MB0✓ yes
1,000compas_pb_zip62.1 KB3.733×9.6 ms45.3 ms54.9 ms1.4031.5 MB0✓ yes
1,000compas_pb_zstd60.5 KB3.827×8.9 ms45.2 ms54.1 ms1.3721.5 MB0✓ yes
1,000compas_msgpack199.2 KB1.163×3.2 ms44.7 ms47.9 ms4.5652.4 MB0✓ yes
1,000compas_msgpack_zstd60.7 KB3.814×5.8 ms44.0 ms49.8 ms1.4152.6 MB0✓ yes
10,000json2.3 MB1.0×65.3 ms396.6 ms461.9 ms5.98114.7 MB0✓ yes
10,000json_zip661.5 KB3.502×92.8 ms404.8 ms497.6 ms1.67314.7 MB0✓ yes
10,000compas_pb1.6 MB1.455×74.9 ms464.6 ms539.5 ms3.50813.0 MB0✓ yes
10,000compas_pb_zip616.8 KB3.756×97.2 ms474.0 ms571.2 ms1.33214.6 MB0✓ yes
10,000compas_pb_zstd592.3 KB3.912×87.9 ms471.8 ms559.6 ms1.28614.6 MB0✓ yes
10,000compas_msgpack1.9 MB1.163×32.0 ms485.1 ms517.1 ms4.20524.3 MB0✓ yes
10,000compas_msgpack_zstd594.7 KB3.896×48.5 ms486.9 ms535.4 ms1.25126.3 MB0✓ yes

cones

Round-trip time
1,000 elements
json
44.8 ms
json_zip
48.6 ms
compas_pb
51.4 ms
compas_pb_zip
52.3 ms
compas_pb_zstd
51.7 ms
compas_msgpack
47.9 ms
compas_msgpack_zstd
50.0 ms
10,000 elements
json
475.5 ms
json_zip
525.1 ms
compas_pb
519.4 ms
compas_pb_zip
537.1 ms
compas_pb_zstd
532.7 ms
compas_msgpack
525.1 ms
compas_msgpack_zstd
537.2 ms
Wire size
1,000 elements
json
256.5 KB
json_zip
77.2 KB
compas_pb
119.2 KB
compas_pb_zip
42.5 KB
compas_pb_zstd
40.5 KB
compas_msgpack
212.9 KB
compas_msgpack_zstd
69.2 KB
10,000 elements
json
2.5 MB
json_zip
765.3 KB
compas_pb
1.2 MB
compas_pb_zip
422.8 KB
compas_pb_zstd
401.8 KB
compas_msgpack
2.1 MB
compas_msgpack_zstd
678.2 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json256.5 KB1.0×6.8 ms38.0 ms44.8 ms6.9051.6 MB0✓ yes
1,000json_zip77.2 KB3.322×10.3 ms38.3 ms48.6 ms2.0621.6 MB0✓ yes
1,000compas_pb119.2 KB2.153×6.6 ms44.8 ms51.4 ms2.7211.3 MB0✓ yes
1,000compas_pb_zip42.5 KB6.032×7.8 ms44.4 ms52.3 ms0.981.4 MB0✓ yes
1,000compas_pb_zstd40.5 KB6.337×7.4 ms44.3 ms51.7 ms0.9351.4 MB0✓ yes
1,000compas_msgpack212.9 KB1.205×3.3 ms44.6 ms47.9 ms4.8842.5 MB0✓ yes
1,000compas_msgpack_zstd69.2 KB3.709×5.2 ms44.9 ms50.0 ms1.5792.7 MB0✓ yes
10,000json2.5 MB1.0×69.5 ms406.0 ms475.5 ms6.46916.0 MB0✓ yes
10,000json_zip765.3 KB3.352×117.3 ms407.8 ms525.1 ms1.92216.1 MB0✓ yes
10,000compas_pb1.2 MB2.153×66.6 ms452.7 ms519.4 ms2.69512.6 MB0✓ yes
10,000compas_pb_zip422.8 KB6.067×78.0 ms459.1 ms537.1 ms0.94313.8 MB0✓ yes
10,000compas_pb_zstd401.8 KB6.383×74.9 ms457.9 ms532.7 ms0.89913.8 MB0✓ yes
10,000compas_msgpack2.1 MB1.205×32.9 ms492.2 ms525.1 ms4.42925.4 MB0✓ yes
10,000compas_msgpack_zstd678.2 KB3.782×54.0 ms483.2 ms537.2 ms1.43727.5 MB0✓ yes

cylinders

Round-trip time
1,000 elements
json
45.3 ms
json_zip
48.5 ms
compas_pb
50.3 ms
compas_pb_zip
52.3 ms
compas_pb_zstd
51.1 ms
compas_msgpack
47.8 ms
compas_msgpack_zstd
50.3 ms
10,000 elements
json
475.0 ms
json_zip
512.8 ms
compas_pb
516.9 ms
compas_pb_zip
536.4 ms
compas_pb_zstd
528.0 ms
compas_msgpack
525.3 ms
compas_msgpack_zstd
530.6 ms
Wire size
1,000 elements
json
260.4 KB
json_zip
77.4 KB
compas_pb
123.1 KB
compas_pb_zip
42.5 KB
compas_pb_zstd
40.5 KB
compas_msgpack
216.8 KB
compas_msgpack_zstd
69.1 KB
10,000 elements
json
2.5 MB
json_zip
767.6 KB
compas_pb
1.2 MB
compas_pb_zip
423.0 KB
compas_pb_zstd
401.8 KB
compas_msgpack
2.1 MB
compas_msgpack_zstd
678.7 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json260.4 KB1.0×6.8 ms38.5 ms45.3 ms6.9211.6 MB0✓ yes
1,000json_zip77.4 KB3.365×10.3 ms38.2 ms48.5 ms2.0761.6 MB0✓ yes
1,000compas_pb123.1 KB2.116×6.6 ms43.8 ms50.3 ms2.881.3 MB0✓ yes
1,000compas_pb_zip42.5 KB6.121×7.8 ms44.5 ms52.3 ms0.981.4 MB0✓ yes
1,000compas_pb_zstd40.5 KB6.434×7.4 ms43.7 ms51.1 ms0.9481.4 MB0✓ yes
1,000compas_msgpack216.8 KB1.201×3.2 ms44.6 ms47.8 ms4.9812.5 MB0✓ yes
1,000compas_msgpack_zstd69.1 KB3.766×5.1 ms45.1 ms50.3 ms1.5692.7 MB0✓ yes
10,000json2.5 MB1.0×69.1 ms405.9 ms475.0 ms6.56916.1 MB0✓ yes
10,000json_zip767.6 KB3.392×103.4 ms409.4 ms512.8 ms1.9216.1 MB0✓ yes
10,000compas_pb1.2 MB2.116×66.6 ms450.3 ms516.9 ms2.79812.6 MB0✓ yes
10,000compas_pb_zip423.0 KB6.156×78.3 ms458.0 ms536.4 ms0.94613.8 MB0✓ yes
10,000compas_pb_zstd401.8 KB6.48×73.2 ms454.8 ms528.0 ms0.90513.8 MB0✓ yes
10,000compas_msgpack2.1 MB1.201×32.9 ms492.4 ms525.3 ms4.50825.4 MB0✓ yes
10,000compas_msgpack_zstd678.7 KB3.837×48.3 ms482.3 ms530.6 ms1.44127.5 MB0✓ yes

ellipses

Round-trip time
1,000 elements
json
44.7 ms
json_zip
48.7 ms
compas_pb
50.4 ms
compas_pb_zip
53.0 ms
compas_pb_zstd
51.7 ms
compas_msgpack
49.5 ms
compas_msgpack_zstd
50.2 ms
10,000 elements
json
473.0 ms
json_zip
510.9 ms
compas_pb
521.1 ms
compas_pb_zip
537.7 ms
compas_pb_zstd
537.0 ms
compas_msgpack
524.1 ms
compas_msgpack_zstd
533.6 ms
Wire size
1,000 elements
json
257.6 KB
json_zip
77.1 KB
compas_pb
122.1 KB
compas_pb_zip
42.4 KB
compas_pb_zstd
41.0 KB
compas_msgpack
213.9 KB
compas_msgpack_zstd
69.6 KB
10,000 elements
json
2.5 MB
json_zip
764.4 KB
compas_pb
1.2 MB
compas_pb_zip
421.8 KB
compas_pb_zstd
412.2 KB
compas_msgpack
2.1 MB
compas_msgpack_zstd
681.5 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json257.6 KB1.0×6.8 ms37.9 ms44.7 ms6.9551.5 MB0✓ yes
1,000json_zip77.1 KB3.339×10.4 ms38.3 ms48.7 ms2.0631.5 MB0✓ yes
1,000compas_pb122.1 KB2.11×6.6 ms43.8 ms50.4 ms2.8531.2 MB0✓ yes
1,000compas_pb_zip42.4 KB6.071×8.4 ms44.6 ms53.0 ms0.9751.3 MB0✓ yes
1,000compas_pb_zstd41.0 KB6.283×7.6 ms44.1 ms51.7 ms0.9511.3 MB0✓ yes
1,000compas_msgpack213.9 KB1.204×3.3 ms46.2 ms49.5 ms4.7442.4 MB0✓ yes
1,000compas_msgpack_zstd69.6 KB3.702×5.2 ms45.0 ms50.2 ms1.5852.6 MB0✓ yes
10,000json2.5 MB1.0×70.0 ms403.0 ms473.0 ms6.54515.2 MB0✓ yes
10,000json_zip764.4 KB3.37×103.7 ms407.2 ms510.9 ms1.92215.2 MB0✓ yes
10,000compas_pb1.2 MB2.11×67.6 ms453.4 ms521.1 ms2.75711.7 MB0✓ yes
10,000compas_pb_zip421.8 KB6.107×79.2 ms458.6 ms537.7 ms0.94212.9 MB0✓ yes
10,000compas_pb_zstd412.2 KB6.249×76.1 ms460.9 ms537.0 ms0.91612.9 MB0✓ yes
10,000compas_msgpack2.1 MB1.204×32.7 ms491.5 ms524.1 ms4.45624.6 MB0✓ yes
10,000compas_msgpack_zstd681.5 KB3.78×48.1 ms485.5 ms533.6 ms1.43726.7 MB0✓ yes

frames

Round-trip time
1,000 elements
json
26.3 ms
json_zip
33.2 ms
compas_pb
30.5 ms
compas_pb_zip
33.4 ms
compas_pb_zstd
31.9 ms
compas_msgpack
27.2 ms
compas_msgpack_zstd
29.2 ms
10,000 elements
json
281.5 ms
json_zip
346.0 ms
compas_pb
315.6 ms
compas_pb_zip
343.6 ms
compas_pb_zstd
325.9 ms
compas_msgpack
298.8 ms
compas_msgpack_zstd
310.6 ms
Wire size
1,000 elements
json
288.9 KB
json_zip
107.2 KB
compas_pb
137.7 KB
compas_pb_zip
69.8 KB
compas_pb_zstd
68.1 KB
compas_msgpack
175.8 KB
compas_msgpack_zstd
94.4 KB
10,000 elements
json
2.8 MB
json_zip
1.0 MB
compas_pb
1.3 MB
compas_pb_zip
695.9 KB
compas_pb_zstd
690.5 KB
compas_msgpack
1.7 MB
compas_msgpack_zstd
941.6 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json288.9 KB1.0×6.9 ms19.4 ms26.3 ms15.2751.3 MB4.4e-16✗ no
1,000json_zip107.2 KB2.695×13.3 ms19.9 ms33.2 ms5.521.3 MB4.4e-16✗ no
1,000compas_pb137.7 KB2.098×5.5 ms25.0 ms30.5 ms5.633964.4 KB4.4e-16✗ no
1,000compas_pb_zip69.8 KB4.14×8.1 ms25.3 ms33.4 ms2.8241.1 MB4.4e-16✗ no
1,000compas_pb_zstd68.1 KB4.241×6.6 ms25.2 ms31.9 ms2.7631.1 MB4.4e-16✗ no
1,000compas_msgpack175.8 KB1.644×2.8 ms24.4 ms27.2 ms7.3742.0 MB4.4e-16✗ no
1,000compas_msgpack_zstd94.4 KB3.06×4.6 ms24.6 ms29.2 ms3.9322.2 MB4.4e-16✗ no
10,000json2.8 MB1.0×71.8 ms209.7 ms281.5 ms14.11313.2 MB5.6e-16✗ no
10,000json_zip1.0 MB2.715×134.8 ms211.1 ms346.0 ms5.16213.2 MB5.6e-16✗ no
10,000compas_pb1.3 MB2.099×55.4 ms260.2 ms315.6 ms5.4199.5 MB5.6e-16✗ no
10,000compas_pb_zip695.9 KB4.153×81.6 ms261.9 ms343.6 ms2.72110.8 MB5.6e-16✗ no
10,000compas_pb_zstd690.5 KB4.186×66.4 ms259.4 ms325.9 ms2.72510.8 MB5.6e-16✗ no
10,000compas_msgpack1.7 MB1.644×28.2 ms270.5 ms298.8 ms6.65320.0 MB5.6e-16✗ no
10,000compas_msgpack_zstd941.6 KB3.069×45.4 ms265.1 ms310.6 ms3.63721.8 MB5.6e-16✗ no

graph

Round-trip time
1,000 elements
json
13.1 ms
json_zip
14.9 ms
compas_pb
7.8 ms
compas_pb_zip
8.5 ms
compas_pb_zstd
8.1 ms
compas_msgpack
15.0 ms
compas_msgpack_zstd
15.6 ms
10,000 elements
json
141.7 ms
json_zip
157.0 ms
compas_pb
76.5 ms
compas_pb_zip
85.0 ms
compas_pb_zstd
78.2 ms
compas_msgpack
164.8 ms
compas_msgpack_zstd
172.0 ms
Wire size
1,000 elements
json
74.9 KB
json_zip
23.8 KB
compas_pb
36.3 KB
compas_pb_zip
15.1 KB
compas_pb_zstd
15.7 KB
compas_msgpack
52.6 KB
compas_msgpack_zstd
17.3 KB
10,000 elements
json
775.1 KB
json_zip
208.4 KB
compas_pb
360.2 KB
compas_pb_zip
139.4 KB
compas_pb_zstd
181.5 KB
compas_msgpack
551.6 KB
compas_msgpack_zstd
116.2 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json74.9 KB1.0×1.6 ms11.5 ms13.1 ms6.6771.8 MB0✓ yes
1,000json_zip23.8 KB3.153×3.3 ms11.6 ms14.9 ms2.091.9 MB0✓ yes
1,000compas_pb36.3 KB2.064×4.1 ms3.8 ms7.8 ms9.8731.1 MB0✓ yes
1,000compas_pb_zip15.1 KB4.961×4.6 ms3.9 ms8.5 ms3.9891.1 MB0✓ yes
1,000compas_pb_zstd15.7 KB4.774×4.2 ms3.8 ms8.1 ms4.2011.1 MB0✓ yes
1,000compas_msgpack52.6 KB1.424×0.6 ms14.4 ms15.0 ms3.7392.4 MB0✓ yes
1,000compas_msgpack_zstd17.3 KB4.323×1.2 ms14.4 ms15.6 ms1.2362.5 MB0✓ yes
10,000json775.1 KB1.0×16.7 ms125.1 ms141.7 ms6.34618.2 MB0✓ yes
10,000json_zip208.4 KB3.719×33.2 ms123.9 ms157.0 ms1.72318.1 MB0✓ yes
10,000compas_pb360.2 KB2.152×39.2 ms37.3 ms76.5 ms9.88410.5 MB0✓ yes
10,000compas_pb_zip139.4 KB5.561×47.1 ms37.8 ms85.0 ms3.77110.9 MB0✓ yes
10,000compas_pb_zstd181.5 KB4.271×40.7 ms37.4 ms78.2 ms4.96810.9 MB0✓ yes
10,000compas_msgpack551.6 KB1.405×6.1 ms158.7 ms164.8 ms3.5624.0 MB0✓ yes
10,000compas_msgpack_zstd116.2 KB6.672×10.0 ms162.0 ms172.0 ms0.73424.5 MB0✓ yes

hyperbolas

Round-trip time
1,000 elements
json
45.8 ms
json_zip
49.1 ms
compas_pb
50.4 ms
compas_pb_zip
52.4 ms
compas_pb_zstd
51.2 ms
compas_msgpack
48.3 ms
compas_msgpack_zstd
50.0 ms
10,000 elements
json
467.3 ms
json_zip
503.8 ms
compas_pb
524.8 ms
compas_pb_zip
533.8 ms
compas_pb_zstd
529.2 ms
compas_msgpack
524.7 ms
compas_msgpack_zstd
526.5 ms
Wire size
1,000 elements
json
259.5 KB
json_zip
77.3 KB
compas_pb
124.0 KB
compas_pb_zip
42.4 KB
compas_pb_zstd
41.0 KB
compas_msgpack
215.8 KB
compas_msgpack_zstd
69.5 KB
10,000 elements
json
2.5 MB
json_zip
766.1 KB
compas_pb
1.2 MB
compas_pb_zip
421.8 KB
compas_pb_zstd
407.0 KB
compas_msgpack
2.1 MB
compas_msgpack_zstd
681.2 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json259.5 KB1.0×7.8 ms38.0 ms45.8 ms6.9941.5 MB0✓ yes
1,000json_zip77.3 KB3.358×10.3 ms38.8 ms49.1 ms2.041.5 MB0✓ yes
1,000compas_pb124.0 KB2.092×6.5 ms44.0 ms50.4 ms2.8871.2 MB0✓ yes
1,000compas_pb_zip42.4 KB6.116×8.1 ms44.3 ms52.4 ms0.9811.3 MB0✓ yes
1,000compas_pb_zstd41.0 KB6.33×7.3 ms43.9 ms51.2 ms0.9561.3 MB0✓ yes
1,000compas_msgpack215.8 KB1.203×3.3 ms45.0 ms48.3 ms4.9122.4 MB0✓ yes
1,000compas_msgpack_zstd69.5 KB3.734×5.0 ms45.0 ms50.0 ms1.5822.7 MB0✓ yes
10,000json2.5 MB1.0×70.1 ms397.2 ms467.3 ms6.69215.2 MB0✓ yes
10,000json_zip766.1 KB3.388×103.7 ms400.1 ms503.8 ms1.96115.2 MB0✓ yes
10,000compas_pb1.2 MB2.093×67.1 ms457.7 ms524.8 ms2.77511.7 MB0✓ yes
10,000compas_pb_zip421.8 KB6.153×78.8 ms455.0 ms533.8 ms0.94913.0 MB0✓ yes
10,000compas_pb_zstd407.0 KB6.377×73.6 ms455.6 ms529.2 ms0.91513.0 MB0✓ yes
10,000compas_msgpack2.1 MB1.203×32.3 ms492.4 ms524.7 ms4.48824.6 MB0✓ yes
10,000compas_msgpack_zstd681.2 KB3.81×50.2 ms476.4 ms526.5 ms1.46426.7 MB0✓ yes

lines

Round-trip time
1,000 elements
json
15.6 ms
json_zip
19.7 ms
compas_pb
18.5 ms
compas_pb_zip
20.4 ms
compas_pb_zstd
19.4 ms
compas_msgpack
16.0 ms
compas_msgpack_zstd
17.4 ms
10,000 elements
json
167.3 ms
json_zip
209.0 ms
compas_pb
192.5 ms
compas_pb_zip
210.8 ms
compas_pb_zstd
201.8 ms
compas_msgpack
173.1 ms
compas_msgpack_zstd
194.2 ms
Wire size
1,000 elements
json
213.0 KB
json_zip
85.0 KB
compas_pb
106.5 KB
compas_pb_zip
50.6 KB
compas_pb_zstd
49.0 KB
compas_msgpack
139.7 KB
compas_msgpack_zstd
76.7 KB
10,000 elements
json
2.1 MB
json_zip
842.8 KB
compas_pb
1.0 MB
compas_pb_zip
503.6 KB
compas_pb_zstd
486.6 KB
compas_msgpack
1.4 MB
compas_msgpack_zstd
751.1 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json213.0 KB1.0×7.6 ms8.0 ms15.6 ms27.3521017.5 KB0✓ yes
1,000json_zip85.0 KB2.507×11.4 ms8.2 ms19.7 ms10.5731020.0 KB0✓ yes
1,000compas_pb106.5 KB2.001×6.9 ms11.5 ms18.5 ms9.459705.9 KB0✓ yes
1,000compas_pb_zip50.6 KB4.211×8.6 ms11.8 ms20.4 ms4.382813.6 KB0✓ yes
1,000compas_pb_zstd49.0 KB4.348×7.8 ms11.6 ms19.4 ms4.328812.4 KB0✓ yes
1,000compas_msgpack139.7 KB1.525×4.5 ms11.5 ms16.0 ms12.4821.6 MB0✓ yes
1,000compas_msgpack_zstd76.7 KB2.776×6.1 ms11.4 ms17.4 ms6.8991.7 MB0✓ yes
10,000json2.1 MB1.0×77.3 ms90.0 ms167.3 ms24.23410.0 MB0✓ yes
10,000json_zip842.8 KB2.527×116.0 ms93.1 ms209.0 ms9.27310.0 MB0✓ yes
10,000compas_pb1.0 MB2.001×69.7 ms122.9 ms192.5 ms8.8736.9 MB0✓ yes
10,000compas_pb_zip503.6 KB4.23×87.5 ms123.3 ms210.8 ms4.1818.0 MB0✓ yes
10,000compas_pb_zstd486.6 KB4.378×79.0 ms122.8 ms201.8 ms4.0588.0 MB0✓ yes
10,000compas_msgpack1.4 MB1.525×46.7 ms126.5 ms173.1 ms11.30716.1 MB0✓ yes
10,000compas_msgpack_zstd751.1 KB2.836×58.9 ms135.2 ms194.2 ms5.68717.4 MB0✓ yes

mesh

Round-trip time
1,000 elements
json
5.1 ms
json_zip
7.2 ms
compas_pb
4.9 ms
compas_pb_zip
5.6 ms
compas_pb_zstd
5.3 ms
compas_msgpack
7.9 ms
compas_msgpack_zstd
8.5 ms
10,000 elements
json
59.3 ms
json_zip
85.2 ms
compas_pb
49.5 ms
compas_pb_zip
57.0 ms
compas_pb_zstd
57.4 ms
compas_msgpack
86.4 ms
compas_msgpack_zstd
90.0 ms
Wire size
1,000 elements
json
80.2 KB
json_zip
27.4 KB
compas_pb
32.1 KB
compas_pb_zip
15.9 KB
compas_pb_zstd
15.8 KB
compas_msgpack
57.3 KB
compas_msgpack_zstd
24.5 KB
10,000 elements
json
864.1 KB
json_zip
238.1 KB
compas_pb
320.3 KB
compas_pb_zip
158.5 KB
compas_pb_zstd
168.6 KB
compas_msgpack
606.5 KB
compas_msgpack_zstd
198.8 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json80.2 KB1.0×1.5 ms3.6 ms5.1 ms23.0491.5 MB0✓ yes
1,000json_zip27.4 KB2.929×3.5 ms3.7 ms7.2 ms7.5981.5 MB0✓ yes
1,000compas_pb32.1 KB2.496×1.7 ms3.3 ms4.9 ms10.099857.4 KB0✓ yes
1,000compas_pb_zip15.9 KB5.031×2.2 ms3.4 ms5.6 ms4.872890.9 KB0✓ yes
1,000compas_pb_zstd15.8 KB5.07×2.1 ms3.3 ms5.3 ms4.957889.6 KB0✓ yes
1,000compas_msgpack57.3 KB1.4×0.4 ms7.5 ms7.9 ms7.8652.0 MB0✓ yes
1,000compas_msgpack_zstd24.5 KB3.268×1.0 ms7.6 ms8.5 ms3.3262.0 MB0✓ yes
10,000json864.1 KB1.0×15.9 ms43.4 ms59.3 ms20.36615.3 MB0✓ yes
10,000json_zip238.1 KB3.629×39.6 ms45.6 ms85.2 ms5.34515.3 MB0✓ yes
10,000compas_pb320.3 KB2.698×16.8 ms32.7 ms49.5 ms10.0288.3 MB0✓ yes
10,000compas_pb_zip158.5 KB5.453×23.8 ms33.3 ms57.0 ms4.8748.6 MB0✓ yes
10,000compas_pb_zstd168.6 KB5.125×19.3 ms38.1 ms57.4 ms4.5358.6 MB0✓ yes
10,000compas_msgpack606.5 KB1.425×4.3 ms82.0 ms86.4 ms7.5719.6 MB0✓ yes
10,000compas_msgpack_zstd198.8 KB4.348×7.8 ms82.2 ms90.0 ms2.47720.2 MB0✓ yes

mesh_attrs

Round-trip time
1,000 elements
json
5.9 ms
json_zip
8.3 ms
compas_pb
5.3 ms
compas_pb_zip
6.1 ms
compas_pb_zstd
5.8 ms
compas_msgpack
8.4 ms
compas_msgpack_zstd
9.3 ms
10,000 elements
json
65.3 ms
json_zip
97.1 ms
compas_pb
53.7 ms
compas_pb_zip
63.6 ms
compas_pb_zstd
61.5 ms
compas_msgpack
93.8 ms
compas_msgpack_zstd
99.4 ms
Wire size
1,000 elements
json
109.5 KB
json_zip
37.2 KB
compas_pb
40.2 KB
compas_pb_zip
23.7 KB
compas_pb_zstd
23.7 KB
compas_msgpack
74.3 KB
compas_msgpack_zstd
33.0 KB
10,000 elements
json
1.1 MB
json_zip
338.9 KB
compas_pb
398.5 KB
compas_pb_zip
232.9 KB
compas_pb_zstd
233.3 KB
compas_msgpack
772.5 KB
compas_msgpack_zstd
275.5 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json109.5 KB1.0×1.9 ms4.0 ms5.9 ms27.8281.6 MB0✓ yes
1,000json_zip37.2 KB2.942×4.4 ms3.9 ms8.3 ms9.7351.6 MB0✓ yes
1,000compas_pb40.2 KB2.726×2.0 ms3.4 ms5.3 ms12.207945.2 KB0✓ yes
1,000compas_pb_zip23.7 KB4.611×2.6 ms3.5 ms6.1 ms6.948986.7 KB0✓ yes
1,000compas_pb_zstd23.7 KB4.617×2.3 ms3.5 ms5.8 ms7.028985.4 KB0✓ yes
1,000compas_msgpack74.3 KB1.474×0.4 ms7.9 ms8.4 ms9.5822.0 MB0✓ yes
1,000compas_msgpack_zstd33.0 KB3.317×1.3 ms8.0 ms9.3 ms4.2212.1 MB0✓ yes
10,000json1.1 MB1.0×19.4 ms45.8 ms65.3 ms25.68915.8 MB0✓ yes
10,000json_zip338.9 KB3.393×47.5 ms49.6 ms97.1 ms7.00415.8 MB0✓ yes
10,000compas_pb398.5 KB2.886×19.6 ms34.1 ms53.7 ms11.9569.2 MB0✓ yes
10,000compas_pb_zip232.9 KB4.936×28.4 ms35.1 ms63.6 ms6.7879.5 MB0✓ yes
10,000compas_pb_zstd233.3 KB4.929×22.1 ms39.3 ms61.5 ms6.0759.5 MB0✓ yes
10,000compas_msgpack772.5 KB1.489×4.3 ms89.4 ms93.8 ms8.84619.9 MB0✓ yes
10,000compas_msgpack_zstd275.5 KB4.175×10.0 ms89.5 ms99.4 ms3.15320.6 MB0✓ yes

parabolas

Round-trip time
1,000 elements
json
44.1 ms
json_zip
46.9 ms
compas_pb
50.4 ms
compas_pb_zip
51.3 ms
compas_pb_zstd
50.5 ms
compas_msgpack
47.0 ms
compas_msgpack_zstd
49.2 ms
10,000 elements
json
465.2 ms
json_zip
498.4 ms
compas_pb
513.5 ms
compas_pb_zip
531.5 ms
compas_pb_zstd
525.1 ms
compas_msgpack
504.0 ms
compas_msgpack_zstd
523.4 ms
Wire size
1,000 elements
json
232.9 KB
json_zip
66.6 KB
compas_pb
114.3 KB
compas_pb_zip
34.2 KB
compas_pb_zstd
32.6 KB
compas_msgpack
200.2 KB
compas_msgpack_zstd
60.8 KB
10,000 elements
json
2.3 MB
json_zip
661.0 KB
compas_pb
1.1 MB
compas_pb_zip
339.8 KB
compas_pb_zstd
324.9 KB
compas_msgpack
2.0 MB
compas_msgpack_zstd
607.9 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json232.9 KB1.0×6.4 ms37.7 ms44.1 ms6.3311.5 MB0✓ yes
1,000json_zip66.6 KB3.497×9.2 ms37.8 ms46.9 ms1.8071.5 MB0✓ yes
1,000compas_pb114.3 KB2.039×6.3 ms44.1 ms50.4 ms2.6561.1 MB0✓ yes
1,000compas_pb_zip34.2 KB6.806×7.5 ms43.8 ms51.3 ms0.81.3 MB0✓ yes
1,000compas_pb_zstd32.6 KB7.139×7.2 ms43.3 ms50.5 ms0.7721.3 MB0✓ yes
1,000compas_msgpack200.2 KB1.164×3.2 ms43.9 ms47.0 ms4.6722.4 MB0✓ yes
1,000compas_msgpack_zstd60.8 KB3.83×4.7 ms44.5 ms49.2 ms1.42.6 MB0✓ yes
10,000json2.3 MB1.0×69.9 ms395.3 ms465.2 ms6.03414.8 MB0✓ yes
10,000json_zip661.0 KB3.524×92.5 ms406.0 ms498.4 ms1.66714.8 MB0✓ yes
10,000compas_pb1.1 MB2.039×64.3 ms449.3 ms513.5 ms2.60411.5 MB0✓ yes
10,000compas_pb_zip339.8 KB6.854×76.3 ms455.2 ms531.5 ms0.76412.6 MB0✓ yes
10,000compas_pb_zstd324.9 KB7.169×73.7 ms451.4 ms525.1 ms0.73712.6 MB0✓ yes
10,000compas_msgpack2.0 MB1.163×31.6 ms472.4 ms504.0 ms4.33924.3 MB0✓ yes
10,000compas_msgpack_zstd607.9 KB3.831×52.6 ms470.8 ms523.4 ms1.32226.3 MB0✓ yes

planes

Round-trip time
1,000 elements
json
13.6 ms
json_zip
18.3 ms
compas_pb
16.9 ms
compas_pb_zip
18.7 ms
compas_pb_zstd
17.8 ms
compas_msgpack
14.1 ms
compas_msgpack_zstd
15.6 ms
10,000 elements
json
145.8 ms
json_zip
191.3 ms
compas_pb
177.9 ms
compas_pb_zip
191.5 ms
compas_pb_zstd
178.5 ms
compas_msgpack
155.8 ms
compas_msgpack_zstd
168.1 ms
Wire size
1,000 elements
json
219.9 KB
json_zip
85.4 KB
compas_pb
107.4 KB
compas_pb_zip
51.1 KB
compas_pb_zstd
49.4 KB
compas_msgpack
143.6 KB
compas_msgpack_zstd
77.5 KB
10,000 elements
json
2.1 MB
json_zip
846.3 KB
compas_pb
1.0 MB
compas_pb_zip
508.5 KB
compas_pb_zstd
497.0 KB
compas_msgpack
1.4 MB
compas_msgpack_zstd
761.7 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json219.9 KB1.0×5.4 ms8.1 ms13.6 ms27.651.0 MB2.2e-16✗ no
1,000json_zip85.4 KB2.575×9.8 ms8.5 ms18.3 ms10.2331.0 MB2.2e-16✗ no
1,000compas_pb107.4 KB2.046×4.7 ms12.3 ms16.9 ms8.962705.7 KB2.2e-16✗ no
1,000compas_pb_zip51.1 KB4.305×6.2 ms12.5 ms18.7 ms4.187815.1 KB2.2e-16✗ no
1,000compas_pb_zstd49.4 KB4.448×5.4 ms12.3 ms17.8 ms4.101813.2 KB2.2e-16✗ no
1,000compas_msgpack143.6 KB1.532×2.4 ms11.8 ms14.1 ms12.491.6 MB2.2e-16✗ no
1,000compas_msgpack_zstd77.5 KB2.839×3.9 ms11.8 ms15.6 ms6.7451.7 MB2.2e-16✗ no
10,000json2.1 MB1.0×56.9 ms89.0 ms145.8 ms25.3110.0 MB2.2e-16✗ no
10,000json_zip846.3 KB2.599×98.5 ms92.8 ms191.3 ms9.33910.0 MB2.2e-16✗ no
10,000compas_pb1.0 MB2.047×47.3 ms130.6 ms177.9 ms8.4256.9 MB2.2e-16✗ no
10,000compas_pb_zip508.5 KB4.325×62.1 ms129.4 ms191.5 ms4.0238.0 MB2.2e-16✗ no
10,000compas_pb_zstd497.0 KB4.425×55.3 ms123.3 ms178.5 ms4.1288.0 MB2.2e-16✗ no
10,000compas_msgpack1.4 MB1.532×25.2 ms130.6 ms155.8 ms11.25616.1 MB2.2e-16✗ no
10,000compas_msgpack_zstd761.7 KB2.887×37.0 ms131.0 ms168.1 ms5.95317.5 MB2.2e-16✗ no

pointcloud

Round-trip time
10,000 elements
json
27.4 ms
json_zip
47.7 ms
compas_pb
13.6 ms
compas_pb_zip
20.1 ms
compas_pb_zstd
14.1 ms
compas_msgpack
31.2 ms
compas_msgpack_zstd
31.5 ms
100,000 elements
json
332.4 ms
json_zip
534.4 ms
compas_pb
179.7 ms
compas_pb_zip
250.9 ms
compas_pb_zstd
192.5 ms
compas_msgpack
376.5 ms
compas_msgpack_zstd
428.1 ms
Wire size
10,000 elements
json
567.0 KB
json_zip
274.2 KB
compas_pb
234.4 KB
compas_pb_zip
223.4 KB
compas_pb_zstd
222.4 KB
compas_msgpack
273.5 KB
compas_msgpack_zstd
244.9 KB
100,000 elements
json
5.5 MB
json_zip
2.7 MB
compas_pb
2.3 MB
compas_pb_zip
2.2 MB
compas_pb_zstd
2.2 MB
compas_msgpack
2.7 MB
compas_msgpack_zstd
2.3 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
10,000json567.0 KB1.0×13.7 ms13.7 ms27.4 ms42.4034.1 MB0✓ yes
10,000json_zip274.2 KB2.068×33.1 ms14.5 ms47.7 ms19.2984.1 MB0✓ yes
10,000compas_pb234.4 KB2.419×3.1 ms10.5 ms13.6 ms22.7913.4 MB0✓ yes
10,000compas_pb_zip223.4 KB2.538×9.0 ms11.1 ms20.1 ms20.5793.7 MB0✓ yes
10,000compas_pb_zstd222.4 KB2.549×3.3 ms10.9 ms14.1 ms20.9273.7 MB0✓ yes
10,000compas_msgpack273.5 KB2.073×3.7 ms27.5 ms31.2 ms10.1864.4 MB0✓ yes
10,000compas_msgpack_zstd244.9 KB2.315×4.6 ms26.9 ms31.5 ms9.3354.6 MB0✓ yes
100,000json5.5 MB1.0×144.5 ms188.0 ms332.4 ms30.87840.6 MB0✓ yes
100,000json_zip2.7 MB2.079×344.4 ms190.0 ms534.4 ms14.69940.6 MB0✓ yes
100,000compas_pb2.3 MB2.419×31.1 ms148.6 ms179.7 ms16.15434.3 MB0✓ yes
100,000compas_pb_zip2.2 MB2.539×94.1 ms156.8 ms250.9 ms14.57936.6 MB0✓ yes
100,000compas_pb_zstd2.2 MB2.549×33.4 ms159.1 ms192.5 ms14.31436.6 MB0✓ yes
100,000compas_msgpack2.7 MB2.073×45.6 ms330.9 ms376.5 ms8.46343.5 MB0✓ yes
100,000compas_msgpack_zstd2.3 MB2.362×73.1 ms355.0 ms428.1 ms6.92246.2 MB0✓ yes

points

Round-trip time
1,000 elements
json
7.0 ms
json_zip
9.5 ms
compas_pb
8.0 ms
compas_pb_zip
9.1 ms
compas_pb_zstd
8.6 ms
compas_msgpack
6.7 ms
compas_msgpack_zstd
8.0 ms
10,000 elements
json
70.9 ms
json_zip
97.1 ms
compas_pb
81.3 ms
compas_pb_zip
87.6 ms
compas_pb_zstd
84.0 ms
compas_msgpack
76.2 ms
compas_msgpack_zstd
89.9 ms
Wire size
1,000 elements
json
141.7 KB
json_zip
54.6 KB
compas_pb
77.2 KB
compas_pb_zip
25.5 KB
compas_pb_zstd
24.4 KB
compas_msgpack
102.5 KB
compas_msgpack_zstd
50.6 KB
10,000 elements
json
1.4 MB
json_zip
539.7 KB
compas_pb
771.5 KB
compas_pb_zip
252.6 KB
compas_pb_zstd
241.6 KB
compas_msgpack
1.0 MB
compas_msgpack_zstd
499.7 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json141.7 KB1.0×3.5 ms3.5 ms7.0 ms41.393500.7 KB0✓ yes
1,000json_zip54.6 KB2.593×5.8 ms3.7 ms9.5 ms14.939503.2 KB0✓ yes
1,000compas_pb77.2 KB1.836×3.2 ms4.8 ms8.0 ms16.407260.1 KB0✓ yes
1,000compas_pb_zip25.5 KB5.559×4.0 ms5.1 ms9.1 ms5.142338.5 KB0✓ yes
1,000compas_pb_zstd24.4 KB5.812×3.7 ms4.9 ms8.6 ms5.131337.3 KB0✓ yes
1,000compas_msgpack102.5 KB1.382×1.7 ms5.0 ms6.7 ms20.955804.8 KB0✓ yes
1,000compas_msgpack_zstd50.6 KB2.797×2.8 ms5.2 ms8.0 ms9.967907.3 KB0✓ yes
10,000json1.4 MB1.0×34.6 ms36.2 ms70.9 ms40.0194.9 MB0✓ yes
10,000json_zip539.7 KB2.625×58.6 ms38.5 ms97.1 ms14.3474.9 MB0✓ yes
10,000compas_pb771.5 KB1.836×33.3 ms48.0 ms81.3 ms16.4682.6 MB0✓ yes
10,000compas_pb_zip252.6 KB5.609×39.2 ms48.3 ms87.6 ms5.3523.3 MB0✓ yes
10,000compas_pb_zstd241.6 KB5.863×35.7 ms48.3 ms84.0 ms5.1173.3 MB0✓ yes
10,000compas_msgpack1.0 MB1.381×16.7 ms59.5 ms76.2 ms17.6578.1 MB0✓ yes
10,000compas_msgpack_zstd499.7 KB2.835×30.8 ms59.1 ms89.9 ms8.6549.1 MB0✓ yes

polygons

Round-trip time
1,000 elements
json
29.1 ms
json_zip
41.3 ms
compas_pb
23.8 ms
compas_pb_zip
27.6 ms
compas_pb_zstd
25.2 ms
compas_msgpack
32.0 ms
compas_msgpack_zstd
34.6 ms
10,000 elements
json
321.8 ms
json_zip
452.4 ms
compas_pb
265.3 ms
compas_pb_zip
307.5 ms
compas_pb_zstd
276.6 ms
compas_msgpack
370.6 ms
compas_msgpack_zstd
392.1 ms
Wire size
1,000 elements
json
439.6 KB
json_zip
197.8 KB
compas_pb
199.2 KB
compas_pb_zip
136.2 KB
compas_pb_zstd
134.9 KB
compas_msgpack
250.0 KB
compas_msgpack_zstd
174.0 KB
10,000 elements
json
4.3 MB
json_zip
1.9 MB
compas_pb
1.9 MB
compas_pb_zip
1.3 MB
compas_pb_zstd
1.3 MB
compas_msgpack
2.4 MB
compas_msgpack_zstd
1.7 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json439.6 KB1.0×10.8 ms18.3 ms29.1 ms24.5732.4 MB0✓ yes
1,000json_zip197.8 KB2.222×22.4 ms18.9 ms41.3 ms10.6992.4 MB0✓ yes
1,000compas_pb199.2 KB2.206×5.4 ms18.4 ms23.8 ms11.0791.9 MB0✓ yes
1,000compas_pb_zip136.2 KB3.229×8.7 ms18.9 ms27.6 ms7.3872.1 MB0✓ yes
1,000compas_pb_zstd134.9 KB3.258×6.6 ms18.6 ms25.2 ms7.4292.1 MB0✓ yes
1,000compas_msgpack250.0 KB1.758×3.7 ms28.2 ms32.0 ms9.0663.2 MB0✓ yes
1,000compas_msgpack_zstd174.0 KB2.527×6.5 ms28.0 ms34.6 ms6.3533.4 MB0✓ yes
10,000json4.3 MB1.0×111.4 ms210.4 ms321.8 ms21.40724.5 MB0✓ yes
10,000json_zip1.9 MB2.232×235.2 ms217.2 ms452.4 ms9.28824.5 MB0✓ yes
10,000compas_pb1.9 MB2.207×54.6 ms210.7 ms265.3 ms9.68419.2 MB0✓ yes
10,000compas_pb_zip1.3 MB3.234×94.1 ms213.3 ms307.5 ms6.52621.2 MB0✓ yes
10,000compas_pb_zstd1.3 MB3.268×66.0 ms210.6 ms276.6 ms6.54421.2 MB0✓ yes
10,000compas_msgpack2.4 MB1.759×36.9 ms333.7 ms370.6 ms7.67231.7 MB0✓ yes
10,000compas_msgpack_zstd1.7 MB2.51×59.5 ms332.6 ms392.1 ms5.39334.2 MB0✓ yes

polyhedrons

Round-trip time
1,000 elements
json
14.0 ms
json_zip
22.1 ms
compas_pb
16.2 ms
compas_pb_zip
18.4 ms
compas_pb_zstd
16.9 ms
compas_msgpack
22.8 ms
compas_msgpack_zstd
23.8 ms
10,000 elements
json
160.7 ms
json_zip
244.9 ms
compas_pb
180.0 ms
compas_pb_zip
211.7 ms
compas_pb_zstd
192.6 ms
compas_msgpack
267.7 ms
compas_msgpack_zstd
292.3 ms
Wire size
1,000 elements
json
372.3 KB
json_zip
143.5 KB
compas_pb
180.7 KB
compas_pb_zip
91.5 KB
compas_pb_zstd
90.3 KB
compas_msgpack
222.7 KB
compas_msgpack_zstd
126.2 KB
10,000 elements
json
3.6 MB
json_zip
1.4 MB
compas_pb
1.8 MB
compas_pb_zip
912.9 KB
compas_pb_zstd
907.3 KB
compas_msgpack
2.2 MB
compas_msgpack_zstd
1.2 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json372.3 KB1.0×7.7 ms6.3 ms14.0 ms60.181.8 MB0✓ yes
1,000json_zip143.5 KB2.593×15.2 ms6.9 ms22.1 ms21.3211.8 MB0✓ yes
1,000compas_pb180.7 KB2.06×7.7 ms8.5 ms16.2 ms21.8121.2 MB0✓ yes
1,000compas_pb_zip91.5 KB4.069×10.0 ms8.4 ms18.4 ms11.1521.4 MB0✓ yes
1,000compas_pb_zstd90.3 KB4.123×8.7 ms8.2 ms16.9 ms11.2611.4 MB0✓ yes
1,000compas_msgpack222.7 KB1.672×1.9 ms20.9 ms22.8 ms10.9052.8 MB0✓ yes
1,000compas_msgpack_zstd126.2 KB2.949×3.8 ms20.1 ms23.8 ms6.4443.0 MB0✓ yes
10,000json3.6 MB1.0×75.9 ms84.8 ms160.7 ms44.96317.6 MB0✓ yes
10,000json_zip1.4 MB2.61×152.5 ms92.5 ms244.9 ms15.79917.6 MB0✓ yes
10,000compas_pb1.8 MB2.061×77.0 ms103.0 ms180.0 ms17.9612.4 MB0✓ yes
10,000compas_pb_zip912.9 KB4.078×100.4 ms111.3 ms211.7 ms8.39814.2 MB0✓ yes
10,000compas_pb_zstd907.3 KB4.103×89.6 ms103.0 ms192.6 ms9.01614.2 MB0✓ yes
10,000compas_msgpack2.2 MB1.672×17.5 ms250.2 ms267.7 ms9.11427.8 MB0✓ yes
10,000compas_msgpack_zstd1.2 MB2.946×33.5 ms258.8 ms292.3 ms4.99930.0 MB0✓ yes

polylines

Round-trip time
1,000 elements
json
28.4 ms
json_zip
45.5 ms
compas_pb
20.4 ms
compas_pb_zip
25.3 ms
compas_pb_zstd
21.9 ms
compas_msgpack
31.7 ms
compas_msgpack_zstd
33.9 ms
10,000 elements
json
313.5 ms
json_zip
474.8 ms
compas_pb
231.4 ms
compas_pb_zip
280.6 ms
compas_pb_zstd
244.9 ms
compas_msgpack
389.8 ms
compas_msgpack_zstd
394.6 ms
Wire size
1,000 elements
json
554.0 KB
json_zip
253.5 KB
compas_pb
247.1 KB
compas_pb_zip
180.9 KB
compas_pb_zstd
179.5 KB
compas_msgpack
305.7 KB
compas_msgpack_zstd
228.6 KB
10,000 elements
json
5.4 MB
json_zip
2.5 MB
compas_pb
2.4 MB
compas_pb_zip
1.8 MB
compas_pb_zstd
1.7 MB
compas_msgpack
3.0 MB
compas_msgpack_zstd
2.2 MB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json554.0 KB1.0×13.6 ms14.8 ms28.4 ms38.2192.9 MB0✓ yes
1,000json_zip253.5 KB2.185×29.8 ms15.7 ms45.5 ms16.5783.0 MB0✓ yes
1,000compas_pb247.1 KB2.242×6.1 ms14.3 ms20.4 ms17.7042.3 MB0✓ yes
1,000compas_pb_zip180.9 KB3.063×10.6 ms14.7 ms25.3 ms12.6122.6 MB0✓ yes
1,000compas_pb_zstd179.5 KB3.087×7.7 ms14.2 ms21.9 ms12.9122.6 MB0✓ yes
1,000compas_msgpack305.7 KB1.813×4.4 ms27.3 ms31.7 ms11.4853.7 MB0✓ yes
1,000compas_msgpack_zstd228.6 KB2.423×6.6 ms27.4 ms33.9 ms8.5584.0 MB0✓ yes
10,000json5.4 MB1.0×135.6 ms177.9 ms313.5 ms31.89929.6 MB0✓ yes
10,000json_zip2.5 MB2.195×292.2 ms182.5 ms474.8 ms14.1629.6 MB0✓ yes
10,000compas_pb2.4 MB2.243×60.9 ms170.5 ms231.4 ms14.83723.2 MB0✓ yes
10,000compas_pb_zip1.8 MB3.066×108.7 ms171.9 ms280.6 ms10.76925.6 MB0✓ yes
10,000compas_pb_zstd1.7 MB3.095×76.2 ms168.7 ms244.9 ms10.86925.6 MB0✓ yes
10,000compas_msgpack3.0 MB1.813×44.0 ms345.8 ms389.8 ms9.05137.4 MB0✓ yes
10,000compas_msgpack_zstd2.2 MB2.478×70.5 ms324.1 ms394.6 ms7.06640.4 MB0✓ yes

projections

Round-trip time
1,000 elements
json
8.2 ms
json_zip
9.9 ms
compas_pb
12.2 ms
compas_pb_zip
13.4 ms
compas_pb_zstd
13.8 ms
compas_msgpack
15.1 ms
compas_msgpack_zstd
16.3 ms
10,000 elements
json
92.7 ms
json_zip
112.7 ms
compas_pb
132.4 ms
compas_pb_zip
141.8 ms
compas_pb_zstd
139.9 ms
compas_msgpack
183.8 ms
compas_msgpack_zstd
195.4 ms
Wire size
1,000 elements
json
188.1 KB
json_zip
35.6 KB
compas_pb
186.5 KB
compas_pb_zip
11.7 KB
compas_pb_zstd
8.8 KB
compas_msgpack
233.4 KB
compas_msgpack_zstd
33.8 KB
10,000 elements
json
1.8 MB
json_zip
352.9 KB
compas_pb
1.8 MB
compas_pb_zip
114.3 KB
compas_pb_zstd
86.0 KB
compas_msgpack
2.3 MB
compas_msgpack_zstd
326.0 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json188.1 KB1.0×3.9 ms4.3 ms8.2 ms45.3021.2 MB0✓ yes
1,000json_zip35.6 KB5.28×5.5 ms4.4 ms9.9 ms8.2981.2 MB0✓ yes
1,000compas_pb186.5 KB1.009×5.3 ms6.9 ms12.2 ms27.671951.2 KB0✓ yes
1,000compas_pb_zip11.7 KB16.112×6.4 ms7.0 ms13.4 ms1.7081.1 MB0✓ yes
1,000compas_pb_zstd8.8 KB21.422×6.2 ms7.6 ms13.8 ms1.191.1 MB0✓ yes
1,000compas_msgpack233.4 KB0.806×1.5 ms13.6 ms15.1 ms17.522.0 MB0✓ yes
1,000compas_msgpack_zstd33.8 KB5.563×2.7 ms13.7 ms16.3 ms2.5352.3 MB0✓ yes
10,000json1.8 MB1.0×38.1 ms54.7 ms92.7 ms35.24712.2 MB0✓ yes
10,000json_zip352.9 KB5.331×55.4 ms57.3 ms112.7 ms6.30412.2 MB0✓ yes
10,000compas_pb1.8 MB1.009×52.6 ms79.8 ms132.4 ms23.9379.4 MB0✓ yes
10,000compas_pb_zip114.3 KB16.461×62.5 ms79.2 ms141.8 ms1.47711.2 MB0✓ yes
10,000compas_pb_zstd86.0 KB21.883×63.1 ms76.8 ms139.9 ms1.14611.2 MB0✓ yes
10,000compas_msgpack2.3 MB0.806×14.6 ms169.2 ms183.8 ms14.12320.5 MB0✓ yes
10,000compas_msgpack_zstd326.0 KB5.771×26.4 ms168.9 ms195.4 ms1.97622.8 MB0✓ yes

quaternions

Round-trip time
1,000 elements
json
7.9 ms
json_zip
11.5 ms
compas_pb
8.3 ms
compas_pb_zip
9.2 ms
compas_pb_zstd
8.8 ms
compas_msgpack
7.6 ms
compas_msgpack_zstd
9.3 ms
10,000 elements
json
79.7 ms
json_zip
115.5 ms
compas_pb
82.8 ms
compas_pb_zip
93.8 ms
compas_pb_zstd
89.1 ms
compas_msgpack
85.3 ms
compas_msgpack_zstd
96.2 ms
Wire size
1,000 elements
json
185.5 KB
json_zip
65.3 KB
compas_pb
90.8 KB
compas_pb_zip
33.8 KB
compas_pb_zstd
33.0 KB
compas_msgpack
124.0 KB
compas_msgpack_zstd
58.3 KB
10,000 elements
json
1.8 MB
json_zip
646.2 KB
compas_pb
908.2 KB
compas_pb_zip
335.7 KB
compas_pb_zstd
327.5 KB
compas_msgpack
1.2 MB
compas_msgpack_zstd
574.2 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json185.5 KB1.0×3.8 ms4.0 ms7.9 ms47.11568.2 KB0✓ yes
1,000json_zip65.3 KB2.84×7.2 ms4.3 ms11.5 ms15.62570.6 KB0✓ yes
1,000compas_pb90.8 KB2.042×3.4 ms4.9 ms8.3 ms18.935283.5 KB0✓ yes
1,000compas_pb_zip33.8 KB5.491×4.1 ms5.0 ms9.2 ms6.849375.6 KB0✓ yes
1,000compas_pb_zstd33.0 KB5.618×3.9 ms5.0 ms8.8 ms6.815374.4 KB0✓ yes
1,000compas_msgpack124.0 KB1.495×1.6 ms6.0 ms7.6 ms21.115985.9 KB0✓ yes
1,000compas_msgpack_zstd58.3 KB3.182×3.2 ms6.1 ms9.3 ms9.8141.1 MB0✓ yes
10,000json1.8 MB1.0×38.5 ms41.2 ms79.7 ms46.0745.6 MB0✓ yes
10,000json_zip646.2 KB2.87×72.2 ms43.3 ms115.5 ms15.2765.6 MB0✓ yes
10,000compas_pb908.2 KB2.042×33.3 ms49.5 ms82.8 ms18.7852.8 MB0✓ yes
10,000compas_pb_zip335.7 KB5.525×42.5 ms51.3 ms93.8 ms6.6993.7 MB0✓ yes
10,000compas_pb_zstd327.5 KB5.664×38.6 ms50.6 ms89.1 ms6.6323.7 MB0✓ yes
10,000compas_msgpack1.2 MB1.496×16.4 ms68.9 ms85.3 ms18.4299.8 MB0✓ yes
10,000compas_msgpack_zstd574.2 KB3.231×27.8 ms68.5 ms96.2 ms8.58811.0 MB0✓ yes

reflections

Round-trip time
1,000 elements
json
8.2 ms
json_zip
9.9 ms
compas_pb
12.9 ms
compas_pb_zip
13.2 ms
compas_pb_zstd
13.0 ms
compas_msgpack
16.5 ms
compas_msgpack_zstd
16.7 ms
10,000 elements
json
105.1 ms
json_zip
116.0 ms
compas_pb
131.3 ms
compas_pb_zip
141.7 ms
compas_pb_zstd
141.9 ms
compas_msgpack
175.5 ms
compas_msgpack_zstd
194.1 ms
Wire size
1,000 elements
json
189.3 KB
json_zip
35.6 KB
compas_pb
186.5 KB
compas_pb_zip
11.6 KB
compas_pb_zstd
8.8 KB
compas_msgpack
233.4 KB
compas_msgpack_zstd
33.7 KB
10,000 elements
json
1.8 MB
json_zip
353.3 KB
compas_pb
1.8 MB
compas_pb_zip
113.9 KB
compas_pb_zstd
85.8 KB
compas_msgpack
2.3 MB
compas_msgpack_zstd
324.9 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json189.3 KB1.0×3.9 ms4.3 ms8.2 ms45.3521.2 MB0✓ yes
1,000json_zip35.6 KB5.313×5.5 ms4.5 ms9.9 ms8.21.2 MB0✓ yes
1,000compas_pb186.5 KB1.015×5.3 ms7.5 ms12.9 ms25.423951.2 KB0✓ yes
1,000compas_pb_zip11.6 KB16.259×6.3 ms6.9 ms13.2 ms1.7331.1 MB0✓ yes
1,000compas_pb_zstd8.8 KB21.601×6.1 ms6.9 ms13.0 ms1.3061.1 MB0✓ yes
1,000compas_msgpack233.4 KB0.811×1.5 ms15.1 ms16.5 ms15.8692.0 MB0✓ yes
1,000compas_msgpack_zstd33.7 KB5.622×2.7 ms14.1 ms16.7 ms2.4552.3 MB0✓ yes
10,000json1.8 MB1.0×38.5 ms66.6 ms105.1 ms29.14112.2 MB0✓ yes
10,000json_zip353.3 KB5.36×54.6 ms61.3 ms116.0 ms5.89912.2 MB0✓ yes
10,000compas_pb1.8 MB1.015×53.1 ms78.2 ms131.3 ms24.4159.4 MB0✓ yes
10,000compas_pb_zip113.9 KB16.634×63.5 ms78.2 ms141.7 ms1.4911.2 MB0✓ yes
10,000compas_pb_zstd85.8 KB22.069×61.0 ms80.9 ms141.9 ms1.08611.2 MB0✓ yes
10,000compas_msgpack2.3 MB0.811×15.0 ms160.5 ms175.5 ms14.8920.6 MB0✓ yes
10,000compas_msgpack_zstd324.9 KB5.83×25.7 ms168.4 ms194.1 ms1.97622.8 MB0✓ yes

rotations

Round-trip time
1,000 elements
json
9.2 ms
json_zip
12.5 ms
compas_pb
11.2 ms
compas_pb_zip
12.5 ms
compas_pb_zstd
12.3 ms
compas_msgpack
15.0 ms
compas_msgpack_zstd
16.9 ms
10,000 elements
json
106.4 ms
json_zip
135.2 ms
compas_pb
124.9 ms
compas_pb_zip
136.0 ms
compas_pb_zstd
134.0 ms
compas_msgpack
192.4 ms
compas_msgpack_zstd
204.4 ms
Wire size
1,000 elements
json
233.3 KB
json_zip
51.8 KB
compas_pb
184.6 KB
compas_pb_zip
22.5 KB
compas_pb_zstd
21.6 KB
compas_msgpack
231.4 KB
compas_msgpack_zstd
50.3 KB
10,000 elements
json
2.3 MB
json_zip
514.7 KB
compas_pb
1.8 MB
compas_pb_zip
222.4 KB
compas_pb_zstd
214.2 KB
compas_msgpack
2.3 MB
compas_msgpack_zstd
489.3 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json233.3 KB1.0×4.6 ms4.6 ms9.2 ms52.3351.3 MB0✓ yes
1,000json_zip51.8 KB4.503×7.7 ms4.8 ms12.5 ms11.0041.3 MB0✓ yes
1,000compas_pb184.6 KB1.264×5.3 ms5.9 ms11.2 ms32.086951.2 KB0✓ yes
1,000compas_pb_zip22.5 KB10.381×6.3 ms6.1 ms12.5 ms3.7471.1 MB0✓ yes
1,000compas_pb_zstd21.6 KB10.81×6.4 ms5.9 ms12.3 ms3.7331.1 MB0✓ yes
1,000compas_msgpack231.4 KB1.008×1.5 ms13.6 ms15.0 ms17.4672.0 MB0✓ yes
1,000compas_msgpack_zstd50.3 KB4.633×3.0 ms13.9 ms16.9 ms3.7132.3 MB0✓ yes
10,000json2.3 MB1.0×46.5 ms60.0 ms106.4 ms39.82412.6 MB0✓ yes
10,000json_zip514.7 KB4.532×75.7 ms59.5 ms135.2 ms8.85812.6 MB0✓ yes
10,000compas_pb1.8 MB1.264×54.1 ms70.9 ms124.9 ms26.6649.4 MB0✓ yes
10,000compas_pb_zip222.4 KB10.487×64.1 ms71.9 ms136.0 ms3.16711.2 MB0✓ yes
10,000compas_pb_zstd214.2 KB10.89×65.1 ms68.9 ms134.0 ms3.18611.2 MB0✓ yes
10,000compas_msgpack2.3 MB1.008×16.2 ms176.2 ms192.4 ms13.45120.5 MB0✓ yes
10,000compas_msgpack_zstd489.3 KB4.768×28.2 ms176.3 ms204.4 ms2.84222.8 MB0✓ yes

scales

Round-trip time
1,000 elements
json
8.8 ms
json_zip
13.4 ms
compas_pb
12.4 ms
compas_pb_zip
13.3 ms
compas_pb_zstd
13.8 ms
compas_msgpack
15.1 ms
compas_msgpack_zstd
17.2 ms
10,000 elements
json
106.0 ms
json_zip
130.1 ms
compas_pb
135.7 ms
compas_pb_zip
144.9 ms
compas_pb_zstd
142.3 ms
compas_msgpack
174.9 ms
compas_msgpack_zstd
203.1 ms
Wire size
1,000 elements
json
211.0 KB
json_zip
56.7 KB
compas_pb
181.7 KB
compas_pb_zip
26.0 KB
compas_pb_zstd
24.3 KB
compas_msgpack
228.5 KB
compas_msgpack_zstd
51.6 KB
10,000 elements
json
2.1 MB
json_zip
561.2 KB
compas_pb
1.8 MB
compas_pb_zip
257.3 KB
compas_pb_zstd
241.6 KB
compas_msgpack
2.2 MB
compas_msgpack_zstd
503.3 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json211.0 KB1.0×4.3 ms4.5 ms8.8 ms48.1111.2 MB0✓ yes
1,000json_zip56.7 KB3.725×8.1 ms5.4 ms13.4 ms10.8021.2 MB0✓ yes
1,000compas_pb181.7 KB1.162×5.4 ms6.9 ms12.4 ms26.773951.2 KB0✓ yes
1,000compas_pb_zip26.0 KB8.123×6.1 ms7.2 ms13.3 ms3.6941.1 MB0✓ yes
1,000compas_pb_zstd24.3 KB8.694×6.9 ms6.9 ms13.8 ms3.6061.1 MB0✓ yes
1,000compas_msgpack228.5 KB0.923×1.5 ms13.7 ms15.1 ms17.1222.0 MB0✓ yes
1,000compas_msgpack_zstd51.6 KB4.09×3.3 ms13.9 ms17.2 ms3.7972.3 MB0✓ yes
10,000json2.1 MB1.0×44.4 ms61.6 ms106.0 ms35.06712.4 MB0✓ yes
10,000json_zip561.2 KB3.759×71.2 ms58.9 ms130.1 ms9.75512.4 MB0✓ yes
10,000compas_pb1.8 MB1.161×54.1 ms81.7 ms135.7 ms22.7799.4 MB0✓ yes
10,000compas_pb_zip257.3 KB8.198×63.4 ms81.5 ms144.9 ms3.23211.2 MB0✓ yes
10,000compas_pb_zstd241.6 KB8.731×64.2 ms78.1 ms142.3 ms3.16811.2 MB0✓ yes
10,000compas_msgpack2.2 MB0.923×14.7 ms160.2 ms174.9 ms14.60320.5 MB0✓ yes
10,000compas_msgpack_zstd503.3 KB4.192×32.2 ms170.9 ms203.1 ms3.01622.7 MB0✓ yes

shears

Round-trip time
1,000 elements
json
8.1 ms
json_zip
9.9 ms
compas_pb
12.4 ms
compas_pb_zip
12.9 ms
compas_pb_zstd
13.1 ms
compas_msgpack
15.0 ms
compas_msgpack_zstd
16.4 ms
10,000 elements
json
98.3 ms
json_zip
109.7 ms
compas_pb
135.6 ms
compas_pb_zip
136.5 ms
compas_pb_zstd
141.9 ms
compas_msgpack
186.0 ms
compas_msgpack_zstd
197.6 ms
Wire size
1,000 elements
json
185.7 KB
json_zip
34.4 KB
compas_pb
181.7 KB
compas_pb_zip
9.1 KB
compas_pb_zstd
7.9 KB
compas_msgpack
228.5 KB
compas_msgpack_zstd
33.5 KB
10,000 elements
json
1.8 MB
json_zip
340.5 KB
compas_pb
1.8 MB
compas_pb_zip
88.4 KB
compas_pb_zstd
77.5 KB
compas_msgpack
2.2 MB
compas_msgpack_zstd
324.3 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json185.7 KB1.0×3.8 ms4.3 ms8.1 ms44.611.2 MB0✓ yes
1,000json_zip34.4 KB5.407×5.4 ms4.5 ms9.9 ms7.861.2 MB0✓ yes
1,000compas_pb181.7 KB1.022×5.5 ms7.0 ms12.4 ms26.634951.2 KB0✓ yes
1,000compas_pb_zip9.1 KB20.491×5.9 ms7.1 ms12.9 ms1.3161.1 MB0✓ yes
1,000compas_pb_zstd7.9 KB23.528×6.1 ms7.0 ms13.1 ms1.1481.1 MB0✓ yes
1,000compas_msgpack228.5 KB0.813×1.5 ms13.5 ms15.0 ms17.2712.0 MB0✓ yes
1,000compas_msgpack_zstd33.5 KB5.551×2.6 ms13.7 ms16.4 ms2.4962.3 MB0✓ yes
10,000json1.8 MB1.0×38.2 ms60.2 ms98.3 ms31.60312.1 MB0✓ yes
10,000json_zip340.5 KB5.455×54.2 ms55.5 ms109.7 ms6.28512.2 MB0✓ yes
10,000compas_pb1.8 MB1.023×54.4 ms81.2 ms135.6 ms22.919.4 MB0✓ yes
10,000compas_pb_zip88.4 KB21.017×57.7 ms78.9 ms136.5 ms1.14811.2 MB0✓ yes
10,000compas_pb_zstd77.5 KB23.971×62.8 ms79.1 ms141.9 ms1.00311.2 MB0✓ yes
10,000compas_msgpack2.2 MB0.813×14.8 ms171.2 ms186.0 ms13.66520.5 MB0✓ yes
10,000compas_msgpack_zstd324.3 KB5.728×25.1 ms172.5 ms197.6 ms1.92522.7 MB0✓ yes

spheres

Round-trip time
1,000 elements
json
47.1 ms
json_zip
52.3 ms
compas_pb
54.2 ms
compas_pb_zip
55.9 ms
compas_pb_zstd
56.0 ms
compas_msgpack
55.8 ms
compas_msgpack_zstd
53.0 ms
10,000 elements
json
495.0 ms
json_zip
532.8 ms
compas_pb
513.1 ms
compas_pb_zip
549.5 ms
compas_pb_zstd
551.1 ms
compas_msgpack
514.6 ms
compas_msgpack_zstd
517.7 ms
Wire size
1,000 elements
json
231.7 KB
json_zip
66.7 KB
compas_pb
112.3 KB
compas_pb_zip
34.3 KB
compas_pb_zstd
32.4 KB
compas_msgpack
199.2 KB
compas_msgpack_zstd
60.7 KB
10,000 elements
json
2.3 MB
json_zip
661.4 KB
compas_pb
1.1 MB
compas_pb_zip
340.4 KB
compas_pb_zstd
326.9 KB
compas_msgpack
1.9 MB
compas_msgpack_zstd
594.8 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json231.7 KB1.0×6.9 ms40.1 ms47.1 ms5.911.6 MB0✓ yes
1,000json_zip66.7 KB3.472×10.7 ms41.6 ms52.3 ms1.6421.6 MB0✓ yes
1,000compas_pb112.3 KB2.063×7.4 ms46.8 ms54.2 ms2.4591.2 MB0✓ yes
1,000compas_pb_zip34.3 KB6.756×8.6 ms47.3 ms55.9 ms0.7421.3 MB0✓ yes
1,000compas_pb_zstd32.4 KB7.145×8.2 ms47.8 ms56.0 ms0.6941.3 MB0✓ yes
1,000compas_msgpack199.2 KB1.163×4.3 ms51.5 ms55.8 ms3.9632.5 MB0✓ yes
1,000compas_msgpack_zstd60.7 KB3.814×5.9 ms47.0 ms53.0 ms1.3232.7 MB0✓ yes
10,000json2.3 MB1.0×70.0 ms425.0 ms495.0 ms5.58215.6 MB0✓ yes
10,000json_zip661.4 KB3.503×101.3 ms431.5 ms532.8 ms1.5715.6 MB0✓ yes
10,000compas_pb1.1 MB2.063×64.2 ms448.8 ms513.1 ms2.56212.4 MB0✓ yes
10,000compas_pb_zip340.4 KB6.805×78.0 ms471.4 ms549.5 ms0.73913.5 MB0✓ yes
10,000compas_pb_zstd326.9 KB7.086×74.5 ms476.7 ms551.1 ms0.70213.5 MB0✓ yes
10,000compas_msgpack1.9 MB1.163×33.6 ms481.0 ms514.6 ms4.24125.2 MB0✓ yes
10,000compas_msgpack_zstd594.8 KB3.895×47.1 ms470.6 ms517.7 ms1.29427.1 MB0✓ yes

toruses

Round-trip time
1,000 elements
json
47.3 ms
json_zip
50.3 ms
compas_pb
51.7 ms
compas_pb_zip
53.1 ms
compas_pb_zstd
52.8 ms
compas_msgpack
49.2 ms
compas_msgpack_zstd
51.6 ms
10,000 elements
json
470.1 ms
json_zip
514.5 ms
compas_pb
522.3 ms
compas_pb_zip
535.9 ms
compas_pb_zstd
534.7 ms
compas_msgpack
514.2 ms
compas_msgpack_zstd
542.3 ms
Wire size
1,000 elements
json
267.5 KB
json_zip
77.2 KB
compas_pb
120.1 KB
compas_pb_zip
42.3 KB
compas_pb_zstd
40.4 KB
compas_msgpack
223.6 KB
compas_msgpack_zstd
69.0 KB
10,000 elements
json
2.6 MB
json_zip
765.6 KB
compas_pb
1.2 MB
compas_pb_zip
420.9 KB
compas_pb_zstd
400.8 KB
compas_msgpack
2.2 MB
compas_msgpack_zstd
676.7 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json267.5 KB1.0×7.2 ms40.1 ms47.3 ms6.8321.6 MB0✓ yes
1,000json_zip77.2 KB3.466×10.6 ms39.7 ms50.3 ms1.9881.6 MB0✓ yes
1,000compas_pb120.1 KB2.227×6.8 ms44.9 ms51.7 ms2.741.3 MB0✓ yes
1,000compas_pb_zip42.3 KB6.318×8.1 ms45.0 ms53.1 ms0.9641.4 MB0✓ yes
1,000compas_pb_zstd40.4 KB6.617×7.6 ms45.2 ms52.8 ms0.9161.4 MB0✓ yes
1,000compas_msgpack223.6 KB1.196×3.4 ms45.8 ms49.2 ms5.0022.6 MB0✓ yes
1,000compas_msgpack_zstd69.0 KB3.874×5.3 ms46.3 ms51.6 ms1.5272.8 MB0✓ yes
10,000json2.6 MB1.0×69.5 ms400.6 ms470.1 ms6.83716.2 MB0✓ yes
10,000json_zip765.6 KB3.494×105.2 ms409.3 ms514.5 ms1.91616.2 MB0✓ yes
10,000compas_pb1.2 MB2.227×67.8 ms454.5 ms522.3 ms2.70612.6 MB0✓ yes
10,000compas_pb_zip420.9 KB6.356×81.4 ms454.5 ms535.9 ms0.94813.8 MB0✓ yes
10,000compas_pb_zstd400.8 KB6.674×73.4 ms461.3 ms534.7 ms0.8913.8 MB0✓ yes
10,000compas_msgpack2.2 MB1.196×34.2 ms480.0 ms514.2 ms4.77126.5 MB0✓ yes
10,000compas_msgpack_zstd676.7 KB3.953×49.9 ms492.4 ms542.3 ms1.40728.7 MB0✓ yes

transformations

Round-trip time
1,000 elements
json
9.1 ms
json_zip
11.8 ms
compas_pb
12.1 ms
compas_pb_zip
13.6 ms
compas_pb_zstd
13.9 ms
compas_msgpack
14.9 ms
compas_msgpack_zstd
17.2 ms
10,000 elements
json
105.1 ms
json_zip
130.2 ms
compas_pb
133.2 ms
compas_pb_zip
143.8 ms
compas_pb_zstd
143.5 ms
compas_msgpack
183.5 ms
compas_msgpack_zstd
204.1 ms
Wire size
1,000 elements
json
219.8 KB
json_zip
58.3 KB
compas_pb
190.4 KB
compas_pb_zip
28.3 KB
compas_pb_zstd
23.6 KB
compas_msgpack
237.3 KB
compas_msgpack_zstd
51.6 KB
10,000 elements
json
2.1 MB
json_zip
578.6 KB
compas_pb
1.9 MB
compas_pb_zip
278.4 KB
compas_pb_zstd
242.5 KB
compas_msgpack
2.3 MB
compas_msgpack_zstd
503.0 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json219.8 KB1.0×4.6 ms4.5 ms9.1 ms49.9261.2 MB0✓ yes
1,000json_zip58.3 KB3.767×7.1 ms4.7 ms11.8 ms12.5921.2 MB0✓ yes
1,000compas_pb190.4 KB1.154×5.3 ms6.8 ms12.1 ms28.555951.1 KB0✓ yes
1,000compas_pb_zip28.3 KB7.78×6.7 ms6.9 ms13.6 ms4.1681.1 MB0✓ yes
1,000compas_pb_zstd23.6 KB9.327×6.8 ms7.1 ms13.9 ms3.3841.1 MB0✓ yes
1,000compas_msgpack237.3 KB0.926×1.5 ms13.5 ms14.9 ms18.0382.0 MB0✓ yes
1,000compas_msgpack_zstd51.6 KB4.26×3.3 ms13.9 ms17.2 ms3.812.3 MB0✓ yes
10,000json2.1 MB1.0×44.5 ms60.6 ms105.1 ms37.12312.5 MB0✓ yes
10,000json_zip578.6 KB3.799×70.4 ms59.7 ms130.2 ms9.91912.5 MB0✓ yes
10,000compas_pb1.9 MB1.154×53.4 ms79.8 ms133.2 ms24.4449.4 MB0✓ yes
10,000compas_pb_zip278.4 KB7.895×65.2 ms78.6 ms143.8 ms3.62611.2 MB0✓ yes
10,000compas_pb_zstd242.5 KB9.063×66.0 ms77.5 ms143.5 ms3.20411.2 MB0✓ yes
10,000compas_msgpack2.3 MB0.926×15.0 ms168.5 ms183.5 ms14.41920.6 MB0✓ yes
10,000compas_msgpack_zstd503.0 KB4.369×32.9 ms171.1 ms204.1 ms3.0122.9 MB0✓ yes

translations

Round-trip time
1,000 elements
json
9.0 ms
json_zip
11.8 ms
compas_pb
14.0 ms
compas_pb_zip
15.3 ms
compas_pb_zstd
14.5 ms
compas_msgpack
14.9 ms
compas_msgpack_zstd
17.3 ms
10,000 elements
json
105.6 ms
json_zip
129.7 ms
compas_pb
151.6 ms
compas_pb_zip
159.1 ms
compas_pb_zstd
154.7 ms
compas_msgpack
188.0 ms
compas_msgpack_zstd
205.2 ms
Wire size
1,000 elements
json
216.9 KB
json_zip
58.4 KB
compas_pb
85.0 KB
compas_pb_zip
25.5 KB
compas_pb_zstd
24.4 KB
compas_msgpack
234.4 KB
compas_msgpack_zstd
51.5 KB
10,000 elements
json
2.1 MB
json_zip
578.2 KB
compas_pb
849.6 KB
compas_pb_zip
252.7 KB
compas_pb_zstd
241.7 KB
compas_msgpack
2.3 MB
compas_msgpack_zstd
502.9 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json216.9 KB1.0×4.5 ms4.5 ms9.0 ms49.391.2 MB0✓ yes
1,000json_zip58.4 KB3.716×6.9 ms4.9 ms11.8 ms12.2441.2 MB0✓ yes
1,000compas_pb85.0 KB2.552×5.4 ms8.5 ms14.0 ms10.184646.7 KB0✓ yes
1,000compas_pb_zip25.5 KB8.502×6.2 ms9.1 ms15.3 ms2.885732.9 KB0✓ yes
1,000compas_pb_zstd24.4 KB8.895×5.8 ms8.7 ms14.5 ms2.868731.7 KB0✓ yes
1,000compas_msgpack234.4 KB0.925×1.5 ms13.4 ms14.9 ms17.8632.0 MB0✓ yes
1,000compas_msgpack_zstd51.5 KB4.21×3.3 ms13.9 ms17.3 ms3.7862.3 MB0✓ yes
10,000json2.1 MB1.0×44.2 ms61.3 ms105.6 ms36.20112.5 MB0✓ yes
10,000json_zip578.2 KB3.751×70.0 ms59.6 ms129.7 ms9.93212.5 MB0✓ yes
10,000compas_pb849.6 KB2.552×53.4 ms98.2 ms151.6 ms8.8596.4 MB0✓ yes
10,000compas_pb_zip252.7 KB8.58×61.5 ms97.6 ms159.1 ms2.6517.2 MB0✓ yes
10,000compas_pb_zstd241.7 KB8.974×57.2 ms97.5 ms154.7 ms2.5387.2 MB0✓ yes
10,000compas_msgpack2.3 MB0.925×14.7 ms173.3 ms188.0 ms13.8520.6 MB0✓ yes
10,000compas_msgpack_zstd502.9 KB4.312×31.5 ms173.8 ms205.2 ms2.96322.8 MB0✓ yes

vectors

Round-trip time
1,000 elements
json
7.3 ms
json_zip
10.3 ms
compas_pb
8.2 ms
compas_pb_zip
9.0 ms
compas_pb_zstd
8.7 ms
compas_msgpack
7.0 ms
compas_msgpack_zstd
8.6 ms
10,000 elements
json
73.3 ms
json_zip
99.1 ms
compas_pb
81.8 ms
compas_pb_zip
89.9 ms
compas_pb_zstd
86.8 ms
compas_msgpack
71.0 ms
compas_msgpack_zstd
80.7 ms
Wire size
1,000 elements
json
142.6 KB
json_zip
54.7 KB
compas_pb
78.1 KB
compas_pb_zip
25.5 KB
compas_pb_zstd
24.4 KB
compas_msgpack
103.5 KB
compas_msgpack_zstd
50.6 KB
10,000 elements
json
1.4 MB
json_zip
540.0 KB
compas_pb
781.3 KB
compas_pb_zip
252.6 KB
compas_pb_zstd
244.1 KB
compas_msgpack
1.0 MB
compas_msgpack_zstd
495.1 KB
sizeformatwire sizevs JSONdumploadround-tripload MB/speak memmax errlossless
1,000json142.6 KB1.0×3.6 ms3.8 ms7.3 ms38.64501.7 KB0✓ yes
1,000json_zip54.7 KB2.61×5.8 ms4.5 ms10.3 ms12.567504.1 KB0✓ yes
1,000compas_pb78.1 KB1.826×3.2 ms5.0 ms8.2 ms16.125260.1 KB0✓ yes
1,000compas_pb_zip25.5 KB5.597×4.0 ms5.0 ms9.0 ms5.189339.4 KB0✓ yes
1,000compas_pb_zstd24.4 KB5.852×3.7 ms5.0 ms8.7 ms5.026338.3 KB0✓ yes
1,000compas_msgpack103.5 KB1.378×1.7 ms5.3 ms7.0 ms19.951805.7 KB0✓ yes
1,000compas_msgpack_zstd50.6 KB2.819×2.8 ms5.8 ms8.6 ms8.977909.2 KB0✓ yes
10,000json1.4 MB1.0×35.0 ms38.3 ms73.3 ms38.0894.9 MB0✓ yes
10,000json_zip540.0 KB2.641×58.8 ms40.3 ms99.1 ms13.7294.9 MB0✓ yes
10,000compas_pb781.3 KB1.826×32.1 ms49.7 ms81.8 ms16.0922.6 MB0✓ yes
10,000compas_pb_zip252.6 KB5.647×39.2 ms50.8 ms89.9 ms5.0963.4 MB0✓ yes
10,000compas_pb_zstd244.1 KB5.842×36.4 ms50.4 ms86.8 ms4.9593.4 MB0✓ yes
10,000compas_msgpack1.0 MB1.378×16.8 ms54.2 ms71.0 ms19.5648.1 MB0✓ yes
10,000compas_msgpack_zstd495.1 KB2.881×25.6 ms55.1 ms80.7 ms9.2039.1 MB0✓ yes
\ No newline at end of file diff --git a/benchmarks/serialization/results/samples/README.md b/benchmarks/serialization/results/samples/README.md new file mode 100644 index 000000000000..598cf0487ffd --- /dev/null +++ b/benchmarks/serialization/results/samples/README.md @@ -0,0 +1,19 @@ +# Encoded-format samples + +Tiny, fully-inspectable fixtures encoded with the three main (uncompressed) formats, so you +can see the *shape* of each encoding. Regenerate with: + + python -m benchmarks.serialization.samples + +Per subject: + +| file | what it is | +|------|------------| +| `.json` | JSON wire format (text) | +| `.pb` | raw protobuf bytes | +| `.pb.json` | protobuf message deserialized + re-serialized to JSON (readable wire structure) | +| `.msgpack` | raw MessagePack bytes | +| `.msgpack.json` | decoded MessagePack tree as JSON (row-oriented `{dtype, data}`) | + +`*.pb.json` shows the schema'd/columnar layout (flat vertex arrays, CSR faces, attribute +columns); `*.msgpack.json` shows the row-oriented dict tree. diff --git a/benchmarks/serialization/results/samples/boxes.json b/benchmarks/serialization/results/samples/boxes.json new file mode 100644 index 000000000000..eb81fba2c3ba --- /dev/null +++ b/benchmarks/serialization/results/samples/boxes.json @@ -0,0 +1,54 @@ +[ + { + "data": { + "frame": { + "point": [ + 27.885359691576753, + -94.99784895546661, + -44.99413632617615 + ], + "xaxis": [ + 1.0, + 0.0, + 0.0 + ], + "yaxis": [ + 0.0, + 1.0, + 0.0 + ] + }, + "xsize": 3.008896643339405, + "ysize": 7.628240927476112, + "zsize": 7.0902953868062015 + }, + "dtype": "compas.geometry/Box", + "guid": "c4c4c702-df70-425e-8bce-717e52b47e39" + }, + { + "data": { + "frame": { + "point": [ + 78.43591354096907, + -82.61223347411678, + -15.61563606294591 + ], + "xaxis": [ + 1.0, + 0.0, + 0.0 + ], + "yaxis": [ + 0.0, + 1.0, + 0.0 + ] + }, + "xsize": 1.268174974942633, + "ysize": 2.9677417732324303, + "zsize": 5.548197592930261 + }, + "dtype": "compas.geometry/Box", + "guid": "47b4989a-ca6c-442e-827c-82917dee3c27" + } +] \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/boxes.msgpack b/benchmarks/serialization/results/samples/boxes.msgpack new file mode 100644 index 000000000000..3dd6c5c1bb90 Binary files /dev/null and b/benchmarks/serialization/results/samples/boxes.msgpack differ diff --git a/benchmarks/serialization/results/samples/boxes.msgpack.json b/benchmarks/serialization/results/samples/boxes.msgpack.json new file mode 100644 index 000000000000..cf54c9a375a0 --- /dev/null +++ b/benchmarks/serialization/results/samples/boxes.msgpack.json @@ -0,0 +1,54 @@ +[ + { + "dtype": "compas.geometry/Box", + "data": { + "xsize": 3.008896643339405, + "ysize": 7.628240927476112, + "zsize": 7.0902953868062015, + "frame": { + "point": [ + 27.885359691576753, + -94.99784895546661, + -44.99413632617615 + ], + "xaxis": [ + 1.0, + 0.0, + 0.0 + ], + "yaxis": [ + 0.0, + 1.0, + 0.0 + ] + } + }, + "guid": "a1921a07-602b-42c4-afa6-9c0c5291bc53" + }, + { + "dtype": "compas.geometry/Box", + "data": { + "xsize": 1.268174974942633, + "ysize": 2.9677417732324303, + "zsize": 5.548197592930261, + "frame": { + "point": [ + 78.43591354096907, + -82.61223347411678, + -15.61563606294591 + ], + "xaxis": [ + 1.0, + 0.0, + 0.0 + ], + "yaxis": [ + 0.0, + 1.0, + 0.0 + ] + } + }, + "guid": "4b8fdc6a-39cd-4522-a354-cddec283e41a" + } +] \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/boxes.pb b/benchmarks/serialization/results/samples/boxes.pb new file mode 100644 index 000000000000..e4eab798e6dd Binary files /dev/null and b/benchmarks/serialization/results/samples/boxes.pb differ diff --git a/benchmarks/serialization/results/samples/boxes.pb.json b/benchmarks/serialization/results/samples/boxes.pb.json new file mode 100644 index 000000000000..0124a81c2c7f --- /dev/null +++ b/benchmarks/serialization/results/samples/boxes.pb.json @@ -0,0 +1,51 @@ +{ + "data": { + "listValue": { + "items": [ + { + "message": { + "@type": "type.googleapis.com/compas_pb.data.BoxData", + "frame": { + "point": { + "x": 27.885359691576753, + "y": -94.99784895546661, + "z": -44.99413632617615 + }, + "xaxis": { + "x": 1.0 + }, + "yaxis": { + "y": 1.0 + } + }, + "xsize": 3.008896643339405, + "ysize": 7.628240927476112, + "zsize": 7.0902953868062015 + } + }, + { + "message": { + "@type": "type.googleapis.com/compas_pb.data.BoxData", + "frame": { + "point": { + "x": 78.43591354096907, + "y": -82.61223347411678, + "z": -15.61563606294591 + }, + "xaxis": { + "x": 1.0 + }, + "yaxis": { + "y": 1.0 + } + }, + "xsize": 1.268174974942633, + "ysize": 2.9677417732324303, + "zsize": 5.548197592930261 + } + } + ] + } + }, + "version": "0.5.0" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/graph.json b/benchmarks/serialization/results/samples/graph.json new file mode 100644 index 000000000000..74119d817f42 --- /dev/null +++ b/benchmarks/serialization/results/samples/graph.json @@ -0,0 +1,50 @@ +{ + "data": { + "attributes": {}, + "default_edge_attributes": {}, + "default_node_attributes": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "edge": { + "0": { + "1": {}, + "2": {} + }, + "1": { + "3": {} + }, + "2": { + "3": {} + }, + "3": {} + }, + "max_node": 3, + "node": { + "0": { + "x": 0.0, + "y": 0.0, + "z": 0.13942679845788375 + }, + "1": { + "x": 1.0, + "y": 0.0, + "z": -0.47498924477733306 + }, + "2": { + "x": 0.0, + "y": 1.0, + "z": -0.22497068163088074 + }, + "3": { + "x": 1.0, + "y": 1.0, + "z": -0.27678926185117725 + } + } + }, + "dtype": "compas.datastructures/Graph", + "guid": "96187abe-6c05-4bb9-a7cd-a67660f77315", + "inheritance": [] +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/graph.msgpack b/benchmarks/serialization/results/samples/graph.msgpack new file mode 100644 index 000000000000..c099afadad3a Binary files /dev/null and b/benchmarks/serialization/results/samples/graph.msgpack differ diff --git a/benchmarks/serialization/results/samples/graph.msgpack.json b/benchmarks/serialization/results/samples/graph.msgpack.json new file mode 100644 index 000000000000..c139e08a598c --- /dev/null +++ b/benchmarks/serialization/results/samples/graph.msgpack.json @@ -0,0 +1,50 @@ +{ + "dtype": "compas.datastructures/Graph", + "data": { + "attributes": {}, + "default_node_attributes": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "default_edge_attributes": {}, + "node": { + "0": { + "x": 0.0, + "y": 0.0, + "z": 0.13942679845788375 + }, + "1": { + "x": 1.0, + "y": 0.0, + "z": -0.47498924477733306 + }, + "2": { + "x": 0.0, + "y": 1.0, + "z": -0.22497068163088074 + }, + "3": { + "x": 1.0, + "y": 1.0, + "z": -0.27678926185117725 + } + }, + "edge": { + "0": { + "1": {}, + "2": {} + }, + "1": { + "3": {} + }, + "2": { + "3": {} + }, + "3": {} + }, + "max_node": 3 + }, + "inheritance": [], + "guid": "d1e71e56-3956-4330-ace4-454027ac4218" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/graph.pb b/benchmarks/serialization/results/samples/graph.pb new file mode 100644 index 000000000000..8f73cc10cb7f Binary files /dev/null and b/benchmarks/serialization/results/samples/graph.pb differ diff --git a/benchmarks/serialization/results/samples/graph.pb.json b/benchmarks/serialization/results/samples/graph.pb.json new file mode 100644 index 000000000000..77f5457c286e --- /dev/null +++ b/benchmarks/serialization/results/samples/graph.pb.json @@ -0,0 +1,74 @@ +{ + "data": { + "message": { + "@type": "type.googleapis.com/compas_pb.data.GraphData", + "nodeKeys": [ + { + "intValue": "0" + }, + { + "intValue": "1" + }, + { + "intValue": "2" + }, + { + "intValue": "3" + } + ], + "nodeAttributes": [ + { + "name": "x", + "doubles": [ + 0.0, + 1.0, + 0.0, + 1.0 + ] + }, + { + "name": "y", + "doubles": [ + 0.0, + 0.0, + 1.0, + 1.0 + ] + }, + { + "name": "z", + "doubles": [ + 0.13942679845788375, + -0.47498924477733306, + -0.22497068163088074, + -0.27678926185117725 + ] + } + ], + "defaultNodeAttributes": { + "z": { + "doubleValue": 0.0 + }, + "y": { + "doubleValue": 0.0 + }, + "x": { + "doubleValue": 0.0 + } + }, + "edgeU": [ + 0, + 0, + 1, + 2 + ], + "edgeV": [ + 1, + 2, + 3, + 3 + ] + } + }, + "version": "0.5.0" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/mesh.json b/benchmarks/serialization/results/samples/mesh.json new file mode 100644 index 000000000000..b0013cd31b96 --- /dev/null +++ b/benchmarks/serialization/results/samples/mesh.json @@ -0,0 +1,55 @@ +{ + "data": { + "attributes": {}, + "default_edge_attributes": {}, + "default_face_attributes": {}, + "default_vertex_attributes": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "edgedata": {}, + "face": { + "0": [ + 0, + 1, + 3, + 2 + ] + }, + "facedata": { + "0": {} + }, + "max_face": 0, + "max_vertex": 3, + "vertex": { + "0": { + "quality": 0.7364712141640124, + "x": 0.0, + "y": 0.0, + "z": 0.13942679845788375 + }, + "1": { + "quality": 0.6766994874229113, + "x": 1.0, + "y": 0.0, + "z": -0.47498924477733306 + }, + "2": { + "quality": 0.8921795677048454, + "x": 0.0, + "y": 1.0, + "z": -0.22497068163088074 + }, + "3": { + "quality": 0.08693883262941615, + "x": 1.0, + "y": 1.0, + "z": -0.27678926185117725 + } + } + }, + "dtype": "compas.datastructures/Mesh", + "guid": "9643d23f-c061-4ee0-8080-3ca93119f0d0", + "inheritance": [] +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/mesh.msgpack b/benchmarks/serialization/results/samples/mesh.msgpack new file mode 100644 index 000000000000..2c72216983c1 Binary files /dev/null and b/benchmarks/serialization/results/samples/mesh.msgpack differ diff --git a/benchmarks/serialization/results/samples/mesh.msgpack.json b/benchmarks/serialization/results/samples/mesh.msgpack.json new file mode 100644 index 000000000000..77314507181f --- /dev/null +++ b/benchmarks/serialization/results/samples/mesh.msgpack.json @@ -0,0 +1,55 @@ +{ + "dtype": "compas.datastructures/Mesh", + "data": { + "attributes": {}, + "default_vertex_attributes": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "default_edge_attributes": {}, + "default_face_attributes": {}, + "vertex": { + "0": { + "x": 0.0, + "y": 0.0, + "z": 0.13942679845788375, + "quality": 0.7364712141640124 + }, + "1": { + "x": 1.0, + "y": 0.0, + "z": -0.47498924477733306, + "quality": 0.6766994874229113 + }, + "2": { + "x": 0.0, + "y": 1.0, + "z": -0.22497068163088074, + "quality": 0.8921795677048454 + }, + "3": { + "x": 1.0, + "y": 1.0, + "z": -0.27678926185117725, + "quality": 0.08693883262941615 + } + }, + "face": { + "0": [ + 0, + 1, + 3, + 2 + ] + }, + "facedata": { + "0": {} + }, + "edgedata": {}, + "max_vertex": 3, + "max_face": 0 + }, + "inheritance": [], + "guid": "05b98985-c320-4c4a-91d4-00c451e27a73" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/mesh.pb b/benchmarks/serialization/results/samples/mesh.pb new file mode 100644 index 000000000000..ed82d47c134d Binary files /dev/null and b/benchmarks/serialization/results/samples/mesh.pb differ diff --git a/benchmarks/serialization/results/samples/mesh.pb.json b/benchmarks/serialization/results/samples/mesh.pb.json new file mode 100644 index 000000000000..3085fc140536 --- /dev/null +++ b/benchmarks/serialization/results/samples/mesh.pb.json @@ -0,0 +1,53 @@ +{ + "data": { + "message": { + "@type": "type.googleapis.com/compas_pb.data.MeshData", + "vertices": [ + 0.0, + 0.0, + 0.13942679845788375, + 1.0, + 0.0, + -0.47498924477733306, + 0.0, + 1.0, + -0.22497068163088074, + 1.0, + 1.0, + -0.27678926185117725 + ], + "faceVertices": [ + 0, + 1, + 3, + 2 + ], + "vertexAttributeColumns": [ + { + "name": "quality", + "doubles": [ + 0.7364712141640124, + 0.6766994874229113, + 0.8921795677048454, + 0.08693883262941615 + ] + } + ], + "defaultVertexAttributes": { + "z": { + "doubleValue": 0.0 + }, + "y": { + "doubleValue": 0.0 + }, + "x": { + "doubleValue": 0.0 + } + }, + "faceSizes": [ + 4 + ] + } + }, + "version": "0.5.0" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/pointcloud.json b/benchmarks/serialization/results/samples/pointcloud.json new file mode 100644 index 000000000000..e6086a4fba29 --- /dev/null +++ b/benchmarks/serialization/results/samples/pointcloud.json @@ -0,0 +1,28 @@ +{ + "data": { + "points": [ + [ + 27.885359691576753, + -94.99784895546661, + -44.99413632617615 + ], + [ + -55.35785237023545, + 47.29424283280247, + 35.33989748458225 + ], + [ + 78.43591354096907, + -82.61223347411678, + -15.61563606294591 + ], + [ + -94.04055611238593, + -56.27240503927933, + 1.0710576206724767 + ] + ] + }, + "dtype": "compas.geometry/Pointcloud", + "guid": "cdc55bde-c217-4959-8a04-1c0279a8175e" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/pointcloud.msgpack b/benchmarks/serialization/results/samples/pointcloud.msgpack new file mode 100644 index 000000000000..f8d61b432bca Binary files /dev/null and b/benchmarks/serialization/results/samples/pointcloud.msgpack differ diff --git a/benchmarks/serialization/results/samples/pointcloud.msgpack.json b/benchmarks/serialization/results/samples/pointcloud.msgpack.json new file mode 100644 index 000000000000..9953520ade77 --- /dev/null +++ b/benchmarks/serialization/results/samples/pointcloud.msgpack.json @@ -0,0 +1,28 @@ +{ + "dtype": "compas.geometry/Pointcloud", + "data": { + "points": [ + [ + 27.885359691576753, + -94.99784895546661, + -44.99413632617615 + ], + [ + -55.35785237023545, + 47.29424283280247, + 35.33989748458225 + ], + [ + 78.43591354096907, + -82.61223347411678, + -15.61563606294591 + ], + [ + -94.04055611238593, + -56.27240503927933, + 1.0710576206724767 + ] + ] + }, + "guid": "21906529-bf0f-443c-b46c-2557de1b7997" +} \ No newline at end of file diff --git a/benchmarks/serialization/results/samples/pointcloud.pb b/benchmarks/serialization/results/samples/pointcloud.pb new file mode 100644 index 000000000000..e960cdd20aee Binary files /dev/null and b/benchmarks/serialization/results/samples/pointcloud.pb differ diff --git a/benchmarks/serialization/results/samples/pointcloud.pb.json b/benchmarks/serialization/results/samples/pointcloud.pb.json new file mode 100644 index 000000000000..0560c793fbb6 --- /dev/null +++ b/benchmarks/serialization/results/samples/pointcloud.pb.json @@ -0,0 +1,22 @@ +{ + "data": { + "message": { + "@type": "type.googleapis.com/compas_pb.data.PointcloudData", + "points": [ + 27.885359691576753, + -94.99784895546661, + -44.99413632617615, + -55.35785237023545, + 47.29424283280247, + 35.33989748458225, + 78.43591354096907, + -82.61223347411678, + -15.61563606294591, + -94.04055611238593, + -56.27240503927933, + 1.0710576206724767 + ] + } + }, + "version": "0.5.0" +} \ No newline at end of file diff --git a/benchmarks/serialization/run.py b/benchmarks/serialization/run.py new file mode 100644 index 000000000000..38063b6621aa --- /dev/null +++ b/benchmarks/serialization/run.py @@ -0,0 +1,228 @@ +"""Runner for the serialization benchmark (PRD section 10). + +Iterates subjects x sizes x formats, builds each fixture once, measures every format +against it, prints a readable table, and writes a CSV. Targets for the PRD's N1 +("binary/columnar materially faster than JSON") are set from the numbers this emits. + +Examples +-------- +Quick baseline (default, small sizes, fast):: + + python -m benchmarks.serialization.run + +Full PRD corpus (large; slow and memory-hungry):: + + python -m benchmarks.serialization.run --preset full + +Subset:: + + python -m benchmarks.serialization.run --subjects mesh pointcloud --formats json +""" + +import argparse +import csv +import os + +import compas +from benchmarks.serialization import fixtures +from benchmarks.serialization import formats +from benchmarks.serialization import metrics +from benchmarks.serialization import report +from benchmarks.serialization import samples + +HERE = os.path.dirname(__file__) +RESULTS_DIR = os.path.join(HERE, "results") + +# Sizes are element counts (vertices / points / nodes / primitives). DEFAULT_SIZES applies +# to any subject not given explicit sizes in PRESETS. +# +# The PRD (10.1) lists 5e6 vertices / 5e7 points as the largest sizes; the "full" preset caps +# at 1e6 elements so a run completes in reasonable time and memory. (Larger sizes are dominated +# by the tracemalloc peak-memory probe, which traces every allocation during deserialize.) The +# binary-vs-JSON scaling trend for N1 is already clear across the 1e3 -> 1e6 sweep. +DEFAULT_SIZES = { + "quick": [1000, 10000], + "full": [1000, 100000, 1000000], +} +PRESETS = { + "quick": { + "pointcloud": [10000, 100000], + }, + "full": { + "mesh": [1000, 100000, 1000000], + "mesh_attrs": [1000, 100000, 1000000], + "pointcloud": [10000, 100000, 1000000], + }, +} + + +def _sizes_for(preset, subject): + return PRESETS[preset].get(subject, DEFAULT_SIZES[preset]) + +CSV_COLUMNS = [ + "subject", + "size", + "format", + "size_bytes", + "compression_vs_json", + # timing (seconds; median over --repeat runs, with spread) + "dump_median_s", + "dump_stdev_s", + "load_median_s", + "load_stdev_s", + "roundtrip_median_s", + # throughput on the serialized payload (MB of wire per second) + "dump_mb_s", + "load_mb_s", + "peak_mem_bytes", + "lossless", + "data_equal", + "canonical_hash_equal", + # fidelity of lossy profiles (float32 etc.) — quantified coordinate error + "max_abs_error", + "rms_error", + "note", +] + + +def _mb_per_s(size_bytes, seconds): + if not seconds: + return float("nan") + return round((size_bytes / 1e6) / seconds, 3) + + +def _human_bytes(n): + for unit in ["B", "KB", "MB", "GB"]: + if n < 1024 or unit == "GB": + return "{:.1f}{}".format(n, unit) + n /= 1024.0 + + +def _coverage(): + """How many of compas_pb's native serializable types the corpus exercises. + + Returns ``{"benchmarked", "serializable", "missing"}`` or ``None`` if compas_pb is absent. + """ + try: + import compas_pb.conversions # noqa: F401 (import registers the serializers) + from compas_pb.registry import SerializerRegistry + + serializable = {t.__name__ for t in SerializerRegistry._SERIALIZERS} + except Exception: + return None + benchmarked = set() + for factory in fixtures.SUBJECTS.values(): + obj = factory(2) + item = obj[0] if isinstance(obj, (list, tuple)) else obj + benchmarked.add(type(item).__name__) + return { + "benchmarked": len(benchmarked & serializable), + "serializable": len(serializable), + "missing": sorted(serializable - benchmarked), + } + + +def run(subjects, preset, format_names, repeat, seed): + active_formats = [f for f in formats.formats() if f.available and (not format_names or f.name in format_names)] + if not active_formats: + raise SystemExit("No matching available formats.") + + rows = [] + + for subject in subjects: + factory = fixtures.SUBJECTS[subject] + for size in _sizes_for(preset, subject): + # Build a fresh fixture per format: serializing accesses .guid on some paths (JSON + # forces it), which would mutate a shared object and unfairly change what a later + # format encodes. A pristine object per format keeps each measurement independent. + measured = {f.name: metrics.measure(f, factory(size, seed), repeat=repeat) for f in active_formats} + json_size = measured.get("json", {}).get("size_bytes") + + for fmt in active_formats: + m = measured[fmt.name] + ratio = (json_size / m["size_bytes"]) if json_size else float("nan") + roundtrip = m["dump_median_s"] + m["load_median_s"] + rows.append( + { + "subject": subject, + "size": size, + "format": fmt.name, + "size_bytes": m["size_bytes"], + "compression_vs_json": round(ratio, 3), + "dump_median_s": round(m["dump_median_s"], 6), + "dump_stdev_s": round(m["dump_stdev_s"], 6), + "load_median_s": round(m["load_median_s"], 6), + "load_stdev_s": round(m["load_stdev_s"], 6), + "roundtrip_median_s": round(roundtrip, 6), + "dump_mb_s": _mb_per_s(m["size_bytes"], m["dump_median_s"]), + "load_mb_s": _mb_per_s(m["size_bytes"], m["load_median_s"]), + "peak_mem_bytes": m["peak_mem_bytes"], + "lossless": m["lossless"], + "data_equal": m["data_equal"], + "canonical_hash_equal": m["canonical_hash_equal"], + "max_abs_error": m["max_abs_error"], + "rms_error": m["rms_error"], + "note": fmt.note, + } + ) + return rows + + +def print_table(rows): + header = "{:<12} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10} {:>10} {:>8}".format( + "subject", "size", "format", "size", "dump_s", "load_s", "trip_s", "peak", "lossless" + ) + print(header) + print("-" * len(header)) + for r in rows: + print( + "{:<12} {:>10} {:>10} {:>10} {:>10.5f} {:>10.5f} {:>10.5f} {:>10} {:>8}".format( + r["subject"], + r["size"], + r["format"], + _human_bytes(r["size_bytes"]), + r["dump_median_s"], + r["load_median_s"], + r["roundtrip_median_s"], + _human_bytes(r["peak_mem_bytes"]), + str(r["lossless"]), + ) + ) + + +def write_csv(rows, out_path): + os.makedirs(os.path.dirname(out_path), exist_ok=True) + with open(out_path, "w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=CSV_COLUMNS) + writer.writeheader() + for r in rows: + writer.writerow(r) + return out_path + + +def main(): + parser = argparse.ArgumentParser(description="COMPAS serialization benchmark (PRD phase 1).") + parser.add_argument("--subjects", nargs="+", choices=sorted(fixtures.SUBJECTS), default=sorted(fixtures.SUBJECTS)) + parser.add_argument("--preset", choices=sorted(PRESETS), default="quick") + parser.add_argument("--formats", nargs="*", default=None, help="Subset of format names; default all available.") + parser.add_argument("--repeat", type=int, default=5, help="Timed runs per measurement (median reported).") + parser.add_argument("--seed", type=int, default=fixtures.DEFAULT_SEED) + parser.add_argument("--out", default=os.path.join(RESULTS_DIR, "baseline_quick.csv"), help="CSV output path.") + parser.add_argument("--no-samples", action="store_true", help="Skip writing encoded-format samples.") + args = parser.parse_args() + + rows = run(args.subjects, args.preset, args.formats, args.repeat, args.seed) + print_table(rows) + out = write_csv(rows, args.out) + meta = {"preset": args.preset, "repeat": args.repeat, "seed": args.seed, "compas": compas.__version__} + meta["coverage"] = _coverage() + html_out = report.write_html(rows, os.path.splitext(out)[0] + ".html", meta=meta) + print("\nWrote {} rows to {}\nWrote report to {}".format(len(rows), out, html_out)) + + if not args.no_samples: + sample_files = samples.dump_samples(os.path.join(os.path.dirname(out) or ".", "samples"), seed=args.seed) + print("Wrote {} encoded-format samples to {}/samples/".format(len(sample_files), os.path.dirname(out) or ".")) + + +if __name__ == "__main__": + main() diff --git a/benchmarks/serialization/samples.py b/benchmarks/serialization/samples.py new file mode 100644 index 000000000000..34779b470baa --- /dev/null +++ b/benchmarks/serialization/samples.py @@ -0,0 +1,127 @@ +"""Dump small, human-readable encoded artifacts for the three main (uncompressed) formats. + +The benchmark runs on large fixtures; these are *tiny* fixtures whose whole encoded shape is +inspectable. For each subject we write, into ``results/samples/``: + +* ``.json`` -- the JSON wire format (already text); +* ``.pb`` -- the raw protobuf bytes (binary); +* ``.pb.json`` -- the protobuf message rendered as JSON (``pb_dump_json``), + i.e. the same bytes deserialized and re-serialized to JSON so the wire *structure* is + readable (flat coordinate arrays, CSR faces, attribute columns, ...); +* ``.msgpack`` -- the raw MessagePack bytes (binary); +* ``.msgpack.json`` -- the decoded MessagePack tree as JSON (the row-oriented + ``{dtype, data, ...}`` shape). + +Comparing ``*.pb.json`` (schema'd / columnar) with ``*.msgpack.json`` (row-oriented tree) +shows how the two binary encodings differ in shape. +""" + +import json +import os + +import compas +from benchmarks.serialization import fixtures + +# Small, fully-inspectable fixtures per subject. +SAMPLES = { + "mesh": lambda seed: fixtures.make_mesh(4, with_attributes=True, seed=seed), + "pointcloud": lambda seed: fixtures.make_pointcloud(4, seed=seed), + "graph": lambda seed: fixtures.make_graph(4, seed=seed), + "boxes": lambda seed: fixtures.make_primitives("box", 2, seed=seed), +} + +_README = """# Encoded-format samples + +Tiny, fully-inspectable fixtures encoded with the three main (uncompressed) formats, so you +can see the *shape* of each encoding. Regenerate with: + + python -m benchmarks.serialization.samples + +Per subject: + +| file | what it is | +|------|------------| +| `.json` | JSON wire format (text) | +| `.pb` | raw protobuf bytes | +| `.pb.json` | protobuf message deserialized + re-serialized to JSON (readable wire structure) | +| `.msgpack` | raw MessagePack bytes | +| `.msgpack.json` | decoded MessagePack tree as JSON (row-oriented `{dtype, data}`) | + +`*.pb.json` shows the schema'd/columnar layout (flat vertex arrays, CSR faces, attribute +columns); `*.msgpack.json` shows the row-oriented dict tree. +""" + + +def _write_text(path, text): + with open(path, "w") as f: + f.write(text) + + +def _write_bytes(path, data): + with open(path, "wb") as f: + f.write(data) + + +def dump_samples(out_dir, seed=fixtures.DEFAULT_SEED): + """Write encoded-format samples for each subject into ``out_dir``. + + Binary formats whose optional dependency is missing are skipped (only their readable + JSON renderings are skipped too). + + Returns + ------- + list[str] + The files written. + """ + os.makedirs(out_dir, exist_ok=True) + written = [] + + try: + from compas_pb import pb_dump_bts + from compas_pb import pb_dump_json + except ImportError: + pb_dump_bts = pb_dump_json = None + + try: + import msgspec + + from benchmarks.serialization.formats import _msgpack_enc_hook + except ImportError: + msgspec = None + + for subject, factory in SAMPLES.items(): + # Fresh object per format: JSON dumping forces .guid, which would then appear in a + # subsequent pb/msgpack dump of the same object. Independent objects show each + # format's true shape (e.g. pb omitting auto-generated guids). + json_path = os.path.join(out_dir, subject + ".json") + _write_text(json_path, compas.json_dumps(factory(seed), pretty=True)) + written.append(json_path) + + if pb_dump_bts is not None: + pb_path = os.path.join(out_dir, subject + ".pb") + _write_bytes(pb_path, pb_dump_bts(factory(seed))) + pbjson_path = os.path.join(out_dir, subject + ".pb.json") + _write_text(pbjson_path, pb_dump_json(factory(seed))) + written.extend([pb_path, pbjson_path]) + + if msgspec is not None: + blob = msgspec.msgpack.encode(factory(seed), enc_hook=_msgpack_enc_hook) + mp_path = os.path.join(out_dir, subject + ".msgpack") + _write_bytes(mp_path, blob) + mpjson_path = os.path.join(out_dir, subject + ".msgpack.json") + _write_text(mpjson_path, json.dumps(msgspec.msgpack.decode(blob), indent=2)) + written.extend([mp_path, mpjson_path]) + + _write_text(os.path.join(out_dir, "README.md"), _README) + return written + + +def main(): + here = os.path.dirname(__file__) + out_dir = os.path.join(here, "results", "samples") + written = dump_samples(out_dir) + print("Wrote {} sample files to {}".format(len(written), out_dir)) + + +if __name__ == "__main__": + main() diff --git a/src/compas/data/data.py b/src/compas/data/data.py index aaaf562f0bd1..d077d56707ec 100644 --- a/src/compas/data/data.py +++ b/src/compas/data/data.py @@ -10,6 +10,7 @@ pass import hashlib +import json from copy import deepcopy from uuid import UUID from uuid import uuid4 @@ -334,6 +335,66 @@ def sha256(self, as_string=False): return h.hexdigest() return h.digest() + def canonical_hash(self, as_string=False): + """Compute a content hash of the object that is independent of guid, name, and serialization format. + + Unlike :meth:`sha256`, which hashes the full JSON text of the object (including its + guid), this method hashes a canonical form of ``__dtype__`` + ``__data__`` only. Two + objects with the same type and data therefore produce the same hash regardless of their + guid/name, and regardless of which format (JSON, protobuf, ...) they were loaded from, + because every format reconstructs the same ``__data__``. + + Parameters + ---------- + as_string : bool, optional + If True, return the digest in hexadecimal format rather than as bytes. + + Returns + ------- + bytes | str + + See Also + -------- + :meth:`sha256` + + Notes + ----- + The canonical form is a UTF-8 JSON encoding of ``{"dtype", "data"}`` with sorted keys + and no insignificant whitespace, produced with the guid excluded from this object and + from any nested :class:`compas.data.Data` objects. This makes the hash suitable for + content-addressed change detection and version control, independent of the wire format. + + Examples + -------- + >>> from compas.geometry import Point + >>> a = Point(0, 0, 0) + >>> b = Point(0, 0, 0) + >>> a.guid == b.guid + False + >>> a.canonical_hash() == b.canonical_hash() + True + + """ + from compas.data import DataEncoder + + previous = DataEncoder.minimal + DataEncoder.minimal = True + try: + canonical = json.dumps( + self.__jsondump__(minimal=True), + cls=DataEncoder, + sort_keys=True, + separators=(",", ":"), + ) + finally: + DataEncoder.minimal = previous + + h = hashlib.sha256() + h.update(canonical.encode()) + if as_string: + return h.hexdigest() + return h.digest() + @classmethod def validate_data(cls, data): """Validate the data against the object's data schema. diff --git a/tests/compas/data/test_data.py b/tests/compas/data/test_data.py index e4f5bff67424..b66118334c1f 100644 --- a/tests/compas/data/test_data.py +++ b/tests/compas/data/test_data.py @@ -1,4 +1,6 @@ +import compas from compas.data import Data +from compas.geometry import Point def test_string_casting(): @@ -11,3 +13,30 @@ def __str__(self): test = TestClass(42) assert str(test) == "TestClass 42" + + +def test_canonical_hash_is_guid_independent(): + a = Point(1, 2, 3) + b = Point(1, 2, 3) + assert a.guid != b.guid + assert a.canonical_hash() == b.canonical_hash() + # sha256 is coupled to the guid, so it differs for these two + assert a.sha256() != b.sha256() + + +def test_canonical_hash_is_content_sensitive(): + assert Point(1, 2, 3).canonical_hash() != Point(1, 2, 4).canonical_hash() + + +def test_canonical_hash_is_stable_and_string_form(): + p = Point(1, 2, 3) + assert p.canonical_hash() == p.canonical_hash() + assert p.canonical_hash(as_string=True) == p.canonical_hash(as_string=True) + assert isinstance(p.canonical_hash(as_string=True), str) + assert isinstance(p.canonical_hash(), bytes) + + +def test_canonical_hash_survives_json_roundtrip(): + p = Point(1, 2, 3) + q = compas.json_loads(compas.json_dumps(p)) + assert q.canonical_hash() == p.canonical_hash()