Benchmarking & comparison: - Add criterion benchmarks (ucobs vs cobs 0.3 vs corncobs 0.1) - Add Python cobs benchmark script - Add performance comparison section to README with full results Performance: - Rewrite encode to scan-and-bulk-copy via copy_from_slice - Rewrite decode to bulk-copy via copy_from_slice - ~2-4x throughput improvement; now matches corncobs on most workloads Quality hardening: - Add #![forbid(unsafe_code)], #![warn(missing_docs)], #[must_use] - Add [lints.rust] unsafe_code = "forbid" in Cargo.toml - Add rust-version (MSRV) = "1.56" - Add docs.rs metadata - 106 tests: 80 unit/corpus + 25 property tests (25k cases each = 625k total) - External test vectors from: Wikipedia, Cheshire & Baker paper, nanocobs, cobs-c, cobs2-rs, Jacques Fortier, Python cobs package - Add fuzz_small_dest fuzz target (encode/decode with undersized buffers) - Improve doc examples to show sentinel append/strip convention Infrastructure: - Add justfile with test (nextest), check, bench, fuzz, miri, fmt targets - cargo-nextest for parallel test execution - Update .gitignore for Python cache files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
68 lines
2.0 KiB
Python
68 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Benchmark Python cobs encode/decode vs ucobs (reference timing only).
|
|
|
|
Requires: pip install cobs
|
|
"""
|
|
|
|
import time
|
|
|
|
|
|
def payload_zeros(n: int) -> bytes:
|
|
return b"\x00" * n
|
|
|
|
|
|
def payload_nonzero(n: int) -> bytes:
|
|
return bytes((i % 255 + 1) for i in range(n))
|
|
|
|
|
|
def payload_mixed(n: int) -> bytes:
|
|
return bytes(i % 256 for i in range(n))
|
|
|
|
|
|
def bench(func, data, iterations=10_000):
|
|
start = time.perf_counter()
|
|
for _ in range(iterations):
|
|
func(data)
|
|
elapsed = time.perf_counter() - start
|
|
return elapsed
|
|
|
|
|
|
def main():
|
|
try:
|
|
from cobs import cobs as cobs_mod
|
|
except ImportError:
|
|
print("ERROR: python cobs package not installed. Run: pip install cobs")
|
|
return
|
|
|
|
sizes = [0, 1, 10, 64, 254, 255, 256, 1024, 4096]
|
|
payloads = [
|
|
("zeros", payload_zeros),
|
|
("nonzero", payload_nonzero),
|
|
("mixed", payload_mixed),
|
|
]
|
|
iterations = 10_000
|
|
|
|
print(f"{'operation':<30} {'size':>6} {'iters':>7} {'total_ms':>10} {'per_op_us':>10} {'MB/s':>10}")
|
|
print("-" * 80)
|
|
|
|
for name, make in payloads:
|
|
for size in sizes:
|
|
data = make(size)
|
|
|
|
# Encode
|
|
elapsed = bench(cobs_mod.encode, data, iterations)
|
|
per_op_us = (elapsed / iterations) * 1_000_000
|
|
mbps = (size * iterations / elapsed / 1_000_000) if elapsed > 0 and size > 0 else 0
|
|
print(f"{'python-cobs encode/' + name:<30} {size:>6} {iterations:>7} {elapsed*1000:>10.2f} {per_op_us:>10.3f} {mbps:>10.1f}")
|
|
|
|
# Decode
|
|
encoded = cobs_mod.encode(data)
|
|
elapsed = bench(cobs_mod.decode, encoded, iterations)
|
|
per_op_us = (elapsed / iterations) * 1_000_000
|
|
mbps = (size * iterations / elapsed / 1_000_000) if elapsed > 0 and size > 0 else 0
|
|
print(f"{'python-cobs decode/' + name:<30} {size:>6} {iterations:>7} {elapsed*1000:>10.2f} {per_op_us:>10.3f} {mbps:>10.1f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|