Skip to content

Commit 3cf116d

Browse files
committed
Add benchmarks
1 parent e4d5beb commit 3cf116d

7 files changed

Lines changed: 219 additions & 66 deletions

File tree

README.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,46 @@ This library implements several state-of-the-art compression algorithms for time
88

99
- **ALP (Adaptive Lossless floating-Point)** - Lossless compression for float64 values using adaptive scaling and bit-packing
1010
- **Delta Encoding** - First-order delta encoding for int32/int64 values
11-
- **Delta-of-Delta (DoD)** - Second-order delta encoding for highly correlated timeseries
11+
- **Delta-of-Delta (DoD)** - Second-order delta encoding for regular timeseries
1212
- **Bitpacking** - Low-level bit manipulation with architecture-specific optimizations (amd64, arm64)
1313

14+
## Benchmarks
15+
16+
Compressing timeseries data with timestamps and float values:
17+
18+
```go
19+
// Compress 120 samples of timestamps and floats
20+
timestamps := []int64{...} // Unix millisecond timestamps
21+
values := []float64{...} // Sensor readings, prices, etc.
22+
23+
// Encode using Delta-of-Delta + ALP
24+
compressedTimestamps := dod.EncodeInt64(nil, timestamps)
25+
compressedValues := alp.Encode(nil, values)
26+
27+
// Decode back to original data
28+
var decodedTimestamps [120]int64
29+
var decodedValues [120]float64
30+
dod.DecodeInt64(decodedTimestamps[:], compressedTimestamps)
31+
alp.Decode(decodedValues[:], compressedValues)
32+
```
33+
34+
Performance comparison vs Gorilla (XOR) compression from Prometheus (Apple M3, 120 samples):
35+
36+
| Codec | Operation | Time/op | Throughput | Compressed Size | Allocs |
37+
|-------|-----------|---------|------------|-----------------|--------|
38+
| Gorilla | Encode | 3321 ns/op | - | 982 bytes | 7 allocs/op |
39+
| Gorilla | Decode | 1715 ns/op | - | - | 1 allocs/op |
40+
| ALP+DoD | Encode | **1406 ns/op** | **2.4x faster** | **840 bytes** | 6 allocs/op |
41+
| ALP+DoD | Decode | **252 ns/op** | **6.8x faster, 3330 MB/s** | - | **0 allocs/op** |
42+
43+
Run benchmarks:
44+
```bash
45+
cd benchmarks
46+
go test -bench=BenchmarkFloats -benchmem
47+
```
48+
49+
See [benchmarks/gorilla_bench_test.go](benchmarks/gorilla_bench_test.go) for implementation details.
50+
1451
## Installation
1552

1653
```bash
@@ -34,11 +71,11 @@ func main() {
3471
data := []float64{1.1, 2.2, 3.3, 4.4, 5.5}
3572
// Compress
3673
compressed := make([]byte, 10)
37-
compressed = alp.Compress(compressed, data)
74+
compressed = alp.Encode(compressed, data)
3875

3976
// Decompress
4077
decompressed := make([]float64, len(data))
41-
alp.Decompress(decompressed, compressed)
78+
alp.Decode(decompressed, compressed)
4279

4380
// Calculate compression ratio
4481
ratio := alp.CompressionRatio(len(data), len(compressed))

alp/alp.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ type CompressionMetadata struct {
4646
ConstantValue float64
4747
}
4848

49-
// Compress compresses an array of float64 values using ALP
50-
func Compress(dst []byte, data []float64) []byte {
49+
// Encode compresses an array of float64 values using ALP
50+
func Encode(dst []byte, data []float64) []byte {
5151
if len(data) == 0 {
5252
return encodeMetadata(CompressionMetadata{
5353
EncodingType: EncodingNone,
@@ -117,8 +117,8 @@ func Compress(dst []byte, data []float64) []byte {
117117
return result
118118
}
119119

120-
// Decompress decompresses ALP-encoded data
121-
func Decompress(dst []float64, data []byte) []float64 {
120+
// Decode decompresses ALP-encoded data
121+
func Decode(dst []float64, data []byte) []float64 {
122122
if len(data) == 0 {
123123
return dst[:0]
124124
}

alp/alp_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ func TestALPCompression(t *testing.T) {
114114

115115
for _, tt := range tests {
116116
t.Run(tt.name, func(t *testing.T) {
117-
// Compress
118-
compressed := Compress(nil, tt.data)
117+
// Encode
118+
compressed := Encode(nil, tt.data)
119119

120-
// Decompress
120+
// Decode
121121
decompressed := make([]float64, len(tt.data))
122-
decompressed = Decompress(decompressed, compressed)
122+
decompressed = Decode(decompressed, compressed)
123123

124124
// Verify length
125125
if len(decompressed) != len(tt.data) {
@@ -148,9 +148,9 @@ func TestALPRandomDataset(t *testing.T) {
148148
data[i] = randGen.Float64() * 1e10
149149
}
150150

151-
compressed := Compress(nil, data)
151+
compressed := Encode(nil, data)
152152
decompressed := make([]float64, len(data))
153-
decompressed = Decompress(decompressed, compressed)
153+
decompressed = Decode(decompressed, compressed)
154154

155155
if len(decompressed) != len(data) {
156156
t.Errorf("Length mismatch: got %d, want %d", len(decompressed), len(data))
@@ -182,9 +182,9 @@ func TestALPLargeDataset(t *testing.T) {
182182
data[i] = float64(i) * 0.1
183183
}
184184

185-
compressed := Compress(nil, data)
185+
compressed := Encode(nil, data)
186186
decompressed := make([]float64, len(data))
187-
decompressed = Decompress(decompressed, compressed)
187+
decompressed = Decode(decompressed, compressed)
188188
if len(decompressed) != len(data) {
189189
t.Errorf("Length mismatch: got %d, want %d", len(decompressed), len(data))
190190
}

alp/benchmark_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,29 @@ func BenchmarkALP(b *testing.B) {
2525
dataset[i] = randGen.Float64() * 1000
2626
}
2727

28-
compressed := Compress(nil, dataset)
28+
compressed := Encode(nil, dataset)
2929
b.Run("CompressionSpeed", func(b *testing.B) {
3030
for _, size := range benchmarkSizes {
3131
data := dataset[:size]
3232
b.Run(strconv.Itoa(size), func(b *testing.B) {
3333
b.SetBytes(int64(size * 8))
3434
b.ResetTimer()
3535
for b.Loop() {
36-
_ = Compress(compressed, data)
36+
_ = Encode(compressed, data)
3737
}
3838
})
3939
}
4040
})
4141

4242
b.Run("DecompressionSpeed", func(b *testing.B) {
4343
for _, size := range benchmarkSizes {
44-
compressed := Compress(compressed, dataset[:size])
44+
compressed := Encode(compressed, dataset[:size])
4545
decompressed := make([]float64, size)
4646
b.Run(strconv.Itoa(size), func(b *testing.B) {
4747
b.SetBytes(int64(size * 8))
4848
b.ResetTimer()
4949
for b.Loop() {
50-
_ = Decompress(decompressed, compressed)
50+
_ = Decode(decompressed, compressed)
5151
}
5252
})
5353
}
@@ -96,24 +96,24 @@ func BenchmarkALP(b *testing.B) {
9696

9797
for patternName, generator := range patterns {
9898
data := generator()
99-
b.Run("Compress/"+patternName, func(b *testing.B) {
99+
b.Run("Encode/"+patternName, func(b *testing.B) {
100100
b.SetBytes(int64(size * 8))
101101
b.ResetTimer()
102102
for b.Loop() {
103-
_ = Compress(compressed, data)
103+
_ = Encode(compressed, data)
104104
}
105105
})
106106
}
107107

108108
for patternName, generator := range patterns {
109109
data := generator()
110-
compressed = Compress(compressed, data)
110+
compressed = Encode(compressed, data)
111111
decompressed := make([]float64, size)
112-
b.Run("Decompress/"+patternName, func(b *testing.B) {
112+
b.Run("Decode/"+patternName, func(b *testing.B) {
113113
b.SetBytes(int64(size * 8))
114114
b.ResetTimer()
115115
for b.Loop() {
116-
_ = Decompress(decompressed, compressed)
116+
_ = Decode(decompressed, compressed)
117117
}
118118
})
119119
}

0 commit comments

Comments
 (0)