Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Due to zero copy optimizations in os.File, using io.CopyBuffer() with os.File and qcow2reader.Image ends as io.Copy() ignoring our buffer. This is slow and a bad example for users of the library. Fix the example by wrapping os.File with a Writer type that implements only io.Writer. With the wrapper reading the image is 3.63 times faster (buffer == 2 MiB), and writing to actual file is 1.68 times faster (buffer == 512 KiB buffer). Reading qcow2 compressed image is only 1.05 times faster (buffer >= 256 KiB). Based on the benchmarks, change the default buffer size to 512 KiB. Making the example more complicated is not great but users need to know how to workaround the "optimizations" in io.CopyBuffer(). I think this should be fixed in the standard library later. Benchmark reading Ubuntu 24.04 image in qcow2 uncompressed format: % hyperfine -w3 -L b 32768,65536,131072,262144,524288,1048576,2097152 \ "./go-qcow2reader-example read -buffer-size {b} /var/tmp/images/test.qcow2 >/dev/null" Benchmark 1: ./go-qcow2reader-example read -buffer-size 32768 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 693.0 ms ± 5.7 ms [User: 108.5 ms, System: 585.7 ms] Range (min … max): 682.5 ms … 703.7 ms 10 runs Benchmark 2: ./go-qcow2reader-example read -buffer-size 65536 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 438.5 ms ± 17.1 ms [User: 76.3 ms, System: 362.1 ms] Range (min … max): 423.6 ms … 477.0 ms 10 runs Benchmark 3: ./go-qcow2reader-example read -buffer-size 131072 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 309.2 ms ± 9.3 ms [User: 62.6 ms, System: 246.6 ms] Range (min … max): 301.4 ms … 332.9 ms 10 runs Benchmark 4: ./go-qcow2reader-example read -buffer-size 262144 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 254.4 ms ± 4.2 ms [User: 54.1 ms, System: 200.6 ms] Range (min … max): 248.5 ms … 263.0 ms 11 runs Benchmark 5: ./go-qcow2reader-example read -buffer-size 524288 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 222.7 ms ± 2.5 ms [User: 49.5 ms, System: 170.2 ms] Range (min … max): 217.9 ms … 228.2 ms 13 runs Benchmark 6: ./go-qcow2reader-example read -buffer-size 1048576 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 206.0 ms ± 6.8 ms [User: 49.5 ms, System: 151.6 ms] Range (min … max): 199.2 ms … 226.3 ms 14 runs Benchmark 7: ./go-qcow2reader-example read -buffer-size 2097152 /var/tmp/images/test.qcow2 >/dev/null Time (mean ± σ): 191.1 ms ± 2.6 ms [User: 47.7 ms, System: 139.0 ms] Range (min … max): 187.0 ms … 197.1 ms 15 runs Summary ./go-qcow2reader-example read -buffer-size 2097152 /var/tmp/images/test.qcow2 >/dev/null ran 1.08 ± 0.04 times faster than ./go-qcow2reader-example read -buffer-size 1048576 /var/tmp/images/test.qcow2 >/dev/null 1.17 ± 0.02 times faster than ./go-qcow2reader-example read -buffer-size 524288 /var/tmp/images/test.qcow2 >/dev/null 1.33 ± 0.03 times faster than ./go-qcow2reader-example read -buffer-size 262144 /var/tmp/images/test.qcow2 >/dev/null 1.62 ± 0.05 times faster than ./go-qcow2reader-example read -buffer-size 131072 /var/tmp/images/test.qcow2 >/dev/null 2.29 ± 0.09 times faster than ./go-qcow2reader-example read -buffer-size 65536 /var/tmp/images/test.qcow2 >/dev/null 3.63 ± 0.06 times faster than ./go-qcow2reader-example read -buffer-size 32768 /var/tmp/images/test.qcow2 >/dev/null Benchmark copying same image to actual file: % hyperfine -w3 -L b 32768,65536,131072,262144,524288,1048576,2097152 \ "./go-qcow2reader-example read -buffer-size {b} /var/tmp/images/test.qcow2 >/tmp/tmp.img" Benchmark 1: ./go-qcow2reader-example read -buffer-size 32768 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.973 s ± 0.050 s [User: 0.126 s, System: 1.663 s] Range (min … max): 1.910 s … 2.073 s 10 runs Benchmark 2: ./go-qcow2reader-example read -buffer-size 65536 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.494 s ± 0.104 s [User: 0.097 s, System: 1.249 s] Range (min … max): 1.437 s … 1.778 s 10 runs Benchmark 3: ./go-qcow2reader-example read -buffer-size 131072 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.274 s ± 0.049 s [User: 0.078 s, System: 1.035 s] Range (min … max): 1.219 s … 1.373 s 10 runs Benchmark 4: ./go-qcow2reader-example read -buffer-size 262144 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.208 s ± 0.092 s [User: 0.058 s, System: 0.918 s] Range (min … max): 1.156 s … 1.462 s 10 runs Benchmark 5: ./go-qcow2reader-example read -buffer-size 524288 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.174 s ± 0.033 s [User: 0.048 s, System: 0.851 s] Range (min … max): 1.149 s … 1.259 s 10 runs Benchmark 6: ./go-qcow2reader-example read -buffer-size 1048576 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.202 s ± 0.068 s [User: 0.043 s, System: 0.821 s] Range (min … max): 1.148 s … 1.342 s 10 runs Benchmark 7: ./go-qcow2reader-example read -buffer-size 2097152 /var/tmp/images/test.qcow2 >/tmp/tmp.img Time (mean ± σ): 1.665 s ± 0.936 s [User: 0.041 s, System: 0.863 s] Range (min … max): 1.173 s … 3.863 s 10 runs Summary ./go-qcow2reader-example read -buffer-size 524288 /var/tmp/images/test.qcow2 >/tmp/tmp.img ran 1.02 ± 0.07 times faster than ./go-qcow2reader-example read -buffer-size 1048576 /var/tmp/images/test.qcow2 >/tmp/tmp.img 1.03 ± 0.08 times faster than ./go-qcow2reader-example read -buffer-size 262144 /var/tmp/images/test.qcow2 >/tmp/tmp.img 1.08 ± 0.05 times faster than ./go-qcow2reader-example read -buffer-size 131072 /var/tmp/images/test.qcow2 >/tmp/tmp.img 1.27 ± 0.10 times faster than ./go-qcow2reader-example read -buffer-size 65536 /var/tmp/images/test.qcow2 >/tmp/tmp.img 1.42 ± 0.80 times faster than ./go-qcow2reader-example read -buffer-size 2097152 /var/tmp/images/test.qcow2 >/tmp/tmp.img 1.68 ± 0.06 times faster than ./go-qcow2reader-example read -buffer-size 32768 /var/tmp/images/test.qcow2 >/tmp/tmp.img Benchmark reading same image in qcow2 compressed format: % hyperfine -w1 -r3 -L b 32768,65536,131072,262144,524288,1048576,2097152 \ "./go-qcow2reader-example read -buffer-size {b} /var/tmp/images/test.zlib.qcow2 >/dev/null" Benchmark 1: ./go-qcow2reader-example read -buffer-size 32768 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 11.475 s ± 0.020 s [User: 10.688 s, System: 0.880 s] Range (min … max): 11.463 s … 11.499 s 3 runs Benchmark 2: ./go-qcow2reader-example read -buffer-size 65536 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 11.256 s ± 0.083 s [User: 10.734 s, System: 0.619 s] Range (min … max): 11.204 s … 11.351 s 3 runs Benchmark 3: ./go-qcow2reader-example read -buffer-size 131072 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 11.011 s ± 0.027 s [User: 10.646 s, System: 0.465 s] Range (min … max): 10.983 s … 11.037 s 3 runs Benchmark 4: ./go-qcow2reader-example read -buffer-size 262144 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 10.944 s ± 0.025 s [User: 10.634 s, System: 0.406 s] Range (min … max): 10.916 s … 10.961 s 3 runs Benchmark 5: ./go-qcow2reader-example read -buffer-size 524288 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 10.921 s ± 0.011 s [User: 10.653 s, System: 0.374 s] Range (min … max): 10.911 s … 10.933 s 3 runs Benchmark 6: ./go-qcow2reader-example read -buffer-size 1048576 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 10.921 s ± 0.019 s [User: 10.668 s, System: 0.371 s] Range (min … max): 10.901 s … 10.938 s 3 runs Benchmark 7: ./go-qcow2reader-example read -buffer-size 2097152 /var/tmp/images/test.zlib.qcow2 >/dev/null Time (mean ± σ): 10.897 s ± 0.025 s [User: 10.659 s, System: 0.350 s] Range (min … max): 10.869 s … 10.913 s 3 runs Summary ./go-qcow2reader-example read -buffer-size 2097152 /var/tmp/images/test.zlib.qcow2 >/dev/null ran 1.00 ± 0.00 times faster than ./go-qcow2reader-example read -buffer-size 524288 /var/tmp/images/test.zlib.qcow2 >/dev/null 1.00 ± 0.00 times faster than ./go-qcow2reader-example read -buffer-size 1048576 /var/tmp/images/test.zlib.qcow2 >/dev/null 1.00 ± 0.00 times faster than ./go-qcow2reader-example read -buffer-size 262144 /var/tmp/images/test.zlib.qcow2 >/dev/null 1.01 ± 0.00 times faster than ./go-qcow2reader-example read -buffer-size 131072 /var/tmp/images/test.zlib.qcow2 >/dev/null 1.03 ± 0.01 times faster than ./go-qcow2reader-example read -buffer-size 65536 /var/tmp/images/test.zlib.qcow2 >/dev/null 1.05 ± 0.00 times faster than ./go-qcow2reader-example read -buffer-size 32768 /var/tmp/images/test.zlib.qcow2 >/dev/null Fixes: #41 Signed-off-by: Nir Soffer <[email protected]>
- Loading branch information