LoginSignup
3
2

More than 3 years have passed since last update.

C++を使ってRubyのメソッドを書く(その2) ベンチマーク

Posted at

目次

C++を使ってRubyのメソッドを書く(その1)
C++を使ってRubyのメソッドを書く(その2) ベンチマーク <- ここ

ベンチマーク

配列の合計を計算する単純なプログラムです。上記のその1で書いた方法(1)と
Cを使ってRubyのメソッドを書く(その2)Numo::NArray (2)の速度を比較してみました。
他にも、 Numo::SFloat型のデータを渡した場合(3) (Numo::DFloatにキャスト後計算)、
NArrayをArrayに変換してから(1)と同じ方法(4)、
ArrayをNumo::DFloatに変換してから(2)と同じ方法(5)、
Rubyの組込み関数Array#sumを使う方法(6)、
Rubyの組込み関数Array#injectを使う方法(7)、
Numo::DFloatの関数sumを使う方法(8)
の速度を比較しています。

結果は最後に書いています。

その1の方法(1)でも十分な速度がありますが、(2)が最高速でした。(6)のArray#sumがかなり速く、バイナリで書かれたRubyの関数は速度で期待できそうです。Numo::DFloat#sumが意外と遅いのとArrayからNumo::DFloatへの変換も遅めのような気がします。

使用したプログラム

test.cpp
#include "test.hpp"

double sum(const std::vector<double>& ary){
    double sum=0.0;
    for (int i=0; i<ary.size(); i++){
        sum+=ary[i];
    }
    return(sum);
}

double sum_nd(int n, double *ary){
    double sum=0.0;
    for (int i=0; i<n; i++){
        sum+=ary[i];
    }
    return(sum);
}
test.hpp
#include <vector>
double sum(const std::vector<double>& ary);
double sum_nd(int n, double *ary);
test.i
%module testF
%{
#include "numo/narray.h"
#include "test.hpp"
%}

%include <std_vector.i>
%template(DoubleVector) std::vector<double>;
extern double sum(std::vector<double> ary);

%typemap(in) (int LENGTH, double *NARRAY_in){
  narray_t *nary;
  if (rb_obj_class($input)!=numo_cDFloat){
      $input = rb_funcall(numo_cDFloat, rb_intern("cast"), 1, $input);
  }
  GetNArray($input, nary);
  if (NA_TYPE(nary)==NARRAY_VIEW_T){
    $input = rb_funcall($input, rb_intern("dup"), 0);
    GetNArray($input, nary);
  }
  $2 = ($2_ltype)na_get_pointer_for_read($input);
  $1 = NA_SIZE(nary);
}
extern double sum_nd(int LENGTH, double *NARRAY_in);
extconf.rb
require 'mkmf'
dir_config("numo/narray")
have_header("numo/narray.h")
create_makefile("testF")
swig -c++ -ruby test.i
ruby extconf.rb  -- --with-numo/narray-include=/opt/lib/ruby/gems/2.6.0/gems/numo-narray-0.9.1.8/lib/numo/
make

ベンチマーク用プログラム

benchmark.rb
require "benchmark"
require "numo/narray"
require "./testF"

data=[*1..100].map(&:to_f)
data_na=Numo::DFloat.cast(data)
data_naf=Numo::SFloat.cast(data)

puts Benchmark::CAPTION
puts Benchmark.measure{
    1000000.times{
        TestF::sum(data) # (1)
    }
}
=begin
以下
TestF::sum_nd(data_na)   # (2)
TestF::sum_nd(data_naf)  # (3)
TestF::sum(data_na.to_a) # (4)
TestF::sum_nd(Numo::DFloat.cast(data)) # (5)
data.sum                 # (6)
data.inject(:+)          # (7)
data_na.sum              # (8)
=end

結果

データの流れ 実行時間 (μs)
(1) Array => vector 2.44
(2) Numo::DFloat => double [] 0.14
(3) Numo::SFloat => cast -> double [] 1.59
(4) Numo::DFloat -> Array => vector 4.77
(5) Array -> Numo::DFloat => double [] 10.11
(6) Array#sum 0.60
(7) Array#inject(:+) 3.42
(8) Numo::DFloat#sum 1.30

(実行時間はループ一回分に換算しています)

3
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2