* ES-BASIC #9 -(by [[K]], 2019.10.10) ** (11-1) ベンチマーク#2 -[[esbasic0007]]でのベンチマークはなかなか現実的で、我ながらうまいと思ったのですが、一点だけ気に入らないことがあります。 -それは「もし私がこのプログラムを高速化しようと思ったら、真っ先に画面左下の三角のゾーンとか、そのそばのやや大きい黒い円の部分(1.5個くらいある)については、まじめに演算せずにn=447を返すようにする」ということです。 --http://k.osask.jp/files/esb20190827b.png --http://k.osask.jp/files/esb20191016a.png -そんな「簡単にできる高速化」すらやらずに、「[[esbasic0007]]がベンチマークとしていい基準だ」とか言っていたら、うぬぼれでしかないのではないかと。 -ということで、この高速化を導入してみました。 -高速化したら2倍くらい速くなってしまってベンチマークとしてつまらなくなってしまったので、処理内容を16倍にして、結果的に[[esbasic0007]]と比較して8倍くらいの負荷のベンチマークになっています。 --一応理屈の上では演算制度が16倍に高まったことになっているのですが、画像にすると正直ほとんど差はありません。そういう意味では無駄な計算をやっているとも言えます。 --しかしこの処理内容の増強を行わないと、値が小さくて速すぎるCPUではスコアの精度が落ちてしまうので、やむを得ないということで勘弁してください。 --しかしこの処理内容の増強を行わないと、速すぎるCPUではスコアの精度が落ちてしまうので、やむを得ないということで勘弁してください。 ** (11-2) C言語版(32bit向け) -もちろんC++もこれで行けます。 #include <stdio.h> #include <time.h> #define MAX 447 #define STEP (14.0 / 0x100000) #define DN 16 int main() { int x, y, n, sx, sy, sn, h = 0, t; double zx, zy, cx, cy, xx, yy; for (y = 0; y < 384; y++) { for (x = 0; x < 512; x++) { if (y >= 137) { n = 447; if (x * 154 + 57600 < (y << 8)) goto n447; t = x - 245; xx = t * t; t = y - 314; yy = t * t; if (xx + yy < 2400) goto n447; t = x - 3; xx = t * t; t = y - 178; yy = t * t; if (xx + yy < 1700) goto n447; } 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++) { xx = zx * zx; yy = zy * zy; if (xx + yy > 4) break; zy = zx * zy * 2 - cy; zx = xx - yy + cx; } sn += n; } } // n = sn / (DN * DN); n = sn >> 8; n447: h += n; // 表示の代わりに集計 } } printf("%d\n", h); printf("(%.3f[sec])\n", clock() / (double) CLOCKS_PER_SEC); return 0; } -これで計算すると h=28654534 になります。 -if (y >= 137) { ... } を書かずに計算させると演算時間が2倍くらいになります。それでももちろん h=28654534 になります。 ** (11-4) C言語版(64bit向け) -もちろんC++もこれで行けます。 #include <stdio.h> #include <time.h> #define MAX 447 #define DN 16 int main() { long long x, y, sx, sy, zx, zy, cx, cy, xx, yy, t; int n, sn, h = 0; for (y = 0; y < 384; y++) { for (x = 0; x < 512; x++) { if (y >= 137) { n = 447; if (x * 154 + 57600 < (y << 8)) goto n447; t = x - 245; xx = t * t; t = y - 314; yy = t * t; if (xx + yy < 2400) goto n447; t = x - 3; xx = t * t; t = y - 178; yy = t * t; if (xx + yy < 1700) goto n447; } sn = 0; for (sx = 0; sx < DN; sx++) { cx = (x * 16 + sx) * 14 + 4673536; for (sy = 0; sy < DN; sy++) { cy = (y * 16 + sy) * 14 + 124928; zx = zy = 0; for (n = 0; n < MAX; n++) { xx = (zx * zx) >> 24; yy = (zy * zy) >> 24; if (xx + yy > 0x4000000) break; zy = (zx * zy) >> 23; zx = xx - yy + cx; zy = zy - cy; } sn += n; } } // n = sn / (DN * DN); n = sn >> 8; n447: h += n; // 表示の代わりに集計 } } printf("%d\n", h); printf("(%.3f[sec])\n", clock() / (double) CLOCKS_PER_SEC); return 0; } -結果としては当然のことながら(11-3)と同じく28656170 が表示されます。 -32bitのC/C++では、いろいろ工夫してもdoubleを使った場合よりも速くすることはできませんでした(インラインアセンブラでIMUL+SHRDを使えば速くできますが)。 * こめんと欄 #comment