Підключення 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 "С"

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

func main() {}


Це повинно містити main. Обов'язково вказати:
import "С"

І для функції які будуть доступні зовні треба вказати:
//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 тут.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.