4 ms·
I don't understand what you're saying but defer cost a few ns so it's very fast and usually not a performance problem. ( ie: don't use it in hot path )
by Thaxll 8y ago
I don't understand what you're saying but defer cost a few ns so it's very fast and usually not a performance problem. ( ie: don't use it in hot path )
- deathanatos 8y agoIf I am understanding him correctly: If defer were statically scoped, that is, if it were scoped to curly braces, you would "know" at compile time when and if a defer is going to run (relative to that scope). That is, a defer boils down to just "at the end of this scope, run this code" and nothing more, and all the compiler has to do is move that code/function call to the closing curly brace. (Similar to how a C++ compiler knows when to run a destructor for an object.) However, by having them be function scoped, this isn't so anymore; e.g, a defer occurring in an if() statement needs to happen at the end of the function, but only if the if occurs. If you loop over a defer, we need to accumulate those. (And the golang tour even explicitly calls the behavior out.[1]) So, instead of just running the defer at the end of the if / inside the if statically, we need to push the defer onto a runtime stack of yet-to-be-run defers that we'll evaluate at the end of the function. This now has to happen at runtime, not compile time, and makes the function compilation more complex, and requires a stack somewhere to push this stuff onto. From a compiler writer's perspective, I would agree w/ the parent: this seems much more complex, and runs counter to golang's otherwise simple design philosophy. [1]: https://tour.golang.org/flowcontrol/13 https://tour.golang.org/flowcontrol/13
- pcwalton 8y agoIt's not that defer is slow in absolute terms†; it's that it's slower than it needs to be for no good reason. The corresponding C++ feature (RAII) is "free" (as in, it adds no overhead over manually writing the cleanup code), while defer is not. Defer would be easier to use for the programmer, easier for the compiler writer, and faster at runtime if it were block-scoped. †Though the linked list that the compiler has to generate could hit malloc, which costs more than a few nanoseconds.
- cyphar 8y agoI haven't tested this since then, but this wasn't the case in 2014[1]. I haven't noticed these issues since then, but back then it cost hundreds of microseconds -- 8 orders of magnitude more than "a few ns". EDIT: I just re-did the relevant tests on Go 1.11.5. It's significantly better than in 2014 but it still costs between ~20us and ~50us ("only" 4 orders of magnitude more than "a few ns"). goos: linux goarch: amd64 BenchmarkPut-8 50000 27502 ns/op BenchmarkPutDefer-8 30000 46774 ns/op BenchmarkGet-8 50000 29812 ns/op BenchmarkGetDefer-8 20000 89701 ns/op PASS [1]: https://lk4d4.darth.io/posts/defer/ https://lk4d4.darth.io/posts/defer/
- yalue 8y agoIt actually did get significantly better in Go 1.8, in early 2017. See https://golang.org/doc/go1.8 https://golang.org/doc/go1.8 ("The overhead of deferred function calls has been reduced by about half."). However, I think this change was mostly just an optimization for common cases with few arguments or no closures (e.g. "defer f.Close()").
- cyphar 8y agoI just re-did the benchmarks with Go 1.11.5, it is much better but it's still 4 orders of magnitude more than "a few nanoseconds".