Cython-accelerated SCALE codec library for Substrate-based blockchains (Polkadot, Kusama, Bittensor, etc.), with no external runtime dependencies.
A drop-in replacement for py-scale-codec: same scalecodec module name and public API, but compiled with Cython for improved throughput.
pip install cyscaleBenchmarked on Apple M-series (Python 3.13) against py-scale-codec 1.2.12. All timings are µs per call; speedup = py ÷ cy.
| Benchmark | py (µs) | cy (µs) | speedup |
|---|---|---|---|
| u8 decode | 3.01 | 1.69 | 1.78× |
| u16 decode | 2.99 | 1.80 | 1.66× |
| u32 decode | 3.11 | 1.83 | 1.70× |
| u64 decode | 3.09 | 1.79 | 1.73× |
| u128 decode | 2.91 | 1.84 | 1.58× |
| Compact decode | 9.59 | 6.90 | 1.39× |
| bool decode | 2.93 | 1.61 | 1.82× |
| H256 decode | 2.93 | 1.66 | 1.76× |
| AccountId decode (SS58 format 42) | 11.45 | 4.31 | 2.66× |
| Str decode | 12.89 | 8.58 | 1.50× |
| (u32, u64, bool) decode | 21.96 | 8.36 | 2.63× |
| u32 encode | 2.34 | 1.50 | 1.57× |
| u64 encode | 2.33 | 1.55 | 1.50× |
| Compact encode | 8.85 | 6.36 | 1.39× |
| H256 encode | 2.47 | 1.42 | 1.74× |
| Benchmark | py (µs) | cy (µs) | speedup |
|---|---|---|---|
| Vec decode (64 elements) | 224.20 | 20.88 | 10.74× |
| Vec decode (1,024 elements) | 3217.32 | 147.21 | 21.85× |
| Vec decode (16,384 elements) | 50396.95 | 2150.86 | 23.43× |
| Bytes decode (1 KB) | 14.67 | 9.99 | 1.47× |
| Bytes decode (64 KB) | 64.15 | 63.30 | 1.01× |
| Bytes decode (512 KB) | 379.99 | 429.74 | 0.88× |
| Vec decode (5 events, V10) | 301.10 | 131.81 | 2.28× |
| MetadataVersioned decode (V10, 85 KB) | 64958.83 | 35936.30 | 1.81× |
| MetadataVersioned decode (V13, 219 KB) | 143029.99 | 81044.60 | 1.76× |
| MetadataVersioned decode (V14, 300 KB) | 390902.34 | 185823.12 | 2.10× |
| Bittensor metadata + portable registry (254 KB) | 443089.47 | 211343.83 | 2.10× |
Primitives and small types see ~2.0–2.6× speedup. Large metadata decoding
sees ~2.1–2.3× speedup — the gain compounds across thousands of recursive
decode calls. Raw bulk byte operations (Bytes/Vec<u8>) above ~64 KB are
dominated by memcpy and see a reduced ~1.3–1.4× speedup.
AccountId with SS58 encoding shows a 1.87× speedup — the SS58 encoding
itself (ss58_encode) is pure Python and limits gains in that path.
batch_decode(type_strings, bytes_list) amortises Python dispatch overhead
across a list of decodes. The baseline below is a py-scale-codec decode loop,
which is the equivalent operation without this API.
Note: bt_decode is excluded from this comparison because it does not perform
SS58 encoding — including it without that post-processing step would be unfair.
| Benchmark | py loop (µs) | cy batch (µs) | speedup |
|---|---|---|---|
| batch_decode AccountId ×10 | 116.66 | 19.36 | 6.03× |
| batch_decode AccountId ×100 | 1159.47 | 190.19 | 6.10× |
| batch_decode AccountId ×1,000 | 11530.59 | 1888.99 | 6.10× |
| Mixed (AccountId/u32/u128) ×100 | 599.73 | 71.42 | 8.40× |
To reproduce, run:
# save a py-scale-codec baseline
python benchmarks/bench.py --save-baseline benchmarks/baseline_py.json
# compare against cy-scale-codec
PYTHONPATH=. python benchmarks/bench.py --compare benchmarks/baseline_py.json| Type | Description | Example SCALE decoding value | SCALE encoded value |
|---|---|---|---|
bool |
Boolean values are encoded using the least significant bit of a single byte. | True |
0x01 |
u16 |
Basic integers are encoded using a fixed-width little-endian (LE) format. | 42 |
0x2a00 |
Compact |
A "compact" or general integer encoding is sufficient for encoding large integers (up to 2**536) and is more efficient at encoding most values than the fixed-width version. | 1 |
0x04 |
Vec |
A collection of same-typed values is encoded, prefixed with a compact encoding of the number of items, followed by each item's encoding concatenated in turn. | [4, 8, 15, 16, 23, 42] |
0x18040008000f00100017002a00 |
str, Bytes |
Strings are Vectors of bytes (Vec<u8>) containing a valid UTF8 sequence. |
"Test" |
0x1054657374 |
AccountId |
An SS58 formatted representation of an account. | "5GDyPHLVHcQYPTWfygtPYeogQjyZy7J9fsi4brPhgEFq4pcv" |
0xb80269ec... |
Enum |
A fixed number of variants, each mutually exclusive. Encoded as the first byte identifying the index of the variant. | {'Int': 8} |
0x002a |
Struct |
For structures, values are named but that is irrelevant for the encoding (only order matters). | {"votes": [...], "id": 4} |
0x04b80269... |