Skip to content

Performance & Benchmarks

PolyXML is engineered to process gigabytes of XML per second by leveraging Rust's zero-cost abstractions, quick-xml streaming events, and lexical-core numeric conversions.

The repository includes a fully reusable, automated benchmark suite covering both pure Rust Criterion tests and Python comparative benchmarks.


1. Python Deserialization & Serialization Throughput

Benchmarks conducted using Python 3.12 (abi3-py312) across 10,000-element streaming payloads (~724 KB XML), micro sensor payloads (~100B), and enterprise orders:

Batch Catalog Workload (10,000 items, ~724 KB XML)

Engine Paradigm / Category Implementation Deserialization Latency Deserialization Throughput Serialization Latency Serialization Throughput Peak RAM
PolyXML Typed Dataclass Rust + PyO3 13.9 ms 51.0 MB/s 7.30 ms 96.2 MB/s 2.0 MB
lxml.objectify Dynamic C Object C / Cython (libxml2) 9.9 ms 71.4 MB/s 3.9 ms 178.2 MB/s 0.2 MB
lxml.etree Untyped DOM C / Cython (libxml2) 10.0 ms 70.5 MB/s <0.1 MB
ElementTree Untyped DOM Python Stdlib C/Python 12.3 ms 57.5 MB/s 7.1 MB
defusedxml Secure DOM Python Defused 27.2 ms 26.0 MB/s 7.1 MB
xmltodict Untyped Dict C (pyexpat) 56.6 ms 12.5 MB/s 79.0 ms 8.9 MB/s 4.8 MB
xsdata Typed Dataclass Pure Python 222.5 ms 3.2 MB/s 282.6 ms 2.5 MB/s 3.3 MB

Key Takeaway: - Vs Typed Dataclasses (xsdata): PolyXML is 16.0x faster at deserialization and 38.7x faster at serialization, while saving over 1.2 MB of memory. - Vs Dict Parsers (xmltodict): PolyXML is 4.1x faster on deserialization and 10.8x faster on serialization, while instantiating strongly-typed dataclasses instead of unstructured string dictionaries. - Vs C Proxies (lxml.objectify): PolyXML executes within 1.39x of raw C dynamic proxy trees, while returning genuine Python dataclasses with IDE autocomplete and type safety.


Real-Time Micro Telemetry Workload (Sensor ~100B, UCI Telemetry)

Engine Category Deserialization Latency Serialization Latency Speedup vs Pure Python
PolyXML Typed Dataclass 2.5 μs 1.4 μs 17.1x
PolyXML (Pydantic) Typed Pydantic v2 3.1 μs 1.5 μs 13.7x
lxml.etree Untyped DOM 3.1 μs 13.7x
lxml.objectify C Dynamic Object 3.3 μs 1.3 μs 13.1x
ElementTree Untyped DOM 4.9 μs 8.7x
defusedxml Secure DOM 8.6 μs 5.0x
declxml Declarative Dict 10.2 μs 23.9 μs 4.2x
xmltodict Untyped Dict 10.3 μs 14.9 μs 4.2x
pydantic-xml Typed Pydantic v2 16.4 μs 15.6 μs 2.6x
untangle Dynamic Object 19.1 μs 2.3x
xsdata Typed Dataclass 43.0 μs 45.0 μs 1.0x (Ref)

Critical telemetry commands and sensor packets deserialize in 2.5 microseconds, beating even C-based DOM parsers (lxml at 3.1 μs, lxml.objectify at 3.3 μs).


2. Pure Rust Core Throughput (crates/polyxml-core)

Statistical benchmarks measured with Criterion.rs:

Workload / Target Operation Latency Throughput Zero Allocations
Sensor Micro (130B) Deserialization 1.19 μs 75.1 MiB/s Direct scalar parse
Sensor Micro (130B) Serialization 479 ns 187.2 MiB/s Zero intermediate DOM
Catalog (1,000 items, ~70 KB) Deserialization 1.01 ms 60.2 MiB/s Zero intermediate DOM
Catalog (1,000 items, ~70 KB) Serialization 358 μs 169.1 MiB/s Streaming buffer
Catalog (10,000 items, ~724 KB) Deserialization 10.18 ms 62.7 MiB/s Streaming buffer
Catalog (10,000 items, ~724 KB) Serialization 3.61 ms 175.2 MiB/s Streaming buffer

3. Key-Value Storage & Binary IPC Throughput (polyxml.dumps_binary / loads_binary)

When storing XML dataclasses and Pydantic models in embedded transactional key-value databases (libmdbx, LMDB, RocksDB) or communicating over Unix Domain Sockets/multiprocessing queues, Python's traditional cloudpickle and standard pickle encounter severe GIL and GC bottlenecks.

polyxml.dumps_binary() and polyxml.loads_binary() provide high-speed MessagePack encoding with universal leaf type hooks (XmlDate, XmlDateTime, XmlDuration, XmlTime, Decimal, QName, Enum, Path, and Pydantic models):

10,000 Complex XML Entities in Real MDBX Pipeline

Serializer Pipeline Dumps Ops/s Dumps Latency Loads Ops/s Avg Payload Size MDBX Write Ops/s
CloudPickle + LZ4 (Legacy) 20,609 ops/s 48.5 μs 49,322 ops/s 547 B 18,287 ops/s
Pickle 5 + LZ4 (Stdlib C) 71,954 ops/s 13.9 μs 50,092 ops/s 539 B
PolyXML Binary + LZ4 163,192 ops/s 6.1 μs 64,781 ops/s 252 B 58,781 ops/s
PolyXML Binary (Direct, No LZ4) 213,003 ops/s 4.7 μs 84,673 ops/s 327 B 63,236 ops/s

Key Takeaways:

  • 7.9x Faster Serialization: Slashes per-object serialization from 48.5 μs down to 6.1 μs.
  • 53.9% Storage Space Reduction: Cuts stored byte size from 547 bytes to 252 bytes per entity.
  • 3.2x MDBX Transaction Speedup: Real database writes into libmdbx jump from 18,287 ops/s to 58,781 ops/s.
  • 100% Fidelity Guarantee: Round-trips preserve exact dataclass types, field metadata, and XML primitive representations with assert loads(dumps(x)) == x.

4. How to Reproduce Benchmarks

The benchmark suite is reusable and version-controlled.

Run All Benchmarks (Rust + Python)

./benchmarks/run_all.sh

Run Pure Rust Criterion Benchmarks

cargo bench --bench core_benchmarks

Run Python Comparative Benchmarks CLI

python -m benchmarks --workload all --catalog-sizes 1000 10000 --iterations 25 --output-md benchmarks/results.md --output-json benchmarks/results.json