関数呼び出しのコストを見てみる

気になったので調べてみたついでに、メモしておく。

main.d

import std.string, std.range, std.algorithm, std.conv, std.array, std.stdio;

alias double[16] Matrix;

Matrix func( Matrix m ) { return m; }
Matrix func_rc( ref const Matrix m ) { return m; }
auto ref func_ar( Matrix m ) { return m; }
auto ref func_ar_rc( ref const Matrix m ) { return m; }
auto func_l = ( Matrix m ) => m;
auto func_l_rc = ( ref const Matrix m ) => m;
Matrix func_i( in Matrix m ) { return m; }
auto ref func_ar_i( in Matrix m ) { return m; }
auto func_l_i = ( in Matrix m ) => m;

void main( string[] args )
{
	Matrix m = [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0 ];
	
	foreach( i; 0..1000000 ) func(m);
	foreach( i; 0..1000000 ) func_rc(m);
	foreach( i; 0..1000000 ) func_ar(m);
	foreach( i; 0..1000000 ) func_ar_rc(m);
	foreach( i; 0..1000000 ) func_l(m);
	foreach( i; 0..1000000 ) func_l_rc(m);
	foreach( i; 0..1000000 ) func_i(m);
	foreach( i; 0..1000000 ) func_ar_i(m);
	foreach( i; 0..1000000 ) func_l_i(m);
	
	return;
}


なコードを -profile付けてコンパイル

dmd main.d -profile

そして実行して吐いた trace.logを見る。



以下結果。
一回目

======== Timer Is 2935112 Ticks/Sec, Times are in Microsecs ========

  Num          Tree        Func        Per
  Calls        Time        Time        Call

      1    12540677     7605232     7605232     _Dmain
11000000      690005      690005           0     pure nothrow @safe const(double[16]) main.__lambda3(const(double[16]))
11000000      687401      687401           0     pure nothrow @safe const(double[16]) main.__lambda2(ref const(double[16]))
11000000      673076      673076           0     double[16] main.func_i(const(double[16]))
11000000      670352      670352           0     const(double[16]) main.func_ar_i(const(double[16]))
11000000      644416      644416           0     pure nothrow @safe double[16] main.__lambda1(double[16])
11000000      569693      569693           0     double[16] main.func_rc(ref const(double[16]))
11000000      516156      516156           0     double[16] main.func(double[16])
11000000      439182      439182           0     ref const(double[16]) main.func_ar_rc(ref const(double[16]))
1000000       45160       45160           0     double[16] main.func_ar(double[16])


二回目

======== Timer Is 2935112 Ticks/Sec, Times are in Microsecs ========

  Num          Tree        Func        Per
  Calls        Time        Time        Call

      1    13766704     8394815     8394815     _Dmain
12000000      749979      749979           0     pure nothrow @safe const(double[16]) main.__lambda2(ref const(double[16]))
12000000      746487      746487           0     pure nothrow @safe const(double[16]) main.__lambda3(const(double[16]))
12000000      726993      726993           0     const(double[16]) main.func_ar_i(const(double[16]))
12000000      722372      722372           0     double[16] main.func_i(const(double[16]))
12000000      692746      692746           0     pure nothrow @safe double[16] main.__lambda1(double[16])
12000000      614376      614376           0     double[16] main.func_rc(ref const(double[16]))
12000000      561719      561719           0     double[16] main.func(double[16])
12000000      467464      467464           0     ref const(double[16]) main.func_ar_rc(ref const(double[16]))
2000000       89749       89749           0     double[16] main.func_ar(double[16])


三回目

======== Timer Is 2935112 Ticks/Sec, Times are in Microsecs ========

  Num          Tree        Func        Per
  Calls        Time        Time        Call

      1    14996076     9190335     9190335     _Dmain
13000000      810349      810349           0     pure nothrow @safe const(double[16]) main.__lambda3(const(double[16]))
13000000      801379      801379           0     pure nothrow @safe const(double[16]) main.__lambda2(ref const(double[16]))
13000000      781782      781782           0     const(double[16]) main.func_ar_i(const(double[16]))
13000000      771946      771946           0     double[16] main.func_i(const(double[16]))
13000000      740717      740717           0     pure nothrow @safe double[16] main.__lambda1(double[16])
13000000      660759      660759           0     double[16] main.func_rc(ref const(double[16]))
13000000      606062      606062           0     double[16] main.func(double[16])
13000000      497636      497636           0     ref const(double[16]) main.func_ar_rc(ref const(double[16]))
3000000      135108      135108           0     double[16] main.func_ar(double[16])

"auto ref 返り値"って場合によっては実行効率かなり高くなるのか…
そして個人的には "ref const 引数" が "修飾無し引数" より効率悪くなる事の方が驚き。
というか悲しい。
以前コード書いてた時に ref const 修飾子付けたら実行速度上がった事があったから
ずっとそれを盲信して来たのだが…うん…


測定方法がこれで合ってるか確信無いので、
こんな結果も出ましたよってくらいに読み流しておいて下さい。