MGL2は遅い?
実際 遅いという話を良く聞く。マシンの性能を出しきるというのが、 MGL の チューニングポリシーじゃないんで、ある程度遅いのはやむを得ない としても、遅いんであれば、コードをでかくしすぎない範囲で速くはしたい。
ここでは、MGL の設計上のおもわくを説明した上で、 MGL が どれだけ遅いか考察してみようかと思う。
モデル
a1) SCREEN_WIDTH 分を table 使って MGL の 色コードに変換する。 a2) put_pixstream() で SCREEN に書き込む。 ( 1-2 を SCREEN_HEIGHT 分やって(=1画面分が終ったら)、refresh() で 画面イメージを update する。) a3) (refresh) get_pixstream() で SCREEN のデータを取って来る。 a4) (refresh) MD_PUT_PIXSTREAMXX() で データを framebuffer に書き出す。
よく使われているのは、だいたいこんな感じだろうと思う。 で、いまは、16bpp なマシンが最も遅いから ターゲットは 16bppということ にする。
完全に最適化できたとして、
b1) 24bpp(32bpp) RGB から 16bpp への変換する b2) そのデータをフレームバッファにたたきこむ
ということになると思う。
そうすると余分な処理は、
おもわく
ざっと見積もって、b1 + b2 のコストと a3 + a4 のコストは同じぐらい ではなかろうか。
a4 で、色変換のためのテーブルを使っているんだけれども最大でも
4096 * 2 = 8K バイト
だし、色使いはランダムじゃないから、うまくキャッシュに載ってくれるはず。 そうすれば、メモリアクセスほどのコストにはならない。
で、a1 + a2 が余分であるのだが... フレームバッファへの書き込みは、メモリへの書き込みとくらべて 遅い。b1 + b2 より速くできることが期待できる。
というわけで、1/2 よりちょい速めの 60% が目論見値。
実際
実際はどうなのか。シグマリオンで性能を比較してみることにする。
まずはハードの性能。
fb16.h の
static char *fb16_base; static int fb16_rowbytes;の static を外し、ここに RGB 生データをたたき込む性能を測る。
ケース1: line 単位で memcpy 使って framebuffer に書く ( source は、同じデータ, dist は 1 画面 を scan) ケース2: 同じ条件で メモリ に書く 値: ケース1: 28.750 ms (per frame) , 5.343 Mdots/sec ケース2: 13.781 ms (per frame) , 11.146 Mdots/sec
ケース1 は、ソースデータはキャッシュに載り、framebuffer はキャッシュ OFF だから 1 メモリトランザクション しか発生しないと思う。
ところが、ケース2 では、cache-fill のために、いったんデータを読み込み モディファイするから、read-write で 2 メモリトランザクションになるはず。
16bppだから... ケース2 で使えたメモリ帯域は、44MB/sec ということになりそうだ。
それはともかく、framebuffer は、メモリと比べて半分の性能も出ないという 結果になった。たぶん妥当だろうなぁ。
ちなみに、この性能は 画面クリアの性能ということになるだろう。 イメージ転送とかでは、source がキャッシュにないからもっと遅い。
モデルでは、RGB データを変換しつつ... ということにする。
ケース3: 640x240 の 32bpp RGB (R8G8B8) データを配列に用意して、 16bpp RGB に変換しつつ framebuffer にたたき込む。 値: ケース3: 59.016 ms (per frame) , 2.603 Mdots/sec
メモリから読み出して色変換するのと、フレームバッファに書き込むのが 同じぐらいだから、意外に速いように思う。
コードは、
for (j=0; j<SCREEN_WIDTH; j++) { d1 = *ip++; d2 = ( (d1 >> 8) & 0xf800 ) | ( (d1 >> 4) & 0x07e0 ) | ( (d1 >> 3) & 0x001f ); *sp++ = d2; } memcpy(p,data_s,SCREEN_WIDTH*2);
こんな感じ。
ケース4: 今度は、MGL の色に一旦変換し、put_pixstream を使ってみる。 ただし refresh なし。 ケース5: refresh 付き 値: ケース4: 72.891 ms (per frame) , 2.107 Mdots/sec ケース5: 158.609 ms (per frame) , 0.968 Mdots/sec
うーん。理論性能の 2.603 Mdots/sec に対して 0.968 Mdots/sec (37%) しか出ていない。やっぱ遅いなオィ。
ぜんぜん 思惑と違うぞ。 ケース4でも b1+b2 を下回っている。ここでは、メモリに書くだけだから、 もっと速い予定だったのだのにぃ。
ちょっと put_pixstream をアンローリングしてみる。
結果は... ちょっとは速くなるんだけどなぁ。
値: ケース4: 68.602 ms (per frame) , 2.239 Mdots/sec ケース5: 148.969 ms (per frame) , 1.031 Mdots/sec
やっぱり遅いね。
結論
おもわくは外れた。MGL は遅い。
どれぐらい遅いかというと...
画面サイズが 320x240 だとして... 直接フレームバッファを叩けば 33.8 fps ぐらい出るのに対し MGL 使うと 13.4 fps ぐらいしか出ない。
もうちょいがんばるかなぁ。
付録
使ったプログラム http:arc/bench16.c
どうがんばろうか?'
native ドローエンジン作ってみようかと思って、シミュレーションしてみた。
ケース6: ドローエンジンだけ native 化したときのシミュレーション: bitmap に書くときに fb16_ctable[] を通す。 ケース7: さらに、frame buffer に直急く書く シミュレーション(shared_fb) 値: ケース6: 65.625 ms (per frame) , 2.341 Mdots/sec ケース7: 65.961 ms (per frame) , 2.329 Mdots/sec
ん? なんかうそっぽいぐらいの値が出てしまった。 ホントかなぁ。
# ちなみに、新しい bench16.c だと ケース3 の最高速の値が、1.954 Mdots/sec # になってしまう。たぶん、キャッシュのあたり具合が変わってしまった # ためだと思う。
それはともかく... これぐらいの値が出るなら、shared_fb でなくても 100 ms (= 320x240 で 20fps) ぐらいにはなりそうかなぁ。
じつは、16bpp native ドローエンジンってのは既にある。dec3kc っていうやつ。 なんで、使っていないかというと...
うーん。 MGL は遅いというステータスは悲しいし、がんばろうかなぁ。