I can't find the source anymore, but I remember reading a few years ago that Go, when compiling, copies (when it can) the inners of simple functions in-place before compiling to so optimize the code and skip the base-overhead of each function allocation (4kb or so?).
I was working on jsonparser which is so amazing, as it can byte-browse a gigantic JSON file without actually having to parse the whole thing based on an interface or a struct and just pick exactly what you need. While I was benchmarking this I noticed some nanosecond-fluctuations and suddenly noticed that it was the one-linerfunction I was using for error-checking.
So taking these 3 tests:
- α tests in-place, no functions
- β tests with a function and parsing a copy of the
errorobject - γ tests with a function and parsing a reference of the
errorobject (I know this is unsafe, but this is merely for testing purposes)
_
func testα(b *testing.B) {
for i := 0; i < b.N; i++ { // Base of 0.33ns
err := errors.New("Oh no!")
if nil != err {
}
}
}
func testβ(b *testing.B) {
for i := 0; i < b.N; i++ { // Base of 0.33ns
err := errors.New("Oh no!")
perror1(err)
}
}
func testγ(b *testing.B) {
for i := 0; i < b.N; i++ { // Base of 0.33ns
err := errors.New("Oh no!")
perror2(&err)
}
}
func perror1(err error) {
if nil == err {
panic(err)
}
}
func perror2(err *error) {
if nil == *err {
panic(*err)
}
}
These are the results (including the average base of 0.33 nanosecond per empty basis test loop cycle):
1.
5.33 ns/op
300000000 iterations
1.599625123s total time,
0 allocs,
0 MB memory allocated
2.
39.56 ns/op
30000000 iterations
1.186849777s total time,
30002037 allocs,
457 MB memory allocated
3.
37.43 ns/op
50000000 iterations
1.87172856s total time,
50003443 allocs,
762 MB memory allocated
I'm wondering why test testα and testβ don't seem to be copied in-place. Does this explicitly require a compiler flag or does it not work when only testing code with go run and it always needs to be go build with params to make that work?
Aucun commentaire:
Enregistrer un commentaire