ruby-trunk-changes r56704 - r56762

今日は Rational の実装の最適化がありました。Rational の演算子などの実装は内部で ruby のメソッド呼び出しを大量に使っていて遅かったので、直接 C の実装を呼ぶようにしています。

mrkn: r56704 2016-11-11 23:38:59 +0900

Rational#+ メソッドの実装で引数が Float の時に to_f と + メソッドを呼んでいたのを nurat_to_f() と RFLOAT_VALUE() で double に変換して加算してから DBL2NUM() でオブジェクトに変換するように最適化しています。

mrkn: r56705 2016-11-11 23:39:07 +0900

Rational#+ メソッドの実装で引数が Integer の時もメソッド呼び出しを避けて num (分子)に other * den (分母)だけ加算した Rational を生成するように関数呼び出しで処理するように最適化しています。

mrkn: r56706 2016-11-11 23:39:16 +0900

rational.c の f_gcd_normal() で f_negate() や f_negative_p() を使っていたところをメソッド呼び出しせずに処理するマクロを導入して最適化しています。

mrkn: r56707 2016-11-11 23:39:25 +0900

FIXNUM_NEGATIVE_P() と FIXNUM_ZERO_P() というマクロ定義を numeric.c から internal.h に移動して、rational.c でもこれを利用するようにしています。

mrkn: r56708 2016-11-11 23:39:34 +0900

NDEBUG というマクロが未定義の時のみ使われている f_mod() の定義を #ifndef NDEBUG でくくって未使用の警告を抑制するようにしています。

mrkn: r56709 2016-11-11 23:42:53 +0900

rational.c の f_addsub() でも f_xxx() 系のメソッド呼び出しを含む関数のかわりに rb_int_xxx() を直接呼び出すようにする最適化を行なっています。

mrkn: r56710 2016-11-11 23:45:11 +0900

Rational#- メソッドの実装で f_addsub() を呼んでいたのを r56705 と同様に den 倍した数を num から引いた値を分子として Rational を生成するように最適化しています。

mrkn: r56711 2016-11-11 23:57:11 +0900

f_addsub() で k が "-" の時(減算する時)も f_sub() のかわりに rb_int_minus() を利用するように最適化しています。また Rational#- で引数が Float の時も r56704 と同様の最適化を施しています。

mrkn: r56712 2016-11-12 00:11:04 +0900

rational.c の f_muldiv() および Rational#* で引数が Float の時の実装でも f_xxx() 系の関数のかわりにメソッド呼び出しをしない方法を使うように最適化しています。

svn: r56713 2016-11-12 00:11:05 +0900

version.h の日付更新。

nagachika: r56716 2016-11-12 00:43:43 +0900

tool/redmine-backporter.rb で backport サブコマンドの処理で例外クラスの名前を typo していたのを修正しています。

mrkn: r56719 2016-11-12 00:55:30 +0900

rational.c の rb_big_fdiv() から rb_big_fdiv_double() という名前で関数を切り出し、double で結果を受け付けるようにして、さらに big_fdiv() の戻り値を VALUE から double にしてわざわざオブジェクトを生成しないようにしています。

mrkn: r56723 2016-11-12 01:17:56 +0900

rational.c の f_muldiv() の符号の反転処理および Rational#* メソッドの実装で引数が Float の時の処理でメソッド呼び出しを含む処理を避けて直接処理する関数を呼び出すように最適化しています。

mrkn: r56724 2016-11-12 01:18:13 +0900

rational.c の f_muldiv() の先頭に引数のオブジェクトのタイプをチェックする assert() を追加しています。

mrkn: r56725 2016-11-12 01:38:28 +0900

次は Rational#** の最適化です。numeric.c の flo_pow() を rb_float_pow() と改名して static をはずして、rational.c でこれを利用して f_expt() のかわりに rb_int_pow() や rb_float_pow() を呼ぶようにしています。

mrkn: r56733 2016-11-12 10:29:01 +0900

Rational#<=> の比較演算子の実装でも f_xxx() 系のメソッド呼び出しを含む関数を含む実装から numeric.c の int_cmp() を rb_int_cmp() と改名して static を外して直接呼び出すようにして最適化しています。

mrkn: r56734 2016-11-12 11:24:32 +0900

Rational#== および Rational#=== の実装も numeric.c の int_equal() を rb_int_equal() として static を外して直接呼ぶようにして最適化しています。

mrkn: r56735 2016-11-12 15:07:01 +0900

Rational#-@ メソッドを追加しています。 Rational では単項演算子の - は実装されていなくて Numeric#-@ にまかせていたのを独自に実装するようにしています。 Numeric#-@ の実装は 0 から self を "-" メソッドを呼ぶことで減算するという実装なので結構遅いんですね。

mrkn: r56736 2016-11-12 15:07:24 +0900

rational.c の 残っていた f_cmp() の呼び出しも他のマクロに置き換えて定義自体を削除しています。

mrkn: r56737 2016-11-12 15:07:46 +0900

Rational() で Rational オブジェクトを生成する時の処理でも f_xxx() 系の関数を避けて直接関数を呼び出すようにする最適化を施しています。

nobu: r56738 2016-11-12 15:12:09 +0900

.gdbinit の rp コマンドで T_OBJECT 型のオブジェクトの表示でインスタンス変数の内容を表示するようにしています。

nobu: r56739 2016-11-12 15:12:12 +0900

singleton_class_of() で生成した singleton class の struct RBasic::flags に FROZEN フラグを立てるのに条件分岐をしていたのを消しています。

nobu: r56740 2016-11-12 15:12:13 +0900

pack.c の str_associate() で String オブジェクトにセットされている associated という内部的なインスタンス変数の値が Array だった時の分岐を消しています。 associate ってなんだっけなぁ…。

mrkn: r56741 2016-11-12 15:17:12 +0900

rational.c の nurat_coerce() で T_FLOAT 型のチェックに TB_TYPE_P() のかわりに RB_FLOAT_TYPE_P() を使うようにし、また f_to_f() のかわりに nurat_to_f() を直接呼ぶようにして最適化しています。

mrkn: r56742 2016-11-12 15:45:11 +0900

numeric.c の int_divmod() と int_and() を rb_int_divmod(), rb_int_and() に改名して static を取り、rational.c で Rational#float, #ceil, #round, #truncate の時にこれらを直接呼んで f_xxx() 系のメソッド呼び出しを含む関数のかわりに使うようにして最適化しています。

mrkn: r56743 2016-11-12 15:58:35 +0900

Rational#to_i の実装でも f_xxx() 系の関数のかわりに Integer を操作する関数を直接呼び出すようにして最適化しています。

mrkn: r56744 2016-11-12 16:28:47 +0900

Rational#fdiv() でも f_xxx() 系の関数のかわりに nurat_to_f() などを直接呼ぶようにしています。また不要になった f_to_f() の定義は削除しています。

mrkn: r56745 2016-11-12 16:32:28 +0900

不要になった id_truncate の初期化は削っています。また id_cmp も削っていますが、こっちは nurat_cmp() で動的に rb_intern() するようにしていて、こっちは最適化とは逆行する変更なので、なんででしょうね…。そういえば idCmp は id.h で定義済みのはず。

nobu: r56746 2016-11-12 17:16:58 +0900

test/-ext-/symbol/test_inadvertent_creation.rb で確実に動的に Symbol になっていない文字列を生成するために乱数や時刻を含む文字列を生成している処理を Test_Symbol::NonInterned という Module に切り出して使いまわすようにリファクタリングしています。

nobu: r56747 2016-11-12 18:43:05 +0900

fstring の singleton_class を得ようとしたら例外が発生するようにしています。厳密には Symbol になっていない文字列の fstring に対して singleton_method を呼ぶと abort していたようです。テストに r56746 で切り出した Test_Symbol::NonInterned を利用しています。 [ruby-dev:49867] [Bug #12923]

mrkn: r56748 2016-11-12 20:51:41 +0900

Numeric#quo で Integer だった時の Rational#/ を呼び出すのをメソッド呼び出しを用いていたのを nurat_div() を直接呼び出すように最適化しています。

mrkn: r56749 2016-11-12 20:51:51 +0900

Float#numerator と Float#denominator というメソッドの実装で float_to_r() で Rational 化して nurat_numerator() や nurat_denominator() を呼び出すように実装しなおしています。

mrkn: r56750 2016-11-12 20:52:00 +0900

Float#to_r の実装で f_to_r() を呼んでいたのを rb_rational_new{1,2}() を直接呼ぶようにしています。また numeric.c の int_lshift() を rb_int_lshift() に改名して static を取って f_lshift() のかわりに呼ぶようにしています。

mrkn: r56751 2016-11-12 20:52:09 +0900

rational.c で ZeroDivisionError 例外を発生させるためのマクロ rb_raise_zerodiv() を定義して使っていたのを rb_num_zerodiv() という関数が numeric.c に定義されていたのでそっちを使うようにしています。

mrkn: r56752 2016-11-12 21:14:10 +0900

Float#rationalize の実装でも f_xxx() 系のメソッド呼び出しを行う関数のかわりに直接実装の関数を呼び出すように最適化しています。

mrkn: r56753 2016-11-12 21:16:12 +0900

rational.c で不要になったマクロ id_lshift と f_lshift() の定義を削除しています。

nobu: r56754 2016-11-12 22:24:23 +0900

r56747 で追加した fstring に singleton_class を呼んだ時のテストで期待する例外クラスを TypeError に限定しています。 [ruby-dev:49867] [Bug #12923]

mrkn: r56755 2016-11-12 23:54:19 +0900

rational.c の f_expt(), f_fdiv() の定義が不要になったので削除して、ID を保持していた変数 id_expt, id_fdiv も削除しています。ここでも一部 rb_intern() を実行時に呼ぶようになっていますね。

mrkn: r56756 2016-11-13 00:07:53 +0900

Rational#negative?, Rational#positive? を定義しています。 Numeric のメソッドにまかせていたのですがこれだと 0 との ">" メソッドでの比較を rb_check_funcall() で呼び出す実装になっていたので効率的な実装にしています。

svn: r56757 2016-11-13 00:07:53 +0900

version.h の日付更新。

mrkn: r56758 2016-11-13 00:43:18 +0900

r56756 で追加した関数を利用して nurat_rationalize() から f_negative_p() および f_negate(), f_abs() の呼び出しを消して直接対応する関数を呼び出すように最適化しています。

mrkn: r56759 2016-11-13 00:43:26 +0900

Integer#lcm の実装のために numeric.c の int_abs() を rb_int_abs() に改名して static を外して rational.c の f_abs() の実装をメソッド呼び出しする形式から Integer なら rb_int_abs() を直接呼ぶ関数に定義しなおしています。

mrkn: r56760 2016-11-13 00:43:34 +0900

rational.c から f_negative_p() の定義を削除して、1箇所残っていた呼び元は rb_num_negative_p() を直接呼ぶようにしています。

mrkn: r56761 2016-11-13 01:29:11 +0900

RB_TYPE_P() のかわりに RB_INTEGER_TYPE_P() や RB_FLOAT_TYPE_P() を呼ぶようにしたり FIX2LONG() == 0 のかわりに FIXNUM_ZERO_P() を呼ぶようにするリファクタリング

mrkn: r56762 2016-11-13 03:42:30 +0900

complex.c でも r56762 と同様に RB_TYPE_P() のかわりにタイプ毎の RB_XXX_P() マクロを使うようにしたり、FIXNUM_ZERO_P() や FIXNUM_NEGATIVE_P() を使うようにするリファクタリングを行なって、不要になった k_fixnum_p() と k_bignum_p() の定義を削除しています。