From b35dec2b4625ad85c0999a4c59a2f05d4eb06d82 Mon Sep 17 00:00:00 2001 From: raven Date: Fri, 27 Mar 2026 19:06:49 -0500 Subject: benchmark custom DEFLATE compressor BenchmarkNaiveGzip flate_test.go:47: compressed empty to 1043631 bytes flate_test.go:47: compressed debug to 1043939 bytes flate_test.go:47: compressed flat to 1043648 bytes flate_test.go:47: compressed sphere to 4981910 bytes flate_test.go:72: naive gzip compressor completed in 24.744500019s BenchmarkDeflate flate_test.go:47: compressed empty to 13525844 bytes flate_test.go:47: compressed debug to 13526111 bytes flate_test.go:47: compressed flat to 13525852 bytes flate_test.go:47: compressed sphere to 19927269 bytes flate_test.go:77: custom deflate compressor completed in 546.099782ms the standard gzip compressor is almost 40 times slower, but it requires around a tenth of the network bandwidth. so, it would seem that, since the internet is slower than CPUs, standard gzip wins out probably. the custom compressor may gain an advantage if huffman trees are actually implemented. however, I'm going to focus on other things for now. --- server/flate.go | 6 ++-- server/flate_test.go | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 server/flate_test.go diff --git a/server/flate.go b/server/flate.go index e2d87f4..01becb6 100644 --- a/server/flate.go +++ b/server/flate.go @@ -8,9 +8,9 @@ import ( // the protocol requires us to send the level as a gzipped flat array. but our // level representation is close to run length encoding that can be achieved -// with DEFLATE. it is thus in theory significantly more CPU efficient to, -// instead of expanding out the RLE and compressing it with the generic -// compressor, translate it directly into DEFLATE +// with DEFLATE. it is thus significantly more CPU efficient to, instead of +// expanding out the RLE and compressing it with the generic compressor, +// translate it directly into DEFLATE func deflateRuns( wr io.Writer, runs []blockData, changes []blockData, levelSize uint32) (length uint32, err error) { diff --git a/server/flate_test.go b/server/flate_test.go new file mode 100644 index 0000000..2a85004 --- /dev/null +++ b/server/flate_test.go @@ -0,0 +1,78 @@ +package server + +import ( + "io" + "bufio" + "testing" + "compress/gzip" + "encoding/binary" +) + +type benchCompressor func(*blockVolume) io.ReadCloser + +func theirCompress(v *blockVolume) io.ReadCloser { + rd, wr := io.Pipe() + go func() { + defer wr.Close() + z := gzip.NewWriter(wr) + defer z.Close() + bw := bufio.NewWriter(z) + defer bw.Flush() + + v.RLock() + defer v.RUnlock() + binary.Write(bw, binary.BigEndian, uint32(v.size.X*v.size.Y*v.size.Z)) + for block := range v.unsyncGetAll() { + err := bw.WriteByte(byte(block)) + if err != nil { + return + } + } + }() + return rd +} + +func ourCompress(v *blockVolume) io.ReadCloser { + return v.syncCompressForNetwork() +} + +func doCompressorBenchmark( + b *testing.B, c benchCompressor, v *blockVolume, testName string) { + b.StartTimer() + rd := c(v) + data, err := io.ReadAll(rd) + if err != nil { + panic(err) + } + b.Logf("compressed %s to %d bytes", testName, len(data)) + b.StopTimer() +} + +func doCompressorBenchmarks(b *testing.B, c benchCompressor) { + b.StopTimer() + var ( + v blockVolume + size = blockPos {1024, 1024, 1024} + ) + v.init(size) + doCompressorBenchmark(b, c, &v, "empty") + v.init(size) + generateDebug(&v) + doCompressorBenchmark(b, c, &v, "debug") + v.init(size) + generateFlat(&v) + doCompressorBenchmark(b, c, &v, "flat") + v.init(size) + generateSphere(&v) + doCompressorBenchmark(b, c, &v, "sphere") +} + +func BenchmarkNaiveGzip(b *testing.B) { + doCompressorBenchmarks(b, theirCompress) + b.Logf("naive gzip compressor completed in %v", b.Elapsed()) +} + +func BenchmarkDeflate(b *testing.B) { + doCompressorBenchmarks(b, ourCompress) + b.Logf("custom deflate compressor completed in %v", b.Elapsed()) +} -- cgit v1.2.3