Essen Rev2 (ess03f)

  • (by K, 2017.08.22)

これはなに?

  • セキュリティキャンプ中に開発したEssen Rev2の紹介。

mandel対決

  • 以下の絵をどのくらい高速に描画できるか?
  • http://khfdpl.osask.jp/download/20170822_mandel.png

対決

  • CPU: Core i7-2600 @ 3.40GHz win32 (32bitモード)
    処理系実行時間処理系のインストールサイズ備考
    gcc -O3 / (GCC) 3.4.5 (mingw special) [32bit]8.045秒(1.00倍)71926.7KB(1.0000倍)(比較基準)
    ess03f / [32bit]9.639秒(1.20倍)23.5KB(0.0003倍)
  • 論点:
    • gccは確かに速い。文句なく速い。生成しているアセンブラを見ていると本当にすごく努力している。
    • しかしess03fだってそんなに悪くない。そのgccに1.2倍のところまで迫っている。しかもたった23.5KBで。
    • gccのサイズについては、不要なものもたくさん含んでいるだろうから、実際に必要なのは数MB程度かもしれない。しかしそれでもess03fのサイズの小ささはかなりのものと言える。
    • ess03fはC言語をそのまま入力できるわけではないので、不公平感はある。しかし以下に示しているようにそれほどかけ離れているわけではない。
  • ess03fの内部情報:
    • JustInTimeコンパイラを内蔵したインタプリタ。
    • 動的型付き言語で、変数宣言などはいらない。
    • 現状ではグローバル変数しかサポートしてない。関数宣言すらできない。
    • 現状で使える型は、整数型と実数型しかない。配列も構造体もまだ使えない。if文もelse節をまだ実装していない。
    • 動的型付き言語では、型推論を行わないとスピードが出ないと言われているが、このess03fでもそれなりに型推論を行っている。もちろん23.5KBの中で!
    • ess03fは、プログラムの実行にあたってCコンパイラやアセンブラやリンカなどが必要になるということはない。23.5KBの中に必要なものはすべて入っている。ピュアなWindowsさえあればOK。
    • 演算は、整数演算についてはすべてEAXのみを使う形でコンパイル。実数演算については、ST(0)を使う形でコンパイル。はっきり言ってタコなコードである(というか23.5KB程度のJITコンパイラに多くを期待されても困る)。
    • そうやって生成したコードから不要な命令を削除する。たとえば以下のケースである。
      // i++
      MOV EAX,i
      INC EAX
      MOV i,EAX
      
      // if (i < 10) ...
      MOV EAX,i  // この命令は削除できる
      CMP EAX,10
      Jcc ...
    • 命令の並べ替えはしない。並べ替えを検討するとすごく難しそうだから。
    • この方針だとレジスタが余るので、プログラマはいくつかの変数を直接レジスタに割り当てることができる。それはgccとは異なり手動で行う。
    • それでもこれだけの速度は出る!・・・CPUがレジスタリネーミングなどで相当に頑張っているのだと思われる。・・・すごい!!
    • 結局何が言いたいのかというと、とりあえずこのサイズでもgcc -O3にここまで迫った速度が出せるということ。

自分でも実験したいという人のためのダウンロード

  • http://khfdpl.osask.jp/download/ess03f.zip
    • mandel.exe : gccで作ったマンデルブロ描画プログラム
    • ess03f.exe : EssenRev2の試作版
    • esmandel.txt : ess03f向けのプログラム
      prompt>ess03 esmandel.txt
      • で実行できる。
    • なお、ess03fのソースコードは今回は公開しません。

参考:ソースコード for gcc

#include "bla.h"
#include <stdio.h>
#include <time.h>

#define MAX 447
#define STEP (7.0 / 0x100000)
#define DN 4

int main()
{
    int x, y, n, sx, sy, sn, c;
    double zx, zy, cx, cy, xx, yy, tx, ty;
    bla_Window *w = bla_openWin(1024, 768, "mandel");
    for (y = 0; y < 768; y++) {
        for (x = 0; x < 1024; x++) {
            sn = 0;
            for (sx = 0; sx < DN; sx++) {
                cx = (double) 0x4750 / 0x10000 + (x * DN + sx) * (STEP / DN);
                for (sy = 0; sy < DN; sy++) {
                    cy = (double) -0x1e8 / 0x10000 - (y * DN + sy) * (STEP / DN);
                    zx = zy = 0;
                    for (n = 0; n < MAX; n++) {
                        yy = zy * zy;
                        xx = zx * zx;
                        tx = xx - yy;
                        ty = zx * zy;
                        ty *= 2.0;
                        zy = ty + cy;
                        zx = tx + cx;
                        xx += yy;
                        if (xx > 4.0) break;
                    }
                    sn += n;
                }
            }
            n = sn / (DN * DN);
            c = 0;
            if (n < 256)
                c = bla_rgb(n, 0, 0);
            else if (n < MAX)
                c = bla_rgb(255, n - 255, 0);
            bla_setPix(w, x, y, c);
        }
        bla_leapFlushAll(w, 300); // これを書かないと途中経過が画面に反映されない.
    }
    printf("time=%d\n", clock());
    bla_wait(-1); // ウィンドウが閉じられるまで待つ.
}

参考:ソースコード for ess03f

MAX :== 447
STEP :== 7.0 / 1048576.0
DN :== 4

zx :== $f00  zy :== $f01  xx :== $f02  yy :== $f03  tx :== $f04  ty :== $f05

openWin(1024 768)

y = 0  do {
    x = 0  do {
        sn = 0
        sx = 0  do {
            cx = 18256.0 / 65536.0 + double(x * DN + sx) * (STEP / double(DN))
            sy = 0  do {
                cy = -488.0 / 65536.0 - double(y * DN + sy) * (STEP / double(DN))
                zx = zy = 0.0
                n = 0  do {
                    yy = zy * zy
                    xx = zx * zx
                    tx = xx - yy
                    ty = zx * zy
                    ty *= 2.0
                    zy = ty + cy
                    zx = tx + cx
                    xx += yy
                    if (xx > 4.0) break
                n++  } while (n < MAX)
                sn += n
            sy++  } while (sy < DN)
        sx++  } while (sx < DN)
        n = sn / (DN * DN)
        c = 0
        if (n < 256) {
            c = rgb(n 0 0)
        }
        if (n >= 256) {
            if (n < MAX) {
                c = rgb(255 n - 255 0)
            }
        }
        setPix(x y c)
    x++  } while (x < 1024)
    leapFlushAll(300)
y++ } while (y < 768)
//leapflushAll(0)

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-08-22 (火) 19:06:10 (784d)