web-dev-qa-db-ja.com

複数の据え置きvs据え置き匿名関数

順序に依存する複数のdeferステートメントを発行したり、ロジックをパッケージ化する匿名関数を延期したりする方が安全または慣用的ですか?

例:

defer os.Remove(tempFile.Name())
defer tempFile.Close()

上記の場合、構文は最小限ですが、延期の順序は実行されるロジックと逆です。

以下の場合、より多くの行、より多くの「構文」がありますが、ロジックはより自然な順序になっています。

defer func() {
    tempFile.Close()
    os.Remove(tempFile.Name())
}()

どちらを使用しますか?

25
Vlad Didenko

この例では、特にエラー処理を追加すると、無名関数が読みやすくなります。

f, err := ioutil.TempFile("", "prefix")
if err != nil {
  log.Println("creating temp file:", err)
  return
}
defer func() {
  err := f.Close()
  if err != nil {
    log.Println("close:", err)
  }
  err = os.Remove(f.Name())
  if err != nil {
    log.Println("remove:", err)
  }
}()

複数のリソースがある場合、通常は複数のdefersが適切です。

26
Ross Light

複数のリソースがある場合は、通常、複数のdeferが適切です。

2019年4月:ただし、その場合は、Go 1.13(2019年第4四半期)を検討してください。これは、 go issue 14939: "runtime:defer is slow" および go issue 6980: "cmd/compile:一部の遅延をスタックフレームに割り当てます"

参照 Go CL 171758: "cmd/compile、runtime:allocate defer records on the stack"

関数本体で延期が最大で1回実行されると、その延期レコードをヒープではなくスタックに割り当てることができます。

これにより、このような遅延(非常に一般的)が速くなります。

この最適化は、cmd/goバイナリの370の静的遅延サイトのうち363に適用されます。

name     old time/op  new time/op  delta
Defer-4  52.2ns ± 5%  36.2ns ± 3%  -30.70%  (p=0.000 n=10+10)

2019年10月(Go 1.13は数週間前にリリースされました)

これは 確認済み(Brad Fitzpatrick) with CL 190098

据え置きステートメントのコスト[go test -run NONE -bench BenchmarkDefer$ runtime]

With normal (stack-allocated) defers only:         35.4  ns/op
With open-coded defers:                             5.6  ns/op
Cost of function call alone (remove defer keyword): 4.4  ns/op

しかし Damien Griskyが追加

Deferの方が安くなりますが、パニック/回復はより高価です。

Cost of defer: 34ns -> 6ns.
Cost of panic/recover: 62ns -> 255ns

それは悪いトレードオフではありません。

2
VonC