研究日誌。

大規模なグラフ処理に対してメモリ階層構造を考慮した高性能なソフトウェアを開発。

循環リスト(circularly-linked list)。

2008-04-08 14:22:22 | Weblog
バケット法 (Dial's implementation) では、探索候補ノードの順番を最大枝長分の配列 bucket[max_length] と、両方向リストによって保持している。bucket[] は、循環リスト(circularly-linked list)であるので、次の要素を参照するときに、次のような演算を多用する。(bucket_size == max_length である)

i = (i + 1) % bucket_size;

バケット法では距離分だけ上記の演算をする(全米データ1対全では 4000万~5000万回ほど)ため、除算のコストがばかにならない。以下のように if 文に書き直すことで改善できる。

i++;
if (i >= bucket_size) i = 0;


また、bucket_size が2のべき乗であるならば、さらに改善できるようなので、bucket[] を切り上げて使用するというのも手のなのかもしれない。

アクセスパターン解析と並行して演算の再確認をしておこうと思う。

最新の画像もっと見る

6 コメント

コメント日が  古い順  |   新しい順
Unknown (後藤)
2008-04-08 23:13:06
整数除算が本当にボトルネックの一つならば大幅に性能を向上させるやり方があります。ただ、デメリットもあるのでまずはどれくらい時間を要しているかを調べてみてはいかが?
例のサイクルカウンタを使って計算時間全体に占める割合を算出するんですが、Intel 系列だと rdtsc のスループットが 64 サイクル程度と大きいので、Opteron を使うといいです(Penryn という手もあるがそれでもまだ大きい)。
あと、問題になるのが bucket_size の数値の大きさ。これが大きいと当然ながら遅くなります。^2 にするともちろん最高ですが、配列のアクセスに気をつけないとキャッシュコンフリクトが出ます。
もし、本当にこの部分がボトルネックになっているならば、Penryn 上での実行時間がかなり短くなっているはずです。Core2 との比から推定してみるのも良いです。
返信する
Unknown (u1low_cheap)
2008-04-09 00:39:48
コメントありがとうございます。

バケット法では枝長の分だけ、この循環リストのインクリメントを行うことになりますので、整数除算もボトルネックの一つになると思われます。

単純に実行時間の比較だけは次の記事で結果を載せておりますが、確かに penryn の方が高速化の割合が大きいです。

ちなみに bucket_size は全米では 368855 になります。このサイズは大きいと考えてよいのでしょうか。サイクルカウンタでも計測してみます。
返信する
Unknown (後藤)
2008-04-09 00:54:14
今ちょっと調べてみましたが、一割くらい Penryun の方が速いです。が、実際にはキャッシュの大きさも影響しているので推定が難しいかもしれません。

整数除余算の問題点は、プロファイルやパフォーマンスカウンタでは検出できないのと、計算自体にやたらと時間を要するということです。

Alpha アーキテクチャみたいに整数除算命令を持っていないシステム上でプロファイルを採ると一発で問題点がわかるんですけどね。Intel 系は便利な命令がたくさん用意してあるので、このような場合には逆に苦労することになります。
返信する
Unknown (u1low_cheap)
2008-04-09 01:11:12
確かにキャッシュの大きさによるものかもしれませんね。ただ、この部分が大きく改善されるのであれば、道路ネットワークではバケット法を用いやすくなると思います。(現段階でもかなり有効ですが、長い枝があるとそのせいで低速化してしまいますので。)
返信する
Unknown (後藤)
2008-04-09 06:20:23
それでは案を一応提示しておきます。
1.データのロードの合間に余算を行う。余算に要する時間はデータのロードとそれほど大きな違いがないため、オーバーラップさせる。欠点はせいぜい倍しか速くならない。
2.テーブルを使って商を求めてから、乗算と引き算を使って余算を求める。10倍くらい速くなるが除数が大きいとテーブルが大きくなってしまい、テーブルのキャッシュミスで性能が落ちる。
3.余算用のベクトル演算ルーチンを作る。余算の演算で互いに依存性がある場合には使えないが、性能は50倍くらい速くなる。キャッシュに対する悪影響も少ない。Itanium2 では1要素あたり2サイクル以下の効率で計算が可能だったはず。

というわけで、お勧めは3です。1は完全にアーキテクチャ依存で、組み込み系のチップでは性能は出ないです。2はキャッシュの大きさに依存するのであまり良くないです。
返信する
Unknown (u1low_cheap)
2008-04-09 13:11:31
ありがとうございます。3番の方向で進めていこうと思います。余算は、1要素ずつ bucket[] 内に探索候補 ID が入っているかチェックする際に用いることになりますので、ベクトル演算ルーチンはかなり有効だと思います。データの再利用性の期待があまりできないことを考えると、こういった部分に集中した方がよいようですね。
返信する

コメントを投稿