Go最近の一番のお気に入り言語なのですが、モヤモヤするところも出てきてるので備忘録的にポエムを書いてみました。
汎用的に書こうとすると遅くなる
ちょっと柔軟に書こうとするとReflection, Interfaceが必要になってきて遅くなる[1]。
Reflectionは色々コード生成ライブラリがあるからいいけど[2][3], InterfaceをつかないとGoっぽいコードにならない。
まぁ Interfaceによる性能劣化が実際に実務で問題になることは当面なさそうだし、
部分的に使わなければいいだけなのだけど、少しモヤモヤ
Rustだとこの辺オーバーヘッドなしに実現してるらしいので気になってる。実務で使うにはもっとエコシステムが育つ必要があるけど、、
package benchmark
import (
"testing"
)
type CalcInterface interface {
Add(a, b int) int
}
type CalcImpl struct {
}
func (m *CalcImpl) Add(a, b int) int {
return a + b
}
type CalcStructWithPointer struct {
impl *CalcImpl
}
type CalcSturctWithInterface struct {
impl CalcInterface
}
type CalcStructWithFunctionPointer struct {
impl func(a, b int) int
}
func BenchmarkCalcStructWithPointer(b *testing.B) {
c := &CalcStructWithPointer{
impl: &CalcImpl{},
}
for i := 0; i < b.N; i++ {
c.impl.Add(10, 20)
}
}
func BenchmarkCalcSturctWithInterface(b *testing.B) {
c := &CalcSturctWithInterface{
impl: &CalcImpl{},
}
for i := 0; i < b.N; i++ {
c.impl.Add(10, 20)
}
}
func BenchmarkCalcStructWithFunctionPointer(b *testing.B) {
c := &CalcStructWithFunctionPointer{
impl: func(a, b int) int {
return a + b
},
}
for i := 0; i < b.N; i++ {
c.impl(10, 20)
}
}
BenchmarkCalcStructWithPointer-8 2000000000 0.30 ns/op
BenchmarkCalcSturctWithInterface-8 1000000000 2.71 ns/op
BenchmarkCalcStructWithFunctionPointer-8 1000000000 2.42 ns/op
net/rpcのフリーズはgRPCのゴリ押しではないか?
去年、net/rpcモジュールがフリーズされた。
net/rpcモジュールはGoからしか使えないものの、IDL不要でgRPCより高速。
上記のissueでは、構造的に問題があるとかバグが多いとか書いていたけど、説得力がなかった、、フリーズはgRPCのゴリ押しではないかと邪推してしまう。言語と特定の技術をセット売りされているようでモヤモヤ
Null Safe欲しい
Nullチェックめんどくさい
シンタックスシュガー欲しい
if err != nil とか、、
関連して、Error モナドが使えるといいのに
無理やりモナドやるならこうかな? 遅いけど
func taskA(context map[string]interface{}) error {
fmt.Println("taskA done")
return nil
}
func taskB(context map[string]interface{}) error {
return fmt.Errorf("I don't want to do task B!")
}
func taskC(context map[string]interface{}) error {
fmt.Println("taskC done")
return nil
}
func do(context map[string]interface{}, tasks ...func(context map[string]interface{}) error) error {
for _, task := range tasks {
if err := task(context); err != nil {
return err
}
}
return nil
}
func main() {
err := do(nil,
taskA,
taskB,
taskC)
fmt.Println(err)
}
参考文献
[1] http://inforno.net/articles/2015/12/11/go-interface-performance-penalty
[2] Genny https://github.com/cheekybits/genny
[3] Who needs generics? Use ... instead! https://appliedgo.net/generics/