Railsã³ã¼ããGoã§æ¸ãç´ãã¦ãFFIã使ã£ã¦RailsããGoã®é¢æ°ãå®è¡ããã¦å®è¡æéã5%以ä¸ã«ç縮ããã
ããã«ã¡ã¯ãWebãã¼ã ã®æ´å±±ï¼@shikeapp0909ï¼ã§ãã
ä»åã¯ãå¼ç¤¾Webã¢ããªãmamanokoãã®ä¸é¨ã«å¦çé«éåã®ããã«Goãå°å ¥ããã®ã§ãã©ã®ããã«å°å ¥ãããããç´¹ä»ãããã¨æãã¾ãã
Railsã§éçºãã¦ããWebã¢ããªå ã«ã©ã®ããã«Goãåãå ¥ããã®ããå®éã®ã³ã¼ãä¾ã交ããªãã解説ãã¦ããããã¨æãã¾ãã
ãªãRailsç°å¢ã«Goãå°å ¥ããã®ã
ãããããªãGoãå°å ¥ãããã¨ã«ãªã£ãã®ãã¨çåã«æããããã¨æãã¾ãã
mamanokoã¯ã¡ãã£ã¢ãµã¼ãã¹ã®ãããã©ã¤ã¿ã¼ããã«æç« ãæ¸ãã¦ããã ãã¦ãã¾ããè¤æ°ã®ã©ã¤ã¿ã¼ããããããæç« éãè¨å¤§ã«ãªãããã表è¨æºãã誤åãã©ããã¦ãåºã¦ãã¦ãã¾ãã¾ãã
ãã®ãããæç« æ ¡æ£ãå¿ è¦ã§ããã以åãããã®æ©è½ãæä¾ãã¦ãã¾ããã
ããããã®æç« æ ¡æ£ã®æ©è½ã¯è¨äºã®æç« ã«å¯¾ãã¦è¡¨è¨æºããªã©ãã§ãã¯ãããåèªãç·ãªãããå¦çããã¦ãããããããã©ã¼ãã³ã¹ãããªãæªãã£ãã§ããè¨äºã®æåæ°ã«ãããã¾ãããã ããã1åãããå¦çã«æéãè¦ãã¦ãã¾ãããã¾ãè² è·ã大ãããããå®è¡ä¸ã«ãµã¼ããè½ã¡ã¦ãµã¼ãã¹ãã¦ã³ããã¨ãã£ããã¨ãæ¸å¿µããã¦ãããããæç« æ ¡æ£æ©è½ããã¼ã¯ã¿ã¤ã ã«ã¯å®è¡ã§ãããæ大åæå®è¡æ°ã¯4ã¾ã§ã¨ããå¶éãè¨ãã¦ãã¾ããã
ãããã¡ãã£ã¢ã¨ããç¹æ§ä¸ãå°ãã§ãæ©ãã1æ¬ã§ãå¤ãè¨äºãå ¬éãã¦ã¦ã¼ã¶ã«æ å ±ãæä¾ãããã¨ãäºæ¥KPIçã«ãæ±ãããããããæç« æ ¡æ£ã使ããã©ã¤ã¿ã¼ããã®æ¥åå¹çãæªããªããå·çã»ç·¨éã¹ãã¼ããè½ã¡ãã¨ãã£ããã¨ã¯æã¾ãããªãç¶æ³ã§ããã
ããã§æç« æ ¡æ£æ©è½ã®é«éåãå³ãããã®æ½çã¨ãã¦ä¸ãã£ãã®ããGoã®é¢æ°ãShared Libraryã¨ãã¦ãã«ãããFFIã使ã£ã¦Railsããå®è¡ããã¨ãããã®ã§ããã
追è¨ï¼2018/07/02ï¼
æ³å以ä¸ã«å¤ãã®æ¹ã«èªãã§ããã ããã³ã¡ã³ãã¾ã§ããããããã ãã¾ãããå½è¨äºãèªãã§ä¸ãã£ãæ¹ãã³ã¡ã³ããã¦ä¸ãã£ãæ¹ããããã¨ããããã¾ãããä»å¾ã®å·çã®å±ã¿ã«ãªãã¾ãã
ããã¾ã§ã®åé¿ãå¼ã¶ã¨ã¯æã£ã¦ããªãã£ãããããã®è¨äºãæ°ããèªãæ¹ã«èª¤è§£ãä¸ãã¦ãã¾ããªãããã«è¿½è¨ãæ®ããã¦ããã ãã¾ãã
ä»åã¯å®è£ ãè¡ãä¸ã§ãã¤ã¯ããµã¼ãã¹åãå¥ãµã¼ãã¼ãç«ã¦ãã¨ãã£ããã¨ãå½åæ¤è¨ãã¦ãã¾ãããã大æãããªå®è£ ã«ãªãå¯è½æ§ããããæéçãªå¶ç´ãããä¸ã§å¯è½ãªéãã¹ã¢ã¼ã«ãªå®è£ ã§ããã©ã¼ãã³ã¹æ¹åãè¡ãããã¨å½åèãã¦ããã®ã§ããã£ã¦ã¿ã¦å¹æãæå¾ ã§ããããªãããã§é²ãããã¨è©¦é¨çãªåãçµã¿ããå§ããã¨ããçµç·¯ãããã¾ããçµæçã«ãããã¿ã¤ãä½æã®æ®µéã§æå¾ ä»¥ä¸ã®å¹æããã£ãã®ã§ãã®ã¾ã¾å®è£ ãé²ãããªãªã¼ã¹ãã¾ããã
ããããçããã®ã³ã¡ã³ãããçè ãRubyã¾ãã¯Railsã®ç¯å²å ã§ãã¢ã«ã´ãªãºã ã®æé©åçã§é«éåãå³ãããã¨ãçæ³ãªã®ã§ã¯ãªããã¨æãã¾ãããã¾ããRubyãGolangãã¢ã«ã´ãªãºã ã«å¯¾ããç解ãå°ãªãã£ãã®ãäºå®ã§ãã
ããããèæ¯ããããæ¬è¨äºã¯ããã¾ã§ä¸ã¤ã®äºä¾ã¨ãã¦æãã¦ããã ããã¨å¹¸ãã§ãã
ãã®è¨äºãå ¬éãã¦ã幸ãã«ãå¤ãæ¹ããã¨ã¦ãåå¼·ã«ãªãã¢ããã¤ã¹ãããã ããã¨ãåºæ¥ã¾ããã çæ§ããé ããã¢ããã¤ã¹ãå ã«ãä»å¾Rubyã»Golangã»ã¢ã«ã´ãªãºã ã®ç解ãæ·±ãã¦å®è£ ãè¦ç´ãã¨ããã¢ããã¼ããåã£ã¦ãããã°ã¨èãã¦ããã¾ãã
ã¢ããã¤ã¹ããã ãã£ãçãã¾ã«éãã¦æè¬ç³ãä¸ãã¾ãããããã¨ããããã¾ããã
åç½®ããé·ããªãã¾ãããã次é ããå®éã«ã©ã®ããã«å°å ¥ããã®ãã«ã¤ãã¦è§£èª¬ãã¾ãã
Railsç°å¢ã§ã©ã®ããã«Goã®é¢æ°ãå®è¡ããã
å ã«ãæããéããFFIã¨ããæ©è½ã使ã£ã¦å®ç¾ãã¦ãã¾ããFFIã¨ã¯ãForeign Function Interfaceã®ç¥ã§ãã詳ããã¯Wikipediaãã覧ãã ãããè¶ ãã£ããç°¡åã«ããã¨å¥ã®è¨èªã§å®è£ ãããé¢æ°ãå®è¡ããæ©è½ã§ãã
Rubyã«ã¯Ruby FFIã¨ããGemãããã®ã§ããã¡ãã使ç¨ãã¾ããããã®Gemã使ã£ã¦ãRubyããCã®é¢æ°ãå®è¡ãããã¨ãã§ãã¾ãã
ç°¡åãªãµã³ãã«ã³ã¼ãã§è§£èª¬ãã¾ãã
Rubyå´
ã¾ãffiãã¤ã³ã¹ãã¼ã«ãã¾ãã
$ gem install ffi
ããã¦ä»¥ä¸ã®ããã«ãã¦Cã®é¢æ°ãå®è¡ãã¾ãã
require 'ffi' module Sum extend FFI::Library ffi_lib 'sum.so' attach_function :sum, [:int, :int], :int end pp Sum.sum(1, 2)
ããã§sum.so
ã®sum
é¢æ°ãå®è¡ããããã«ãªãã¾ãã
Goå´
package main import "C" // export sum func sum(a int, b int) int { return a + b } func main() {}
mainã«ã¯ä½ãæ¸ãã¾ããã代ããã«FFIã§å®è¡ãããé¢æ°ã®ä¸ã«// export é¢æ°å
ã¨ã³ã¡ã³ããæ¸ãã¾ãã
ããã¦ãGoã®ã³ã¼ããCã®Shared Libraryã¨ãã¦ãã«ãããããã§ãããGoã®ã³ãã³ãã以ä¸ã®ããã«å®è¡ããã ãã§ãã
$ go build -buildmode=c-shared -o sum.so sum.go
å®è¡ããã¨sum.go
ãã³ã³ãã¤ã«ãã¦sum.so
ã¨ãããã¡ã¤ã«ããã«ãããã¾ãã
ããã§RubyããGoã®é¢æ°ãå®è¡ãããã¨ãã§ãã¾ãã
以ä¸åèè¨äºã§ãã
mamanokoã§ã®å®è£ ä¾
ã§ã¯ããããã¯å®éã«mamanokoã§ã©ãããå®è£ ããããããç´¹ä»ãã¾ãã
ã¾ãæ©è½çãªè¦æ±ããããããããã¨ã以ä¸ã®ããã«ãªãã¾ãã
1. æ ¡æ£å¯¾è±¡ã®æç« ãDBããæ½åº 2. ãã§ãã¯ããåèªã¨ãã§ãã¯ããé¤å¤ããåèªãDBããæ½åº 3. é¤å¤åèªãé¤ããæ ¡æ£å¯¾è±¡ã®æç« ã«ãã§ãã¯å¯¾è±¡ã®åèªãåå¨ãããç²¾æ» 4. ãã§ãã¯ã«å¼ã£ããã£ãç®æããã¤ã©ã¤ããã¦è¡¨ç¤º
ããã§é¤å¤ããåèªãåºã¦ããã®ã¯ãä¾ãã°ãæãã¨ãã表è¨ã¯ãã¨ããã«ããã¨ãã£ãå ´åã«ããæéããªã©ãæããå ¥ãåèªã§ãã§ãã¯å¯¾è±¡ããã¯é¤å¤ãããåèªãåå¨ããããã§ãã
ããã®3çªã«è©²å½ããå¦çãç¸å½éããããã«ããã¯ã«ãªã£ã¦ããã®ã§ããããGoã§ãªãã¬ã¼ã¹ããã®ãä»åã®ç®çã«ãªãã¾ãã
ãã¼ã¿ã¯Goå´ã§DBã«æ¥ç¶ãã¦åå¾ãã¦ãããã£ãã®ã§ãããORMãActiveRecordã¨ã®äºé管çã«ãªãã®ã¯é¿ãããã£ããããActiveRecordã§åå¾ãã¦ã·ãªã¢ã©ã¤ãºãã¦Goå´ã«æ¸¡ããã¨ã«ãã¾ããã ããã§ãGoå´ã§åãåãæã®åã«ã¤ãã¦1çªè©°ã¾ãã¾ããã
çµè«ããè¨ãã¨ãRailsããã¯stringã¨ãã¦æ¸¡ãã®ã§ãããGoå´ã§ã¯stringã§ã¯åãåããã¨ãã§ããã*C.char
åã§åãåãå¿
è¦ãããã¾ããã
ãã¾ã ã«ãªãstringã§åãåããªãã®ãããã£ã¦ãªãã®ã§ãããCã«ã³ã³ãã¤ã«ãã¦ãããããªãã§ããããã
jsonã®stringã渡ãããã§ãããGoå´ã§*C.char
ããstringã«ãã£ã¹ãããªããã°ãªãã¾ãããããã«ã¯ãC.GoString(articleJSON)
ã¨ãã¾ãã
ããã«ä»åº¦ã¯stringã«ãã£ã¹ãããJSONæååãstructæ§é ä½ã«ãããã³ã°ããå¿
è¦ãããã®ã§ãjson.Unmarshal([]byte(C.GoString(articleJSON)), &article)
ã¨ãªãã¾ãã
Cã®charåããGoã®stringã«ãã£ã¹ããã¦ãããã«ãã¤ãã³ã¼ãã«ãã£ã¹ããã¦ãæ§é ä½ã«ãããã³ã°ãããããããããã§ãããããæè¿æé»åå¤æã®ä¸çã«ãã£ã¨ããã®ã§ãæ示çã«ãã£ã¹ããããã®ãã¨ã¦ãé¢åã«æãã¾ããã»ã»ã»
ããã¦è²ã
ã¨ãã«ããã«ãããçµæãæçµçã«jsonã«ãã¦Railså´ã«æ»ãã¦ããã®ã§ãããããã§ãã¾ããã£ã¹ããå¿
è¦ã«ãªãã¾ããæ§é ä½ -> []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))
ããã§FFIã§Railsã»Goéã®å¤ã®åã渡ããã²ã¨ã¾ãã§ããããã«ãªãã¾ããã
ããã§ã¯ããã¾ã§ãã³ã¼ãã§ã¾ã¨ãã¾ãã
ã¾ãRailså´ã¯ãlibé
ä¸ã«lib/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
ããã¦Goå´ã§ããlibé
ä¸ã«src
ã¨bin
ãã£ã¬ã¯ããªãåããGOPATHãè¨å®ãã¦Railsã¨åãããã¸ã§ã¯ãå
ã§éçºãã¾ããããã«ããããã¤ããªãbin
é
ä¸ã«åãåºãããã«ãã¦ãã¾ãã
æ§é ä½ã¯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() {}
ããã§Railså´ããGoã«å¥½ããªãã¼ã¿ã渡ãã¦ããã¦ããã¨ã¯Goã®æ¹ã§ã´ãªã´ãªå¦çãå®è¡ãããã¨ãã§ããããã«ãªãã¾ããã
FFIã使ã£ãæã®ãã©ã¡ã¼ã¿ã®åã«ã¤ãã¦ã¯ã以ä¸ã®è³æãåèã«ãªãã¾ããã
www.slideshare.net
å®éã®ããã©ã¼ãã³ã¹ã¸ã®å½±é¿
Railsã®ã³ã¼ããGoã«ãªãã¬ã¤ã¹ãããã¨ã§ã©ãã ãããã©ã¼ãã³ã¹ãåä¸ããã®ãæ°ã«ãªããã¨æãã¾ãã
è¨äºã®æåæ°ã«ä¾åããããä¸æ¦ã«ã¯è¨ãã¾ããããã ããã以ä¸ã®éãã ã¨æãã¾ãã
Before 45~60sec After 1~3sec
è¨æ¸¬ã¯Chromeã®Devãã¼ã«ã®Networkã§ãµã¼ããªã¯ã¨ã¹ãã®ã¬ã¹ãã³ã¹ã¿ã¤ã ãææ¨ã«ãã¾ããã
ãã¡ããã¡ãéããªãã¾ããããµã¼ãã¸ã®è² è·ã軽æ¸ããããã¼ã¯ã¿ã¤ã ã«ã¯ä½¿ç¨ã§ããªãã£ããã®ã24æéãã¤ã§ã使ããããã«ãªãã¾ããã
ä»ã®æãªãªã¼ã¹ãã4ã¶æãããçµã¡ã¾ãããä¸åº¦ããµã¼ãã¹ãã¦ã³ãã¨ã©ã¼ãçºçãã¦ãã¾ãããæ»ã£ã¦ããã©ã¤ã¿ã¼ãããã¡ã®æ ¡æ£ä½æ¥ãæãããã«ãªã£ããããããã¡ããã¡ãæè¬ããã¾ããããã¯ãã¨ã³ã¸ãã¢ã¨ãã¦ã¦ã¼ã¶ããæè¬ããããã¦ã¼ã¶ã«åãã§ãããã¨ããã®ãä¸çªå¬ãããã¢ããã¼ã·ã§ã³ã«ãªãã¾ããã
Mamanoko Tips
æå¾ã«æç« æ ¡æ£æ©è½ã®å®è£ ã®ä¸é¨ããç´¹ä»ãããã¨æãã¾ãã
æç« ã«å¯¾ãã¦åèªã®ãã§ãã¯ãããå¦çããã¾ãè¦ã¦ããã¾ãã
æç« ã®stringãã該å½ããåèªã®indexã®sliceãã¨ãã°è¯ãããã¨èãã¾ããããstringããã±ã¼ã¸ã«ãããããé¢æ°ãè¦å½ãããªãã£ãã®ã§ãregexããã±ã¼ã¸ã®FindAllStringIndex
ã使ããã¨ã«ãã¾ããããããããã¤ãæ²è
ã§ãstringã®indexã§ã¯ãªããæåã®ãã¤ãã®éå§ä½ç½®ã¨çµäºä½ç½®ã®sliceã®sliceãè¿ãã¾ããã¤ã¾ãè¿ãå¤ã¯[][]byte
ã¨ãªã£ã¦ãã¾ãã
æ¬å½ã¯regexã¯ããã©ã¼ãã³ã¹çã«ãã¾ã使ããªãæ¹ãè¯ããããã®ã§é¿ãããã£ãã®ã§ãããè´ãæ¹ãªããã®ããã«ãã¾ãããä»ã«è¯ãæ¹æ³ãããã°ãæ示ããã ãããã§ãã
ã³ã¼ãã«ããã¨ããã§ããdictionary.Word
ãæ ¡æ£å¯¾è±¡åèªã§ãtext
ãæç« ã§ãã
dictionaryWord := regexp.MustCompile(regexp.QuoteMeta(dictionary.Word))
highlights := dictionaryWord.FindAllStringIndex(text, -1)
ããã«ãããã§æ½åºããããã®ã®ä¸ãããé¤å¤åèªã«è©²å½ããåèªãé¤ããªããã°ãªãã¾ããã
åãè¦é ã§é¤å¤åèªã®indexãã¨ãã¾ããããã¦ãhighlights
ããindexããã¶ããã®ãé¤å¤ãã¾ãã
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[:], "")
text
ã¯stringã§ãããstringã«å¯¾ãã¦for range
ã§ã«ã¼ããåãã¨ã1æåãã¨ã®ãã¤ãã®éå§ä½ç½®ã¨runeæåãåå¾ã§ãã¾ãã
ãããå©ç¨ãã¦ãå ã»ã©åå¾ããæ ¡æ£å¯¾è±¡åèªã®ãã¤ãä½ç½®ãæ ¼ç´ãããsliceãæ¯è¼ãã¦ãã¤ã©ã¤ã表è¨ãããã©ãããå¤å¥ãã¾ãã
runeæåã¯[]rune{textRune}
ã¨ãããã¨ã§å
ã®æååã«å¤æã§ãã¾ãã
ããã¦åºæ¥ä¸ãã£ã[]string
ãstringããã±ã¼ã¸ã®Join
ã使ã£ã¦ç¹ãã¦ä¸ã¤ã®æååã«ãã¦ãããã°å®æã§ãã
ææ
å®ã¯ä»åGoè¨èªã触ãã®ã¯åãã¦ã§ã1é±éã®ã¤ã³ãããæéãè¨ãã¦ããæ¬æ©è½ã®å®è£ ã«åãæããã¾ããã
Goè¨èªã¯è¨èªä»æ§ãããªãã·ã³ãã«ã§ãå®è£ ãã¦ãã¦ã©ã®é¢æ°ã使ãã¹ããè¿·ããã¨ããªãã£ãããæ¨æºããã±ã¼ã¸ãããªãå¼·åã§å¤§ä½ããã ãã§ãªãã¨ããªãã¨ããã®ãã¨ã¦ãè¯ãã¨æãã¾ããããã©ã¼ããã¿ãããã®ã§ãã©ã¼ããããæ°ã«ãããã¨ãªãã¬ãªã¬ãªå®è£ ã§ããã®ã¯è¯ãéçºä½é¨ã«ã¤ãªããã¾ããã
ãã¤ã³ã¿ã®ã¨ãããèªåã«ã¯é£ãããã¾ã ç解ãããã¦ããªãã§ãããããã§ããããªãå ¥ããã¨ãã§ãã¦ãGoè¨èªå¥½ãã«ãªãã¾ããã
ãããRubyãRailsãªã©é«ç´å¤æ©è½ãªè¨èªãFWãã移ã£ã¦ããã¨ããããã®é¢æ°ãªããããã¨ãã£ãäºæ ã«ééãã¦èªåã§å®è£ ãããã¨ã«ãªãã¨ãã£ããã¨ãå¤ãã¦å¤§å¤ããã§ãã»ã»ã»
ã¨ãããä»åã®ç®çã§ãã£ãæç« æ ¡æ£æ©è½ã®é«éåã¯Goã§ãªãã¬ã¼ã¹ãããã¨ã§è¦äºéæãããã¨ãã§ãã¾ããããããã¾ããããã¾ã§ããã©ã¼ãã³ã¹åä¸ããã¨ã¯èªåã§ãé©ãã§ãã
We're Hiring!!
å¼ç¤¾ã§ã¯Web / ãã¤ãã£ãã¨ã³ã¸ãã¢ãåéãã¦ããã¾ãã
ãèå³ãããã¾ããããæ°è»½ã«ãé£çµ¡ãã ããï¼