* 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]|RIGHT:8.045秒(1.00倍)|RIGHT:71926.7KB(1.0000倍)|(比較基準)| |ess03f / [32bit] |RIGHT:9.639秒(1.20倍)|RIGHT: 23.5KB(0.0003倍)|| -論点: --gccは確かに速い。文句なく速い。生成しているアセンブラを見ていると本当にすごく努力している。 --しかしess03fだってそんなに悪くない。そのgccに1.2倍のところまで迫っている。しかもたった23.5KBで。 --gccのサイズについては、不要なものもたくさん含んでいるだろうから、実際に必要なのは数MB程度かもしれない。しかしそれでもess03fのサイズの小ささはかなりのものと言える。 --ess03fはC言語をそのまま入力できるわけではないので、不公平感はある。しかし以下に示しているようにそれほどかけ離れているわけではない。 -ess03fの内部情報: --JustInTimeコンパイラを内蔵したインタプリタ。 --動的型付き言語で、変数宣言などはいらない。 --現状ではグローバル変数しかサポートしてない。関数宣言すらできない。 --現状で使える型は、整数型と実数型しかない。ifもelse節をまだ実装していない。 --現状で使える型は、整数型と実数型しかない。配列も構造体もまだ使えない。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) * こめんと欄 #comment