1. ki073

    Posted

    ki073
Changes in title
+C++を使ってRubyのメソッドを書く(その2) ベンチマーク
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,130 @@
+###目次
+[C++を使ってRubyのメソッドを書く(その1)](https://qiita.com/ki073/items/b35e1e0ecc4862ea3c14)
+C++を使ってRubyのメソッドを書く(その2) ベンチマーク <- ここ
+
+###ベンチマーク
+配列の合計を計算する単純なプログラムです。上記のその1で書いた方法(1)と
+[Cを使ってRubyのメソッドを書く(その2)Numo::NArray](https://qiita.com/ki073/items/a9a06ed5f505e763893b) (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への変換も遅めのような気がします。
+
+###使用したプログラム
+
+```c++: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);
+}
+```
+
+```c++: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);
+```
+
+```ruby: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
+```
+
+ベンチマーク用プログラム
+
+```Ruby: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<double>| 2.44|
+|(2)|Numo::DFloat => double []| 0.14|
+|(3)|Numo::SFloat => cast -> double []| 1.59|
+|(4)|Numo::DFloat -> Array => vector<double>| 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|
+
+(実行時間はループ一回分に換算しています)