I understand that defer sentence must be executed whenever the function ends, but is it really needed to take care about JavaScript exceptions? The problem is performance. defer code is converted into a little complicated JS code with $methodVal and performance is not good.
In Go semantics, exception doesn't exist and, IIUC, GopherJS generates throw sentence only for panics. In most cases, like when no recover is called, defer is not called when panic-ing.
performance is not good.
Do you have benchmarks to demonstrate this? It would be helpful to have a discussion about something concrete.
To answer your question, I don't know, this would require investigation. Perhaps @neelance knows more about this.
Defer is always called when panic-ing.
Dmitri Shuralyov notifications@github.com schrieb am Fr., 26. Mai 2017,
20:23:
performance is not good.
Do you have benchmarks to demonstrate this? It would be helpful to have a
discussion about something concrete.To answer your question, I don't know, this would require investigation.
Perhaps @neelance https://github.com/neelance knows more about this.—
You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub
https://github.com/gopherjs/gopherjs/issues/648#issuecomment-304354993,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAA_ONBY9g-Pl5Tsy2e0q0I1mbhwCJj2ks5r9xi7gaJpZM4Nn6Vg
.
@shurcooL Here you are:
package test
import (
"testing"
)
var i = 0
func begin() {
i++
}
func end() {
i--
}
func fooNormal() {
begin()
end()
}
func fooDefer() {
begin()
defer end()
}
func BenchmarkNormal(b *testing.B) {
for i := 0; i < b.N; i++ {
fooNormal()
}
}
func BenchmarkDefer(b *testing.B) {
for i := 0; i < b.N; i++ {
fooDefer()
}
}
$ gopherjs test --bench="." github.com/hajimehoshi/test
gopherjs: Source maps disabled. Install source-map-support module for nice stack traces. See https://github.com/gopherjs/gopherjs#gopherjs-run-gopherjs-test.
BenchmarkNormal 300000000 5.26 ns/op
BenchmarkDefer 10000000 137 ns/op
PASS
fatal error: all goroutines are asleep - deadlock!
FAIL github.com/hajimehoshi/test 4.047s
Defer is always called when panic-ing.
I was misunderstanding. defer was actually called:
package main
func main() {
defer println("defer is called")
panic("panic!")
}
$ go run test.go
defer is called
panic: panic!
goroutine 1 [running]:
main.main()
/Users/hajimehoshi/go/src/github.com/hajimehoshi/test/test.go:5 +0x99
exit status 2
Thanks for the benchmark. I can reproduce results:
$ gopherjs test --bench=.
BenchmarkNormal 300000000 5.49 ns/op
BenchmarkDefer 10000000 138 ns/op
PASS
ok github.com/shurcooL/play/31 4.280s
$ $ GOMAXPROCS=1 go test -bench=.
BenchmarkNormal 2000000000 1.80 ns/op
BenchmarkDefer 30000000 53.2 ns/op
PASS
ok github.com/shurcooL/play/31 5.437s
So, with Go 1.8.3, gc compiler, the defer version is 29x slower. With GopherJS, the defer version is 25x slower.
That seems very reasonable to me, is it not?
Defer is a run-time mechanism and hast a small overhead. Depending on the context, it may or may not be expensive to use. As I wrote in https://dmitri.shuralyov.com/idiomatic-go#use-defer-if-its-overhead-is-relatively-negligible-or-it-buys-meaningful-readability, it should be used only when its overhead is relative negligible, or if it buys meaningful readability. Also see https://lk4d4.darth.io/posts/defer/.
Aha, I didn't realize defer costs so much even with the Go official compiler. Thanks, I'll close this issue.