Подключения Go shared library к Ruby

от автора

С выходом Go 1.5 появилась возможность делать go library для сторонних программ на других языках. То есть можно написать package который делает что то интересное и тяжело или просто уже готовое решения и подключить его в другую не Go программу. Это может быть C, android, objective C and etc. Я покажу как это легко можно подключить к Ruby.

1. Если у вас есть готовое решение проблемы на go, то не зачем его заново писать на Ruby;
2. Go работает явно быстрее Ruby если мы говорим про свою логику а не готовые решения с gem в которых часто работает C;
3. Go требует меньше памяти если вам надо работать с кучей данный.

Начнем с Go package.

Пишем наше быстрое хорошое рабочее решения на Go:

Код на Go

package main  import "C"  //export add func add(x, y int) int { 	c := 0 	for i := 0; i < 50000; i++ { 		c += x + y + 1 	} 	return c }  func main() {} 

Это должно содержать main. Обязательно указать:

import "C" 

И для функции которые будут доступны с наружи надо указать:

//export %имя функции% 

Теперь когда GO программа готова, надо сделать ей build:

go build -buildmode=c-shared -o libadd.so testruby.go 

-buildmode — это то, что появилось на Go 1.5, есть несколько разных вариантов, нам надо c-shared. После компиляции получаем .so и .h файл. Теперь это можно поключить в стороние не GO программы.

Теперь часть Ruby.

Нам нужен gem ffi. Ставим его через gem install или через gemfile+bundle install. Подключаем нашу библиотеку к Ruby:

Код на Ruby

require 'ffi'  module MegaSum   extend FFI::Library   ffi_lib 'lib/libadd.so'   attach_function :add, [:int, :int], :int end 

Тут мы указываем где лежит наш .so файл, какие у него есть функции вызовы (на которых мы написали "//export"), что они принимают и что возвращают(полный список типов можно посмотреть на тут). После этого можно работать:

Вызов Go

 def self.add_go(x,y)     Sum.add(x,y)   end 

Первый вызов будет немного медленный (наверно загружает все в память).

Benchmarks!

Код на Ruby который делает тоже самое

def self.add_ruby(x,y)     c = 0     i = 0     while i<50000       c += x + y + 1       i = i+1     end     c   end 

[21] pry(main)> Benchmark.realtime { (1..1000).to_a.each {GoHello.add_ruby(3,2) }} => 1.763254 [22] pry(main)> Benchmark.realtime { (1..1000).to_a.each {GoHello.add_go(3,2) }} => 0.030442 [23] pry(main)> Benchmark.realtime { (1..100000).to_a.each {GoHello.add_go(3,2) }} => 3.103797 [24] pry(main)> Benchmark.realtime { (1..100000).to_a.each {GoHello.add_ruby(3,2) }} => 195.282368 

Как видно что на арифметике простой Go обгоняет Ruby в 60 раз.

Минусы:
1. Не уверен, что можно в Go разводить кучу горутин. У меня это работало на маленькой проверки (не тысячи горутин);

П.C.: Есть похожее решение для Python тут.

ссылка на оригинал статьи http://habrahabr.ru/post/266037/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *