可読性と速度の両立の為のテンプレート
いつものごとく大げさなタイトルだが(ry
仕事でコードレビューをやっていたら以下のようなコードを見つけた。
void Module::InitLabelImage() { for( int z = 0; z < iSizeZ; ++z ){ for( int y = 0; y < iSizeY; ++y ){ for( int x = 0; x < iSizeX; ++x ){ if( m_bBit ){ if( (m_aaaMask[z][y][x] & mask) != 0 ){ m_aaaLabel[z][y][x] = flag; } }else{ if( m_aaaMask[z][y][x] == mask ){ m_aaaLabel[z][y][x] = flag; } } } } } }
上記のコードの問題点はどこか?まあいろいろあるとは思うが一番大きいのは多重ループ内での条件分岐だろう。これだとループの回数だけ条件分岐が行われ、速度に影響が出る。
void Module::InitLabelImage() { if( m_bBit ){ for( int z = 0; z < iSizeZ; ++z ){ for( int y = 0; y < iSizeY; ++y ){ for( int x = 0; x < iSizeX; ++x ){ if( (m_aaaMask[z][y][x] & mask) != 0 ){ m_aaaLabel[z][y][x] = flag; } } } } }else{ for( int z = 0; z < iSizeZ; ++z ){ for( int y = 0; y < iSizeY; ++y ){ for( int x = 0; x < iSizeX; ++x ){ if( m_aaaMask[z][y][x] == mask ){ m_aaaLabel[z][y][x] = flag; } } } } } }
上記のように書き換えれば条件分岐がループの外にあるので1回しか呼ばれない。
しかし、条件分岐の中身は殆ど同じなので保守や可読性の観点では前者の方がよいだろう。今回の場合は単純なのでそれほどでもないがループの中身が大きかったり、また条件分岐が2つでなくたくさんあった場合は酷いことになるだろう。
というわけで、前者のような可読性を維持したまま速度を出す方法はないか?と考えると条件分岐をテンプレートにしてしまえばいいという結論に至った。
template< bool BIT > inline bool check_mask( const byte&, const byte& ); template<> inline bool check_mask< true >( const byte& m1, const byte& m2){ (m1 & m2) != 0; } template<> inline bool check_mask< false >( const byte& m1, const byte& m2){ m1 == m2; } //↑比較関数の定義をしておく。 template< bool BIT > void Module::InitLabelImage() { for( int z = 0; z < iSizeZ; ++z ){ for( int y = 0; y < iSizeY; ++y ){ for( int x = 0; x < iSizeX; ++x ){ if( check_mask< BIT >( m_aaaMask[z][y][x], mask ) ){ m_aaaLabel[z][y][x] = flag; } } } } } //↑メソッド自体をテンプレートにしてしまう。 if( m_bBit ){ InitLabelImage< true >(); }else{ InitLabelImage< false >(); } //↑実際に使う場所で条件分岐する。
check_maskがインライン展開されなかったら逆に遅くなるがまあそんな酷いコンパイラは存在しないと思われるので上記のコードだと、書き方は1番目のコードだがコンパイルすると2番目のコードが生成される。比較関数の位置と実際に使われる位置が離れてしまうのが若干可読性を落としてしまっているが、やはりループの中身が大きかったり条件分岐が複数あった場合はこの書き方の効果が発揮されるだろう・・・と信じております(弱