[Из песочницы] Подключения 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 тут.

© Habrahabr.ru