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


Web@shikeapp0909

WebmamanokoGo

RailsWebGo

RailsGo


Go

mamanoko



14

1KPI使

GoShared LibraryFFI使Rails

2018/07/02








RubyRailsRubyGolang



RubyGolang




RailsGo


FFI使FFIForeign Function InterfaceWikipedia

RubyRuby FFIGem使Gem使RubyC


Ruby


ffi

$ gem install ffi

C
require 'ffi'

module Sum
  extend FFI::Library
  ffi_lib 'sum.so'
  attach_function :sum, [:int, :int], :int
endppSum.sum(1, 2)

sum.sosum

Go

package main

import "C"

// export sum
func sum(aint, b int) int {
    return a + b
}

func main() {}

mainFFI// export 

GoCShared LibraryGo

$ go build -buildmode=c-shared -o sum.so sum.go

sum.gosum.so

RubyGo



c7.se

qiita.com

mamanoko


mamanoko


1. 校正対象の文章をDBから抽出
2. チェックする単語とチェックから除外する単語をDBから抽出
3. 除外単語を除き、校正対象の文章にチェック対象の単語が存在するか精査
4. チェックに引っかかった箇所をハイライトして表示



3Go

GoDBORMActiveRecordActiveRecordGo Go1

RailsstringGostring*C.char stringC

jsonstringGo*C.charstringC.GoString(articleJSON)

stringJSONstructjson.Unmarshal([]byte(C.GoString(articleJSON)), &article)

CcharGostring

jsonRails -> []byte -> string -> *C.char


resultJSON, err := json.Marshal(result)
if err != nil {
    log.Fatalf("result json encode error : %s\n", err)
}

return C.CString(string(resultJSON))

FFIRailsGo



Railsliblib/ffi/textlint.rb
require 'ffi'

class FFI::Textlint
  extend FFI::Library

  ffi_lib 'lib/ffi/bin/textlint.so'

  attach_function :textlint, [:string, :string], :string
end

GolibsrcbinGOPATHRailsbin

structs
package main

import (
    "C"
    "encoding/json"
    "ffi/app/struct"
    "log"
    "string"
)

var dictionaries []structs.Dictionary

//export textlint
func textlint(articleJSON *C.char, dictionariesJSON *C.char) *C.char {
    article := new(structs.Article)
    articleErr := json.Unmarshal([]byte(C.GoString(articleJSON)), &article)
    if articleErr != nil {
        log.Fatalf("article json parse error : %s\n", articleErr)
    }

    dictionariesErr := json.Unmarshal([]byte(C.GoString(dictionariesJSON)), &dictionaries)
    if dictionariesErr != nil {
        log.Fatalf("dictionaries json parse error : %s\n", dictionariesErr)
    }

    result := new(structs.Result)

    // lint実施...

    resultJSON, err := json.Marshal(result)
    if err != nil {
        log.Fatalf("result json encode error : %s\n", err)
    }

    return C.CString(string(resultJSON))
}

func main() {}

RailsGoGo

FFI使


Ruby meets Go from NTT Communications Technology Development 
www.slideshare.net


RailsGo


Before
45~60sec

After
1~3sec

ChromeDevNetwork

使24使

4

Mamanoko Tips






stringindexslicestringregexFindAllStringIndex使stringindexsliceslice[][]byte

regex使

dictionary.Wordtext
dictionaryWord := regexp.MustCompile(regexp.QuoteMeta(dictionary.Word))
highlights := dictionaryWord.FindAllStringIndex(text, -1)



indexhighlightsindex
var highlightBytesList [][]int
for _, highlightBytes := range highlights {
    isHighLight := true
    for _, unhighlightBytes := range unhighlights {
        if unhighlightBytes[0] <= highlightBytes[0] && highlightBytes[1] <= unhighlightBytes[1] {
            isHighLight = false
            break
        }
    }
    if isHighLight {
        highlightBytesList = append(highlightBytesList, highlightBytes)
    }
}





<span class="hightlight"></span>
var lintedText []string
for index, textRune := range text {
    isHighlight := false
    for _, highlightBytes := range highlightBytesList {
        if highlightBytes[0] <= index && index < highlightBytes[1] {
            isHighlight = true
            break
        }
    }

    if isHighlight {
        lintedText = append(lintedText, "<span class=\"highlight\">"+string([]rune{textRune})+"</span>")
    } else {
        lintedText = append(lintedText, string([]rune{textRune}))
    }
}

return strings.Join(lintedText[:], "")

textstringstringfor range1rune

slice

rune[]rune{textRune}

[]stringstringJoin使


Go1

Go使

Go

RubyRailsFW

Go

We're Hiring!!


Web / 



www.wantedly.com