siiky
2023/08/29
2023/08/29
en
Wrote a program at $JOB that, for each frame obtained from a camera, tries to scan barcodes. The purpose was to replace a handful of libraries not compatible with Node.js v18[^0]. The Node.js controller program spawns this other program (camera-streamer), passing some static parameters as program arguments, and otherwise communicating through stdin/stdout. It's a simple solution and I'm pretty happy with it[^1].
We wanted something compiled and relatively fast (though almost anything would be better than JS), and with good C interop because of the libs we used underneath. We chose Go. It's an annoying language, but I can't say it was a bad choice in the end. Message passing in Go is (almost) a gift from Joe Armstrong himself (if it wasn't so dumbbed down), and makes concurrency super easy (though not reliable)!
At some point I suspected a memory leak. The glue C code is very simple, simple enough I carefully analyzed, and covered all cases[^2]. Still, there were signs of a memory leak. I got valgrind out, and no matter what I did, no leaks detected. I searched around at the time and read comments saying that Go's GC likes to hang on to objects for longer than one would expect. Case closed!
Or is it? Yesterday I left it running for a good few minutes and memory usage climbed to over 1.6GB[^3]! This is a no-go (hah!) so I'm back investigating today. I started by searching around for anything memory-related, with the GC idea in mind:
https://lwn.net/Articles/428100
https://groups.google.com/g/golang-dev/c/EpUlHQXWykg/m/LN2o9fV6R3wJ
https://groups.google.com/g/linux.kernel/c/MDIzfQMT3zU
https://golang.howtos.io/understanding-and-improving-go-memory-usage
https://chris124567.github.io/2021-06-21-go-performance
Nothing useful. So I turned back on no-memleaks-for-sure and collected some stats:
for formatid in 0 1 2 5; do for timeout in 5 10 20 30 60 90 120 160 200 240; do echo "${formatid}" | /usr/bin/time -f "format:${formatid} timeout:${timeout}s %M" timeout "${timeout}s" ./camera-streamer /dev/video0 zxing 10 QRCode; done; done 2>&1 | rg --color=never '^format:\d+\ttimeout:\d+s\t\d+