###目次
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への変換も遅めのような気がします。
###使用したプログラム
#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);
}
#include <vector>
double sum(const std::vector<double>& ary);
double sum_nd(int n, double *ary);
%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);
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
ベンチマーク用プログラム
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 |
(実行時間はループ一回分に換算しています)