Compresser la télémétrie temps-réel jusqu'à ~1 bit par point — sans rien perdre. Timestamps en delta-of-delta + floats en XOR, l'algorithme Gorilla de Facebook (Pelkonen et al., VLDB 2015), en Python pur stdlib, sans dépendance.
capteur / PLC flux (ts, valeur) GorillaEncoder transport
┌──────────┐ ───────────────────────────▶ ┌───────────────────┐ ───────────────▶ edge → cloud
│ 21.3°C │ (1700000001, 21.3) │ ts: delta-of-delta│ ~1–8 bits/pt
│ 21.3°C │ (1700000002, 21.3) │ val: XOR + frame │ (vs 128 bits
│ 21.4°C │ (1700000003, 21.4) │ → bitstream │ en brut)
└──────────┘ └───────────────────┘
Une donnée de série temporelle brute, c'est 16 octets par point : 8 pour
l'horodatage, 8 pour le double. Sur une passerelle edge qui pousse des milliers
de capteurs vers le cloud, c'est le poste de coût numéro un — bande passante,
stockage TSDB, facture. Gorilla exploite deux régularités physiques pour faire
fondre ça sans perte.
1. Timestamps → delta-of-delta. La télémétrie est échantillonnée sur une horloge : les deltas successifs sont quasi constants. On encode donc la variation du delta. Échantillonnage parfaitement régulier → 1 seul bit par point. Une gigue est rangée dans des buckets de plus en plus larges (7 / 9 / 12 / 64 bits).
2. Valeurs → XOR. Deux lectures consécutives d'un capteur physique partagent l'essentiel de leurs bits IEEE-754. Le XOR des voisins laisse une courte plage de bits utiles encadrée de zéros ; on ne stocke que cette plage, et on réutilise le cadre précédent tant qu'il tient. Valeur inchangée → 1 seul bit.
Le tout est strictement sans perte : decompress(compress(points)) == points,
bit pour bit sur les double (NaN, ±inf, ±0.0 et subnormaux compris — c'est testé).
pip install -e .
python -m gorilla bench --points 20000points 20,000
raw (16 B/pt) 320,000 B
gorilla-tsc 48,932 B
ratio 6.5x
bits / point 19.57
round-trip lossless OK
En Python :
from gorilla import compress, decompress
points = [(1700000000, 21.3), (1700000001, 21.3), (1700000002, 21.4)]
blob = compress(points) # -> bytes, prêt à pousser sur MQTT / S3 / TSDB
assert decompress(blob) == points # round-trip sans perteAPI streaming (un point à la fois, mémoire constante) :
from gorilla import GorillaEncoder
enc = GorillaEncoder()
for ts, value in bridge.stream(...): # ex. la sortie d'industrial-retrofit
enc.add(ts, value)
payload = enc.to_bytes()Mesures réelles sur 50 000 points (16 B/point en brut). Gorilla excelle quand
les valeurs se répètent ou dérivent lentement — le cas normal d'un capteur — et
dégrade gracieusement (jamais en dessous du brut) sur du bruit haute entropie.
| forme du signal | brut | gorilla-tsc | ratio | bits/point |
|---|---|---|---|---|
| lecture constante | 800 KB | 12.5 KB | 63.9x | 2.00 |
| capteur lent, résolution 0.1 | 800 KB | 125 KB | 6.4x | 20.05 |
| bruité, 2 décimales (pire cas) | 800 KB | 329 KB | 2.4x | 52.67 |
Pas de promesse marketing « 100x » : sur du signal réel à résolution finie on est typiquement entre 6x et 16x, et le pire cas reste gagnant.
Le blob commence par un magic GOR1 + le nombre de points (uint32 big-endian),
suivi du bitstream. Outils en ligne de commande :
gorilla bench --points 100000 # mesure le ratio sur de la télémétrie synthétique
gorilla compress in.csv out.gor # CSV (timestamp,value) -> blob compressé
gorilla decompress out.gor # blob -> CSV sur stdout (ou un fichier)- Zéro dépendance — tourne sur une passerelle edge contrainte, un Raspberry Pi,
une Lambda. Les primitives bit-level (
BitWriter/BitReader) sont incluses. - Sans perte, prouvé — suite de tests round-trip, dont du fuzzing pseudo-aléatoire et les cas IEEE-754 limites (NaN, ±inf, subnormaux, gros sauts d'horloge).
- Streaming —
GorillaEncoder.add()encode en O(1) mémoire ; pas besoin de charger toute la série.
pip install -e ".[dev]"
pytest -q # 36 testsPensé pour s'enchaîner avec industrial-retrofit :
celui-ci transforme des registres Modbus legacy en télémétrie SI propre,
gorilla-tsc la compresse pour le transport edge → cloud.
MIT — voir LICENSE.