フィボナッチが30倍早くなった話


KS8695という古いARM SOCのBare Metalにmrubyをのせてfib(32)を計算させたところ、1155秒もかかりました。いくら古いといえ同時期のMIPS SOCのADM5120が114秒なので遅すぎです。

ARMはMMUを有効にしないとパフォーマンスが出ないという話を思い出しいろいろ調べてみました。

以下のコードが大変参考になりました。

上記のディレクトリにも長いREADMEが入っていますが、このコードを解説した人も見つけました。

KS8695はARMv4ですが、ARMv6まではそれほど違いがないようなので、そのままコードが利用できました。ARMv7以降はこれより複雑になっているようです。

Bare MetalでMMUを有効にするには物理アドレス=仮想アドレスにして固定のテーブルを作ります。

dwelch67さんのmmuのディレクトリのコードはmmuを有効にしているだけでキャッシュを有効にしてないので、extestディレクトリのコードや解説の方を参考にしてコードを取り込んでみました。

セクションと呼ばれる1Mバイト単位の処理で先頭の8つ(8M)のSDRAMをキャッシュ可能に設定してL1をスタートしました。他はFlashやIOになるので、キャッシュ無しの設定です。

このケースはmmuをスタートの前と後でアドレスが同じでなので、処理に影響はありません。FreeBSDのarmやmipsではkernelの物理アドレスと仮想アドレスが違いますが、絶対アドレスでのジャンプはないので、ちょっとしたテクニックで、仮想アドレスに移行できます。

        for(ra=0;;ra+=0x00100000)
        {
                if (ra < 0x800000)   /* SDRAM */
                        mmu_section(ra,ra,0x0000 | BUFFERABLE | CACHEABLE);
                else
                        mmu_section(ra,ra,0x0000);
                if(ra==0xFFF00000) break;
        }

        start_mmu(MMUTABLEBASE,0x00000001|0x1000|0x0004);

        start_l1cache();

これでfib(32)を計ったところ37秒まで短縮されました。

通常の処理はこれで良いのですが、Network機能などでDMAをおこなう場合は、キャッシュされると困ります。FreeBSDのドライバではキャッシュを物理メモリに強制的にはく処理と、キャッシュを捨てる(メモリから読み直し)の処理を入れています。Bare Meralの場合DMAするバッファのメモリだけはキャッシュ無しにしておいてそこを使うのが良いと思います。

私のコードはここに置いてあります。