railsのコードをgolangに書き直したら、10倍早くなりました
ffiでgoを呼び出すようにすると早くなるという記事を見たので、
実際に使用してみたら、本当に10倍ほど早くなりました。
ディレクトリ構成
今回はrailsなのでlib配下にffiディレクトリを用意して、その中にソースコードをおきました。
- /app
- /models
- test.rb
- /models
- /lib
- /ffi
- sample.rb
- .directory-list.md
- /src
- sample.go
- /model
- data.go
- /bin
- sample.so
- /ffi
事前準備
ffiのgemを入れておく
gem install ffi
go.mod ファイル作成
cd lib/ffi/src
go mod init ffi/src
上記を実行すると、下記のようなgo.modファイルが作れられます。
これがないと、各ファイルを呼び出せなくてエラーになりました。
module ffi/src
go 1.17
ruby側
sample.rb
require "ffi"
class Ffi::Sample
extend FFI::Library
ffi_lib File.dirname(__FILE__) + '/bin/sample.so'
# golangとの接続用
attach_function :sampleCalc, [:string], :string
end
test.rb
class Test
def execute
test_data = []
test_data << {
X: 1,
Y: 2,
Comment: "abc"
}
test_data << {
X: 2,
Y: 3,
Comment: "efg"
}
ret = Ffi::PathFind.sampleCalc test_data.to_json
ret= JSON.parse(ret, :symbolize_names => true)
end
end
go側
data.go
package model
type Sample struct {
X int
Y int
Comment string
}
sample.go
package main
import (
"C"
"fmt"
"encoding/json"
"ffi/src/model"
"log"
)
//export sampleCalc
func sampleCalc(sampleJson *C.char) *C.char {
// jsonをstructに入れる
sm := []model.Sample{}
err := json.Unmarshal([]byte(C.GoString(sm)), &sm)
if err != nil {
log.Fatalf("gridinfo json parse error : %s\n", err)
}
:
:
処理を書いていく
:
:
// structをjson形式に変換
resultJSON, err := json.Marshal(sm)
if err != nil {
log.Fatalf("result json encode error : %s\n", err)
}
return C.CString(string(resultJSON))
}
//export sampleCalc
を記述しないと呼び出しができないようです。
exportの前にスペースが入ってもダメです。
goをbuildする
goのファイルをビルドします。
go build -buildmode=c-shared -o ../bin/sample.so sample.go
これでgolangのプログラムrubyから呼び出すことができるようになりました。
まとめ
今回はハッシュの配列(go側では構造体の配列)を渡す必要があり、
方法が不明だったため、json形式に変換してから受け渡しするようにしました。
もし方法があれば教えていただきたいです。
goでの開発が初めてだったので、調べながらコードを書いたので、
もしかしたらもっと良い方法があるのかも。。
とはいえ、速度は10倍近く速くなったのでよかったです。