Isn't Go Reflection Slow?
Sharing a short note about something which surprised me today.
I’ve just started working on a self-healing Bittorrent tracker, which uses tracker-level tools (peer allocation algorithms and error messages) to handle unhealthy Bittorrent swarms. Ordinarily, such unhealthy swarms (low seeder to leecher ratio, broken clients, etc.) are either ignored or are handled outside the protocol using a separate frontend. I want to explore to what extent they can be handled by the tracker itself, within the constraints of the Bittorrent protocol. For more information, check out the README on my Github, although none of this is actually implemented yet: https://github.com/dmoerner/etracker.
Bittorrent trackers use the Bittorrent
bencoding format to
send messages to peers. Bittorrent trackers also need to be highly scalable.
I noticed that existing bencoding implementations, like
https://github.com/jackpal/bencode-go, use reflection to implement Marshal
and Unmarshal
functions, similar to the well-known examples for JSON and XML.
However, I recalled reading that reflection involves significant performance penalties. Learning Go by Jon Bodner says that a simple filter function implemented using reflection is 50–75 times slower than the same function implemented with generics (p. 425).
With performance at a premium, and a tracker needing to implement only a proper
subset of bencoding (since it receives messages from peers with GET requests),
I decided to hand-implement that subset of bencoding using bytes.Buffer
, and
use benchmarks to compare it to the bencode-go
library which uses reflection.
The results:
goos: linux
goarch: amd64
pkg: etracker
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
BenchmarkNonReflect-2 16438 68255 ns/op
BenchmarkReflectLibrary-2 17455 68031 ns/op
PASS
ok etracker 3.766s
There’s no difference at all! This was surprising. Unless I’m missing
something, bencode.Marshal
does use reflection. I suspect that the subset of
bencoding I’m implementing is so simple that we don’t run into any overhead
from reflection. But still, I would have expected to at least see something.