良い情報を発信していきたいので、寄付頂ければ幸いです

BTC 13JpgsF3n6K2WhjEeUuUUqS7V71gWdFx56
BCH 18q6rfi9ynyTgynrB8tJ2eSDLPQM32RZk5
ETH 0x00afa2f16a062450ebedb7e3cabe79cc7801a231
NEM NAJOM3CD26UI3CF4LUCWLN2QJEHGFFJZ7AHLRAOQ
Shield SbrJFMavwAx2y6S7kLnxZLDgqrgjBYMVU4
MONA MPpuEnmqDYBCxSZyG5cBDt6UWtXczmRmkn

作っているOSSメモ - 旅行しながら働く ムラサメ研究所社長ブログ

Golangの値渡し、ポインタ渡し、インタフェース呼び出しの計測

interface呼び出しのパフォーマンスが気になったので
ついでに値レシーバとポインタレシーバの比較もしてみた

package main

import (
    "fmt"
    "time"
)

type IHoge interface {
    Print()
}

type HogeImpl struct {
    num int
}

func (s *HogeImpl) Print() {
    //fmt.Printf("%d\n", s.num)
    //s.num = s.num + 1
}

func print1(s HogeImpl) {
    s.Print()
}
func print2(s *HogeImpl) {
    s.Print()
}

func printInterface1(s IHoge) {
    s.Print()
}
func printInterface2(s *IHoge) {
    (*s).Print()
}


func bench(f func()(),l int){
    start := time.Now()
    for i:=0; i< l; i++{
        f()
    }
    end := time.Now()
    fmt.Printf("%fsec\n", (end.Sub(start)).Seconds())
}

func main() {
    t := 500000000
    h := &HogeImpl{0}
    bench(func(){h.Print()}, t)
    bench(func(){print1(*h)}, t)
    bench(func(){print2(h)}, t)
    bench(func(){printInterface1(h)}, t)

    //  cannot use &h (type **HogeImpl) as type *IHoge in argument to printInterface2:
    //  *IHoge is pointer to interface, not interface
    //printInterface2(&h)
    //printInterface2(&h)

}

play.golang.org

Playgroundでは負荷かかる処理は実行できないのでローカルでどうぞ

今回は、構造体のメンバも int1個なので、ポインタでも値でも変わらないサイズなので
殆どオーバーヘッドはないはず

結果は毎回誤差はあるが

0.640506sec
0.644128sec
0.642002sec
1.175025sec

基本的に、直接呼出し、構造体値渡し、構造体ポインタ渡しは 誤差範囲 ただ、インタフェース呼び出しは約2倍かかった

5億回の呼び出しで0.5秒のオーバーヘッド。10億分の1秒だ
うちのPCが3.6GHzなので、だいたい4クロック程度(スーパースカラー無視して)

オブジェクト指向であれば、仮想関数テーブル呼び出しなどでそこそこ大きなオーバーヘッドが想定されるが
Goの場合はダックタイピングなので、単純に普通の関数呼び出しが1回増えただけなんだろう

オブジェクト指向とちがい、この辺りのオーバーヘッドは無いに等しい
意外とダックタイピングいいかも
ま、C++でも可能ならオブジェクト指向ではなく、テンプレートでダックタイピング継承するし。

Golangのinterfaceに対する疑問を調べてみた

インタフェース使ってDIの作成していたときに
構造体なら暗黙的に ポインタでも実体でも "." でアクセスできます
C++的にいえば "->" が必要な場所でも暗黙的に "." でアクセスできる
ところが interfaceにすると (*s).Hoge() と、明示的にキャストしないといけなく、"->"が存在しないので面倒でした

そこで仮説として、Goのinterfaceは、既に参照ではないか? と思ったので調べてみました

play.golang.org

結論から言えば、やはりすでに参照です(上記の結果は見にくいですが、Interfaceは実態を渡してもメンバ変数が変化している)

C#でSAPが書ける Blazorフレームワークを勉強したい

qiita.com

まず、私のWeb開発に対する意見なんだけど
ごめんなさい、スクリプト言語は私の中では5年前に終わった
Railsが出た当初は、こんなエコなシステム!RailsだけでWebはいいじゃん
って思った時期もあったんだけど、結局はコンパイルできないためテストコードいっぱい必要で開発速度アドバンテージ失い
PaaSの方がアドバンテージ高く
そしてスクリプトの実行速度の遅さに、決定的だったのがCPUがマルチコア化し、プロセス生成ではもうスペック出せない事

そこで、非同期フレームワークnodeが出たのだが、結局はnodeの速度限界とシングルスレッドしか扱えない事、言語構文から無理が出て

最終的には、JavaScala)、Erlang(Elixir)、 C#、Go、Rust・・・ といった、非同期可能なコンパイル言語以外ないなーと思うように その中から選ぶと
Scalaコンパイル遅い。Javaは言語が古かった(最近のJavaはラムダも使えたり十分だが昔は一貫してオブジェクト指向で関数型要素が入ってこなかった)、Erlang(Elixir)はVMの速度が遅いという話で及び腰、Rustは開発がちょっと遅くて安定しない、C#Windowsでしか動かないから論外
で、Goという選択肢を選んだ

が、最近はその状況もかわってきてる

Javaは最近言語をどんどん柔軟に関数型プログラミングを採用してきたので今は十分使える

Erlangは実際に評価してみないとな・・多分今は問題になるほど遅くはないと思う

www.atmarkit.co.jp
MicrosoftがRust をバックアップするとの事で状況は変わってきた

そして、.net Coreという、マルチプラットフォームで動作する .netフレームワークMicrosoftが発表した!!

という事で、最も期待しているのがC#
そのなかで Blazorというフレームワークが面白そうだ

BlazorはC#でSPAを作れるらしい
なんとViewもC#で作れる
仕組みは、C#で書いてビルド時に .netのILからWebAssemblyに変換してブラウザで実行していると!

ってことで、Blazor注目!!

TODOアプリ

そういえば、ライブラリの勉強の時にみんなが作るTODOアプリ
私は1回も書いたことがない
一度作ってみようか?

でも実際は、TrelloとかRedmineGithubを使うんだけど

個人用のTODOアプリを今まで Trelloで使ってて、ちょっと機能大きすぎ感があり
GMailのTODOアプリに変えたけど今度は機能すくなく

今は Microsoft TO-DO がちょうどいい大きさなので、使っています

会社のホームページ変更とブログ移転の検討

www.murasame-lab.com
上記が今の会社のホームページとなっている
知人のWebデザイナーが仕事が暇というので仕事をしてもらったが、その後体調を崩してメンテがしてもらえていない
そのため、会社のホームページを一新しようと思っている

とりあえず、2年ほど前から海外移住した友達のWebデザイナーに打診中だが
断られたら、ひとまずWordPressで作ろうと思う(実は今のページを作る前にモックでWPで作ったのがある)

それに加え、自社ドメインを強くするためにも、ブログを自ドメインにしようとおもう
はてなブログはProにすれば独自ドメインにすることが可能だが
もしかしたら自社のWordPressに変更するかもしれない

移行手順はもう完璧だ

とりあえず、少しずつ会社らしくしていこう

Go言語にデストラクタがないので何とかしたい

Goの真実

Goにはコンストラクタもデストラクタもない
ので、便宜上 コンストラクタに当たる関数は 構造体名の頭にNewのプリフィクスを入れた関数である
C言語だと init_hoge とするのが NewHogeになっただけだ
そして、そのNew関数は構造体のポインタを返す(Cでも呼び方わすれたが構造体をClassっぽく書くときに同じことをする)
結局C言語なのだ(念のため、C++ではない)

そしてデストラクタがないので、呼び出し側が終了関数を明示的に呼ばなければならない
Cだと delete_hoge とか release_hoge、teardown_hoge・・・ 呼び名が統一されてる覚えがない
Goにもデストラクタとして呼び名が統一されている気がしない
つまり、よく呼び忘れてメモリリークする!!

https://play.golang.org/p/i6-vTaTVx9Z

package main
import (
    "fmt"
)


type File struct{
  filename string
}

func NewFile(f string) *File{
  fmt.Printf("File{%s} new&open\n", f)    
  return &File{f}
}

func(s *File)Read(){
  fmt.Printf("File{%s} read\n", s.filename)    
}

func(s *File)Close(){
  fmt.Printf("File{%s} close\n", s.filename)    
}




func main() {
  fmt.Println("main start")    

  file := NewFile("test")
  file.Read()

  fmt.Println("main finish")    

}



main start
File{test} new&open
File{test} read
main finish

上記のコードはRAII原則にのっとり、New時にリソースをOpenしているが
残念なことにライブラリを使う人がClose()を呼び忘れたため、リソースリークしている

defer

いちおうGoにはdeferがあり、関数が終了した時に実行されるファイナライザを書くことができる

https://play.golang.org/p/LPEm7oDSf2M

...
  file := NewFile("test")
  defer file.Close()
  file.Read()
...

main start
File{test} new&open
File{test} read
main finish
File{test} close

これは便利だが、このdeferはライブラリを使う側の人間が忘れたらCloseしてくれない

構造体の方でdeferを入れたい

そう考えるかもしれないが、deferはあくまで、関数の終了時のファイナライザだ
コンストラクタにdeferを入れると当然コンストラクトした後にすぐCloseが走る

https://play.golang.org/p/aav2psenFxF

...
func NewFile(f string) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  defer file.Close()
  return &file
}
...

main start
File{test} new&open
File{test} close
File{test} read
main finish

runtime.SetFinalizer

見るからに悪手だが、ランタイムライブラリに、ガーベージコレクションが走った時に実行されるコールバックがある

  x:= "hoge"
  runtime.SetFinalizer(&x, func(x *string){fmt.Println("SetFinalizer")})

  // 上記だと即終了しGCが走らないので故意にGCを走らせ待機させる

と、上記では xがGCされる時に 次のラムダ式が呼ばれる。ラムダ式の引数はGCされるオブジェクトのポインタだ

https://play.golang.org/p/QYkVhC1uQKP

これを構造体に当てはめられないか?

コンストラクタでSetFinalizerする

コンストラクタで構造体を作成し、ポインタを使う側に返すが、このポインタにSetFinalizerを設定し
構造体がGCされるときにCloseしてみよう
念のためインスタンスを複数作る

https://play.golang.org/p/LL3ejYYcowe

package main
import (
    "fmt"
    "runtime"
    "time"
)


type File struct{
  filename string
}

func NewFile(f string) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  runtime.SetFinalizer(&file, func(f *File){f.Close()})

  return &file
}

func(s *File)Read(){
  fmt.Printf("File{%s} read\n", s.filename)    
}

func(s *File)Close(){
  fmt.Printf("File{%s} close\n", s.filename)    
}

func(s *File)Nop(){
  fmt.Printf("File{%s} nop\n", s.filename)    
}


func hoge(){
  file := NewFile("test")
  file.Read()
  file2 := NewFile("test2")
  file2.Read()
  file3 := NewFile("test3")
  file3.Nop()
}

func main() {
  fmt.Println("main start")    
  
  hoge()
  runtime.GC()
  time.Sleep(1 * time.Second)

  fmt.Println("main finish")     
}

main start
File{test} new&open
File{test} read
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} nop
File{test3} close
File{test2} close
File{test} close
main finish

綺麗に動いてしまった・・・

レシーバーにSetFinalizerする

先ほどはコンストラクタにSetFinalizerをしたので、たぶん確実にFinalizerが走るが
特定メソッドで、レシーバーに対してSetFinalizerしたらどうなるか?
たとえばReadメソッドでやってみる

func(s *File)Read(){
  runtime.SetFinalizer(s, func(f *File){f.Close()})
  fmt.Printf("File{%s} read\n", s.filename)    
}


main start
File{test} new&open
File{test} read
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} nop
File{test2} close
File{test} close
main finish

すばらしい、意図したとおりに動いた(file3はReadメソッドを読んでないのでFinalizeされてない) 使うシーンがあるか不明だが、例えばOpenメソッドを呼んだ時だけCloseのFinalizerを設定
という使い方も不可能ではなさそうだ

でもね

GCされるまでは開放されないので、もし長時間GCされずに存在していたら
その間ファイルを開きっぱなしだったり、ネットワークリソースを開放しない事になる
だから、SetFinalizerでデストラクターをするのは、よくない事は明確である・・

どこを間違えたかと思ったが、Goはオブジェクト指向言語ではないというのが答えだ
本当は関数型のように書くべきなんだろうか?

Callback にすべきではないか?

コンストラクタでdeferしても、コールバックで続きの処理を行えば
コンストラクタのコンテキスト内なので問題ないはずだ・・・

https://play.golang.org/p/1ZBphVFMHKM

  package main
import (
    "fmt"
    "runtime"
    "time"
)


type File struct{
  filename string
}

func NewFile(f string, cb func(file *File)*File) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  defer file.Close(nil)

  if(cb != nil){
    return cb(&file)
  }    
  return &file
}

func(s *File)Read(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} read\n", s.filename)    
  if(cb != nil){
    return cb(s)
  }    
  return s
}

func(s *File)Close(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} close\n", s.filename)
  if(cb != nil){
    return cb(s)
  }
  return s
}

func(s *File)Nop(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} nop\n", s.filename)    
  if(cb != nil){
    return cb(s)
  }
  return s
}


func hoge(){
  _ = NewFile("test", func(file *File)*File{
    file.Read(func(file *File)*File{
      fmt.Println("callback hell")
      return file          
    })
    return file
  })
}

func main() {
  fmt.Println("main start")    
  
  hoge()
  runtime.GC()
  time.Sleep(1 * time.Second)


  fmt.Println("main finish")    

  
}



main start
File{test} new&open
File{test} read
callback hell
File{test} close
main finish

やったぜ!!(やりたくない

もう少し実用的に

ReadやWriteの引数ちゃんと作って、もう少し実用的にします
エラーも返さないとね(エラー処理は省略

https://play.golang.org/p/XbAWauNUNyZ

package main

import (
    "fmt"
    "runtime"
    "time"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File) (*File, error)) (*File, error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    defer Close(&file, nil)

    if cb != nil {
        return cb(&file)
    }
    return &file, nil
}

func Read(file *File, len int, cb func(file *File, readed *string) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} read\n", file.filename)
    data := "readed"

    if cb != nil {
        return cb(file, &data)
    }
    return file, nil
}

func Write(file *File, data *string, cb func(file *File, written int) (*File, error)) (*File, error) {
    written := len(*data)
    fmt.Printf("File{%s} write {%s}  size={%d}\n", file.filename, *data, written)

    if cb != nil {
        return cb(file, written)
    }
    return file, nil
}

func Close(file *File, cb func(file *File) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} close\n", file.filename)
    if cb != nil {
        return cb(file)
    }
    return file, nil
}

func Nop(file *File, cb func(file *File) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} nop\n", file.filename)
    if cb != nil {
        return cb(file)
    }
    return file, nil
}

func hoge() {
    _, _ = NewFile("test", func(file *File) (*File, error) {
        Read(file, 5, func(file *File, readed *string) (*File, error) {
            fmt.Printf("READ{%s}\n", *readed)
            data :="hogehoge"
            Write(file, &data, func(file *File, written int) (*File, error) {
                fmt.Printf("WRITE [%d]bytes\n", written)
                return file, nil
            })

            return file, nil
        })
        return file, nil
    })

}

func main() {
    fmt.Println("main start")

    hoge()
    runtime.GC()
    time.Sleep(1 * time.Second)

    fmt.Println("main finish")

}


main start
File{test} new&open
File{test} read
READ{readed}
File{test} write {hogehoge}  size={8}
WRITE [8]bytes
File{test} close
main finish

ライブラリを使う側からはもうCloseの事考えなくてよくなりました
やったね!!

しかし、コールバック地獄、エラー処理の場所・・・もっと考えなければならない事が増えてしまいました

しかもGoはJavaScriptC#のような、クロージャを簡単に書く書式もないので、まいかいfunc ()と大変

コールバック必要なのはNewだけだったんや!

別に非同期プログラミングのようなコールバックする必要はない
GoはGoroutineという素晴らしい仕組みがあるので、同期っぽく書けばいい
わざわざ人間に不親切なコールバック地獄にする必要がない

コールバックを使った理由はNew関数を抜けるときにdeferdでCloseし忘れを防ぎたい
処理が終わるまでNew関数を抜けないためだ
ReadやWriteまでコールバックする必要がない。Newだけでよかったんだ

https://play.golang.org/p/WtixVskDI7R

package main

import (
    "fmt"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File)error) ( error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    defer file.Close()
        err := cb(&file)

    return err
}

func (s *File) Read(){
    fmt.Printf("File{%s} read\n", s.filename)
}

func (s *File) Close() {
    fmt.Printf("File{%s} close\n", s.filename)
}

func (s *File) Nop() {
    fmt.Printf("File{%s} nop\n", s.filename)
}

func hoge() {
    _ = NewFile("test", func(file *File)error{
            file.Read()
        return nil
    })
}

func main() {
    fmt.Println("main start")

    hoge()

    fmt.Println("main finish")

}

main start
File{test} new&open
File{test} read
File{test} close
main finish

当然ちゃんとクローズされるし、コールバックは初回のNewだけなので、コードの見通しも悪くない
ただ、コールバックとして全てを中にいれなければいけない
オブジェクト指向脳だと自由に構造体を使い回したいと思うだろう

コールバックとオブジェクト指向のハイブリッド

Goにはメソッドのオーバーライドやデフォルト引数がない
可変長引数で処理できるが今回はそこは手抜きで、CallbackがnilだとClose管理はライブラリでは行わない

https://play.golang.org/p/M6g-dqUz86P

package main

import (
    "fmt"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File) error) (*File, error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    var err error = nil
    if cb != nil {
        defer file.Close()
        err = cb(&file)
    }

    return &file, err
}

func (s *File) Read() {
    fmt.Printf("File{%s} read\n", s.filename)
}

func (s *File) Close() {
    fmt.Printf("File{%s} close\n", s.filename)
}

func (s *File) Nop() {
    fmt.Printf("File{%s} nop\n", s.filename)
}

func hoge() {
    // クロージャで自動的にCloseする
    _,_ = NewFile("test", func(file *File) error {
        file.Read()
        return nil
    })
    
    // クローズは使う側が責任持つ
    file2, _ := NewFile("test2", nil)
    defer file2.Close()
    file2.Read()

    // 忘れると当然リークします
    file3, _ := NewFile("test3", nil)
//  defer file3.Close()
    file3.Read()
}

func main() {
    fmt.Println("main start")

    hoge()

    fmt.Println("main finish")

}


main start
File{test} new&open
File{test} read
File{test} close
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} read
File{test2} close
main finish

結局どれがいいのか?

Finalizerは動作はするが、GCが走るまで開放が行われないので実用的ではない
コールバック地獄はダメ!絶対!(おそらくGoはGoroutineで同期的に書く設計なので、promise/futureやasync/awaitのようなコールバック解決ライブラリはあまり出ない)
Newのコールバックは場合によっては使えると思う
でも基本は、ライブラリを使う側が責任もって後始末しなければいけないっぽい

ハイブリッドは良さそう