3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

速度改善のためRailsコードをGoで書き直して、FFIを使ってみた

Last updated at Posted at 2022-08-09

railsのコードをgolangに書き直したら、10倍早くなりました

ffiでgoを呼び出すようにすると早くなるという記事を見たので、

実際に使用してみたら、本当に10倍ほど早くなりました。

ディレクトリ構成

今回はrailsなのでlib配下にffiディレクトリを用意して、その中にソースコードをおきました。

  • /app
    • /models
      • test.rb
  • /lib
    • /ffi
      • sample.rb
      • .directory-list.md
      • /src
        • sample.go
        • /model
          • data.go
      • /bin
        • sample.so

事前準備

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倍近く速くなったのでよかったです。

参考

RailsコードをGoで書き直して、FFIを使ってRailsからGoの関数を実行させて実行時間を5%以下に短縮させた

RubyからGoの関数をつかう → はやい

Go and Ruby-FFI

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?