Pull to refresh

Ruby и C. Часть 1.

Reading time3 min
Views6.5K
Ruby очень легко интегрируется с языком программирования C. Можно создавать расширения для Ruby. Или мы можем сделать обертку для библиотеки на C, и использовать ее как обычную Ruby библиотеку. Так же можно реализовать критичные вычисления на C прямо в Ruby коде! Другой вариант интеграции — это использование Ruby в C/C++ программах, в качестве скриптового языка. Например, как это сделано в Google SketchUp.

Давайте посмотрим, какие возможности Ruby представляет для интеграции с C.


Пусть в нашей программе есть bottleneck, какой-то кусок вычислений, который, например, выполняется в цикле. Эти вычисления очень сильно понижают производительность приложения.
Решение — переписать критичный кусок на C, т.к. компилируемые языки заведомо быстрее интерпретируемых.

Тут у нас два варианта, либо создать расширение для Ruby, либо использовать код C прямо из Ruby.
Для начала выберем второй вариант, и в этом нам на помощь придет библиотека RubyInline.
Пусть у нас есть метод разложения числа на простые делители. Пойдем по самому простому пути, и реализуем этот метод алгоритмом перебора:
    class Factor
        #num - входное число, show - выводить ли результат на экран
        def factorization num, show=false 
            num1 = num
            n = 2 #начинаем поиск делителей с двойки
            while n*n <= num1
                if num%n == 0 # если текущее число - делитель, выводим его
                    num = num / n 
                    puts n if show
                else #иначе увеличиваем текущее число
                    n += 1
                end
            end
        end
    end

Но нас не устраивает скорость работы этого метода, по-этому перепишем его на C, с помощью библиотеки RubyInline.
Для начала установим ее:
gem install RubyInline
RubyInline требует POSIX систему, т.е либо *nix, либо Cygwin на Windows.
А также C/C++ компилятор.

Простой пример использования:
    require 'rubygems'
    require 'inline' #подключаем RubyInline

    class Test
        #обработчик встроенного кода
        inline do |builder| #в блок передается объект класса Inline::C
            builder.c '
                static char *test() {
                    return "Hi from C!:)";
                }
            '        
        end
    end
    
    puts Test.new.test # напечатает "Hi from C!:)"


В примере кода мы сделали небольшой C метод, возвращающий строку приветсвия.
Метод c выполняет следующие действия:
— выполняет конверсию типов аргументов и возвращаемого значения из C в Ruby
— компилирует строку с кодом метода
— добавляет классу Test метод test
После этого мы можем использовать test как обычный Ruby метод.

Вернемся к факторизации. Перепишем метод factorization с помощью RubyInline.
    require 'rubygems'
    require 'inline'
    
    class FastFactor
        inline do |builder|
            builder.c '
                static void factorization(int num, int show) {
                    int i = 0;
                    int num1 = num;
                    int n = 2;
                    while ( n*n <= num1 ) {
                        if ( num%n == 0 ) {
                            num = num / n;
                            if ( show )
                                printf( "%d\n", n );
                            i ++;
                        } else {
                            n ++;
                        }
                    }
                }
            '        
        end
    end


А теперь, самое интересное. Тесты производительности.
Тест Ruby реализации:
    f = Factor.new
    1000.times { f.factorization 999999}

Результат:
$time ruby factor.rb
    real	0m1.034s
    user	0m1.028s
    sys 	0m0.004s

И аналогично C:
    f = FastFactor.new
    1000.times { f.factorization 999999, false }

И результат:
time ruby factorfast.rb
    real	0m0.116s
    user	0m0.092s
    sys          0m0.024s


Как видно из теста, RubyInline позволяет ускорить критичные куски кода практически в 10 раз!
Код тестов: factor.rb, factorfast.rb.
Подводя итог, можно сказать, что если наше приложение не справляется с нагрузками, то мы всегда можем его ускорить, реализовав критичные моменты на C. Благо Ruby предоставляет нам мощные и удобные средства для этого.

В следующей части я расскажу про создание C расширений для Ruby.

Ссылки по теме:
RubyInline: http://rubyforge.org/projects/rubyinline/
Документация: http://rubyinline.rubyforge.org/RubyInline/
Tags:
Hubs:
Total votes 44: ↑44 and ↓0+44
Comments40

Articles