マンデルブロ描画プログラムVersion2.3

icon 項目のみ表示/展開表示の切り替え

概要

任意の領域に対して単精度または倍精度浮動小数点でマンデルブロを描画します。
計算にはFPU、SSE、SSE2、AVX命令を使うことができます。

画面上でマウスにより任意の範囲を拡大したり、中心点を移動することが可能。
色数が多い領域を自動的に連続拡大する機能があります。
主要計算ルーチンにインラインアセンブラを使用しFPU、SSE、SSE2、AVXを使用しています。
同時計算スレッド数を指定できますのでマルチコアやハイパースレッドを有効に活用できます。
ディスクトップサイズにとらわれず大きな画像を作成可能。8192*8192の画像が作成できました。
画像をクリップボードへコピーできる。
BMP形式で保存可能。
Version 2.1からの改良点は以下の通りです。

古いパソコンを使う機会があったのでWindows 95で使用できるようにVisual C++ 97(5.0)でコンパイルできるようにコードを修正しました。MMX Pentium(Windows 95)で実行できました。
SSEで計算するときに色変換時にSSE2のコードが使われていたので、SSEのみで計算できるように修正しました。
SSE2をサポートしていないCeleron(Coppermine-128k)でSSE2命令が含まれる実行ファイルを実行するとエラーが起こらないことがわかりました。エラーは起こりませんが、正常動作はしません。

テスト環境

コンパイラと実行可能なOS及びCPU

Visual C++ 2013 Express 32bit マルチバイト/UNICODE

Windows XP以上 CPUが対応していれば FPU SSE SSE2 AVXを使用可能

Visual C++ 2008

Windows 2000以上 CPUが対応していれば FPU SSE SSE2を使用可能

Visual C++ 2005

Windows 98以上 OS CPUが対応していればFPU SSEを使用可能

Visual C++ 97(5.0)

Windows 95以上 FPUのみ

動作確認環境

以下の環境で動作確認をしました。
HITACHI FLORA 270 PC-5NH02-YA5LA MMX Pentium 166MHz with Windows 95 Visual C++ 97で作成した32bit実行ファイル
HITACHI FLORA 270GX PC7NW1-GDE27H420Celeron 600MHz with Windows 98 SE Visual C++ 97で作成した32bit実行ファイル
HITACHI FLORA 270GX PC7NW1-GDE27H420Celeron 600MHz with Windows 2000 Visual C++ 2005で作成した32bit実行ファイル
HP ProBook5220m Core i3-M370 with Windows XP 32bit Visual C++ 2013で作成した32bit実行ファイル
自作PC Core i7-3820 with VirtualBox上のWindows Vista 32bit Visual C++ 2013で作成した32bit実行ファイル
自作PC Core i7-3820 with Windows 7 64bit Visual C++ 2013で作成した32bit/64bit実行ファイル
HP ProBook5220m Core i3-M370 with Windows 8.1 64bit Visual C++ 2013で作成した32bit実行ファイル
HP ProBook5220m Core i3-M370 with Windows 10 64bit Visual C++ 2013で作成した32bit実行ファイル

マンデルブロについて

b 虚数 amin,bmin a 実数 amax,bmax 0,0 x0 y0 画面 SVGの代替画像

Zn+1=Zn2+C
Z=x+yiC=a+bi
とした場合で
Z=Z^2+C
が成り立つ場合のx とy を求めます。
xn+1+yn+1i=(xn+yni)^2+a+bi
xn+1+yn+1i=xn^2+2 \times xn \times yni-yn^2+a+bi
実数: xn+1=xn^2-yn^2+a
虚数: yn+1=2 \times xn \times yn+b
この式を簡単に解けないためx0=0,y0=0 からスタートして近似していきます。
このときの近似回数をその点の色とします。ただし、Zの絶対値\sqrt{x^2+y^2} が2を超えた場合、発散して近似できないので打ち切りとし黒とします。また、近似回数の上限値を超えた場合、黒とします。
これらの計算を画面の横方向X座標をaとし縦方向をY座標をbとしてそれぞれの色を求めます。

原始的なプログラム

void draw(HDC hdc,double x0,double y0){
        double a_min=-0.7;
        double a_max=1.5;
        double b_min=-1.5;
        double b_max=1.5;
        double a_step=(a_max-a_min)/x0;
        double b_step=(b_max-b_min)/y0;
        int wx,wy;
        wx=0;
  for(doublea=a_min;a<=a_max;a+=a_step){
                wy=0;
    for(doubleb=b_min;b<=b_max;b+=b_step){
                        double x=0;
                        double y=0;
                        int c=0;
      for(c=0;c<=511;c++){
                                double x2=x*x;
                                double y2=y*y;
                                double zx=x2-y2-a;
                                double zy=2*x*y-b;
                                x=zx;
                                y=zy;
                                if(x2+y2>=4)
                                        break;
                        }
                        if(c==512){
                                c=0;
                                SetPixel(hdc,wx,wy,0);
                        }else{
                                SetPixel(hdc,wx,wy,color_map512(c));
                        }
                        ++wy;
                }
                ++wx;
        }
}

メニュー

ファイルメニュー

名前をつけて保存

画像をbmp形式でファイルに保存する。

終了

本プログラムを終了する。

編集メニュー

コピー

クリップボードへ画像をコピーする。

表示メニュー

前に戻る

直前の画像を表示する

次に進む

直後の画像を表示する

窓ズーム

表示画面上で拡大する領域を指定する。

移動

表示画面上でどこを中心にするか指定

ズームアウト

表示画面を0.5倍にする。

オプション

計算方法、画像サイズ等を指定するダイアログボックスを表示します。

左上座標・右下座標
計算するフラクタル座標の範囲を指定します。
画像サイズ
作成するフラクタル画像のサイズを指定します。
色数
計算の反復回数を指定します。
スレッド数
同時に起動するスレッド数を指定します。
FPU SSE C++ SSE SSE2 AVX
使用する拡張命令を指定します。
初期値
フラクタルの計算諸元を以下の値に設定します。
初期座標:-1.33333,-1.00000 1.33333,1.00000
画素数:800*600
反復数: 32768回

自動拡大


自動拡大は現在表示されている領域に対して色数が多い領域(16分割)を拡大し画像を保存するようにしました。拡大は、指定した回数分、自動的に行います。
自動拡大された画像を確認すれば、マンデルブロのお気に入りの領域を探すのが楽になります。
自動拡大された画像はカレントフォルダーにimage日時という名前のフォルダーの中のimageフォルダーに保存されます。
自動拡大した場合に作成された画像ファイルをエクスプローラーでサムネイル表示したものを以下に示します。

座標及び色数等と画像のサムネイルを表示するためのindex.htmlファイルが作成されます。
以下に自動拡大で作成される画像の一覧のリンクを示します。
index.html

各CPUでの実行速度

自動拡大の実行速度

初期座標:-1.33333,-1.00000 1.33333,1.00000
画素数:800*600
最大画像数: 100回
最大ループ: 32768回
名称 スレッド FPU 倍精度 C++ 倍精度 SSE 単精度 SSE2 倍精度 AVX 単精度 AVX 倍精度
Core(TM) i7-6700 CPU @ 3.40GHz(Skylake) 8 71.93 48.817 100.02 21.861 53.709 15.080
Celeron G3900(Skylake) 2.8GHz 2 191.876 163.942 309.073 80.622
Celeron 600MHz(Coppermine) 1 2274.3 2080.111 5011.356
Intel(R) Celeron(R) CPU 900 @ 2.20GHz(Penryn) 2.2GHz 1 479.748 575.689 838.364 364.448
Pentium(R) 4 CPU 3.20GHz 2 401.2970 387.2970 794.281 196.3910
Celeron(R) CPU G1620 @ 2.70GHz 2 189.293 190.451 292.904 79.895
Celeron(R) CPU G1840 @ 2.80GHz 2 191.840 168.955 296.796 76.060
Core(TM) i3-M370 @ 2.40GHz 4 140.0704 120.047 228.845 63.110
Core(TM) i7 CPU 860 @ 2.80GHz 8 84.307 52.680 98.395 31.711
Core(TM) i7 CPU 870 @ 2.93GHz 8 52.167 60.575 89.840 24.476
Core(TM) i7-3820 CPU @ 3.60GHz 8 37.181 37.183 76.258 18.307 30.880 10.562
Core(TM) i7-3770 CPU @ 3.40GHz 8 42.073 40.710 75.769 21.481 34.508 13.168
Atom Z3775 4 657.435 459.341 702.252 408.438
AMD A4-5300 3.4GHz 2 253.094 169.127 345.930 91.557 232.238 76.721
Core(TM) i7-4790 4.0GHz(Haswell Refresh) 8 71.967 38.985 72.821 20.654 41.028 16.053
Core(TM) i7-2600(Sandy Bridge) 3.8GHz 8 49.43 47.175 83.398 24.882 40.482 14.992
Core(TM) i5-4440(Haswell) 3.1GHz 4 97.121 86.136 142.052 39.895 65.242 23.359
単精度と倍精度では精度が異なるため計算結果に相違がでて、スタートが同じ座標でも拡大が進むにつれ違う領域を計算することになる。このケースでは、倍精度の時、計算負荷が少ない領域が選択されている。

各CPUでの実行速度

各CPUの計算時間(単位は秒)をシングルスレッドとCPUのコア数、サポートしている最大スレッド数それぞれ計測してみた。
各実行速度は、CPUへのスレッド割り付けは固定しているが、スレッドに*がついている場合は、CPUの割り付けは固定していない速度を表している。

Version 2.3での実行速度

名称 スレッド FPU 倍精度 C++ 倍精度 SSE 単精度 SSE2 倍精度 AVX 単精度 AVX 倍精度
i7-6700 CPU(Skylake) 3.40GHz 65W 1 18.346 15.673 4.001 7.992 2.141 4.188
i7-6700 CPU(Skylake) 3.40GHz 65W 8 4.985 3.895 1.094 2.032 0.563 2.126
Celeron G3900(Skylake) 2.8GHz 1 23.87 20.361 5.391 10.486
Celeron G3900(Skylake) 2.8GHz 2 12.407 10.673 2.703 5.797
MMX Pentium 166MHz(Tillamook) 1 871.88 2615.39
Pentium 2 450MHz(Deschutes) 1 213.835 414.276
Celeron 600MHz(Coppermine) 1 163.41 591.60 49.431
Celeron 667MHz(Coppermine) 1 157.8 571.17 未測定 未測定
Celeron 2.0GMHz(Northwood) 1 70.313 69.485 14.50 27.203
Atom D2701(Cedarview) 2.13GHz 10W 1 110.32 56.207 22.954 83.115
Atom D2701(Cedarview) 2.13GHz 10W 4 46.706 24.001 9.642 37.189
Pentium4 641(Cedar Mill) 3.2GHz 86W *1 50.381 48.912 11.830 19.83
Pentium4 641(Cedar Mill) 3.2GHz 86W *2 26.112 25.050 7.376 14.283
Celeron 430(Conroe-L) 1.8GHz 35W 1 40.06 48.329 9.922 23.728
Celeron 925(Penryn) 2.3GHz 35W 1 29.465 35.59 7.207 15.803
Intel(R) Celeron(R) CPU 900 @ 2.20GHz(Penryn) 2.2GHz 1 31.73 37.58 7.597 16.786
Core2Duo E7500(Wolfdale) 2.93GHz 65W *1 22.745 27.300 5.818 12.043
Core2Duo E7500(Wolfdale) 2.93GHz 65W *2 11.434 13.697 2.964 6.069
i3-370M(Arrandale) 2.4GHz 35W 1 26.125 26.094 6.250 12.578
i3-370M(Arrandale) 2.4GHz 35W 4 11.625 10.984 2.750 5.500
Celeron G1620 2.7GHz 55W *1 24.174 22.517 5.438 10.376
Celeron G1620 2.7GHz 55W *2 12.000 11.188 2.703 5.219
Celeron G1840 2.8GHz 53W 1 23.798 20.860 5.094 10.109
Celeron G1840 2.8GHz 53W 2 12.160 10.611 2.593 5.110
i7-860(Lynnfield) 2.8GHz 95W 1 19.737 19.451 4.716 9.508
i7-860(Lynnfield) 2.8GHz 95W 8 5.672 5.312 1.313 2.774
i7-2600(Sandy Bridge) 3.8GHz 95W 1 16.754 16.474 3.947 7.754 1.919 3.728
i7-2600(Sandy Bridge) 3.8GHz 95W 8 4.586 4.508 1.108 2.090 0.546 1.030
i7-3820(Sandy Bridge-E) 4.2GHz 130W 1 15.05 14.960 3.523 6.949 1.728 3.346
i7-3820(Sandy Bridge-E) 4.2GHz 130W 8 3.956 3.807 0.930 1.840 0.432 0.890
i7-4790(Haswell Refresh) 4.0GHz 84W 1 16.739 14.664 3.650 7.129 2.262 4.368
i7-4790(Haswell Refresh) 4.0GHz 84W 8 4.742 3.946 1.077 1.919 1.108 1.185
Core(TM) M-5Y71 1.2GHz 4.5W 1 23.344 15.579 4.359 8.188 2.344 4.562
Core(TM) M-5Y71 1.2GHz 4.5W 2 13.688 8.735 2.485 4.703 1.375 2.594
Core(TM) M-5Y71 1.2GHz 4.5W 4 11.578 7.485 2.547 4.078 1.171 2.343
AMD A4-5300 65W 1 26.286 19.203 5.569 10.826 3.026 6.068
AMD A4-5300 65W 2 16.411 10.374 3.026 5.679 2.652 5.975
Atom Z3775 1.46GHz(Bay Trail-T) 1 77.906 32.469 10.938 33.688
Atom Z3775 1.46GHz(Bay Trail-T) 4 32.860 13.735 4.594 14.234
i5-4440 3.1GHz(Haswell) 3.1GHz 84W 1 20.311 17.768 4.400 8.596 2.481 4.774
i5-4440 3.1GHz(Haswell) 3.1GHz 84W 4 8.580 7.504 1.840 3.635 1.045 2.012
i5-4440 3.1GHz(Haswell) 3.1GHz 84W 8 5.647 4.961 1.263 2.387 0.702 1.342

各CPUの概要

CPU クロック 開発コード コア数 HT L1 inst L1 data L2 L3 ラインサイズ メモリ帯域
MMX Pentium 166MHz Tillamook 1 無効 16kB(4way) 16kB(4way) 32 バス66MHz*8=533MB/s
Pentium 2 450MHz Deschutes 1 無効 16kB(4way) 16kB(4way) 512kB(4way) 32 メモリ100MHz*8=800MB/s
Celeron 600MHz Coppermine 1 無効 16kB(4way) 16kB(4way) 128kB(4way) 32 メモリ100MHz*8=800MB/s
Celeron 667MHz Coppermine 1 無効 16kB(4way) 16kB(4way) 128kB(4way) 32 メモリ100MHz*8=800MB/s
Celeron 2.0GHz Northwood 1 無効 12kuμops(8way) 8kB(4way) 128kB(2way) 64 FSB400MHz DDR266デュアル 4.3GB/s
Atom D2701 2.13GHz Cedarview 2 有効 32kB*2(8way) 24kB*2(6way) 512kB*2(8way) 64 DDR3-1066 17.1GB/s
Pentium4 641 3.2GHz Cedar Mill 1 有効 12kuμops(8way) 16kB(8way) 2MB(8way) 64 DDR2-533 6.4B/s
Celeron 430 1.8GHz Conroe-L 1 無効 32kB(8way) 32kB (8way) 512kB(2way) 64 DDR2-800 6.4GB/s
Celeron 925 2.3GHz Penryn 1 無効 32kB(8way) 32kB (8way) 1MB(4way) 64 DDR3-1333 10.7GB/s
Celeron 900 2.2GHz Penryn 1 無効 32kB(8way) 32kB (8way) 1MB(4way) 64 DDR3-1333 10.7GB/s
Core2Duo 2.93GHz Wolfdale 2 無効 32k*2(8way) 32k*2(8way) 3MB(12way) 64 FSB1066MHz DDR2-800 12GB/s
Core i3-370M 2.4GHz Arrandale 2 有効 32kB*2(4way) 32kB*2(8way) 256kB*2(8way) 3MB(12way) 64 DDR3-1066 17.1GB/s
Celeron G1620 2.7GHz Ivy Bridge 2 無効 32KB*2(8way) 32KB*2(8way) 256kB*2(8way) 2M(8way) 64 DDR3-1600 12.8GB/s
Celeron G1840 2.8GHz Haswell 2 無効 32KB*2(8way) 32KB*2(8way) 256kB*2(8way) 2M(8way) 64 DDR3-1333 21GB/s
Core i7-860 2.8GHz(3.46GHz) Lynnfield 4 有効 32k*4(8way) 32k*4(8way) 256k*4(8way) 8M(16way) 64 DDR3-1333 21GB/s
Core i7-2600 3.4GHz(3.8GHz) Sandy Bridge 4 有効 32k*4(8way) 32k*4(8way) 256k*4(8way) 8M(16way) 64 DDR3-1333 21GB/s
Core i7-3820 3.6GHz(4.2GHz) Sandy Bridge-E 4 有効 32KB*4(8way) 32KB*4(8way) 256kB*4(8way) 10M(20way) 64 DDR3-1600 51.2GB/s
Core i7-4790 3.6GHz(4.0GHz) Haswell Refresh 4 有効 32KB*4(8way) 32KB*4(8way) 256kB*4(8way) 8M(16way) 64 DDR3-1600 25.6GB/s
AMD A4-5300 3.4GHz(3.6GHz) Trinity 2 無効 64kB(2way) 16kB(4Way)*2 1024kB(16Way) 64 DDR3-1600 12.8GB/s
Core(TM) M-5Y71 1.2GHz(2.9GHz) Broadwell-Y 2 有効 32kB*2(8Way) 32kB*2(8Way) 256kB*2(8Way) 4MB(16Way) 64 LPDDR3-1600 25.6GB/s
Atom Z3775 1.46GHz Bay Trail-T 4 無効 32kB*4(8Way) 24kB*4(6Way) 1MB*2(16Way) 64 LPDDR3-1066 8.533GB/s
i5-4440 3.1GHz (3.3GHz) Haswell 4 無効 32KB*4(8way) 32KB*4(8way) 256kB*4(8Way) 6M(12way) 64 DDR3-1600 25.6GB/s
Celeron G3900 2.8GHz Skylake 2 無効 32kB*2(8way) 32kB*2(8way) 256kB*2(4way) 2MB(8way) 64 DD4-2133 デュアル34GB/s

変更経歴

SSE及びAVXの32768色の反復回数を色に割り当てるソースを修正 2017/09/18
A4-5300の実行速度を追加 2016/01/23

旧バージョン

マンデルブロ描画プログラムVersion2.1 2014/11/25
マンデルブロ描画プログラム 2014/09/27

プログラムソースの概要

mandel23.cpp

_tWinMain

GdiplusStartupによりGDI+を初期化します。
GetEncoderClsidによりjpeg用のエンコーダーを取得します。
ウィンドウを起動します。
GdiplusShutdownによりGDI+を開放します。

WndProc

ウィンドウプロシージャーです。
WM_CREATE
ウィンドウの初期化時に呼び出されます。
ステータスバーの作成をします。
画面のバッファであるメモリコンテキストデバイスの作成及び計算の初期値を設定し、初期値に基づくマンデルブローを描画します。
WM_SIZE
ウィンドウの大きさが変更されたとき呼び出されます。
ステータスバーの位置を変更します。
WM_MOUSEMOVE
マウスカーソルが移動されたときに呼び出されます。
移動中又は窓ズームに応じた処理を行います。
WM_LBUTTONDOWN
マウスの左ボタンが押された時に呼び出されます。
移動中又は窓ズームに応じた処理を行います。
WM_LBUTTONUP
マウスの左ボタンが離された時に呼び出されます。
移動中又は窓ズームに応じた処理を行います。
WM_COMMAND
IDM_ALLSET
マンデルブロの座標範囲、画像の大きさ、スレッド数、拡張命令を設定するダイアログボックスを呼び出します。
IDM_SAVE2
ファイルを選択するコモンダイアログボックスを表示しファイル名を取得します。
save_bmpを呼び出し画像をBMP形式で保存します。
IDM_EXIT
プログラムを終了させます。
IDM_BACK
前回の座標で表示します。
IDM_FORU
過去の表示から1つ現在に向かって表示を進めます。
IDM_ZOOM_OUT
ズームアウトします。
IDM_WIN_ZOOM
窓ズームを開始します。
IDM_WIN_MOVE
表示を移動させます。
IDM_AUTO
自動拡大する回数を入力するダイアログボックスを表示し、回数を取得します。
日時を取得し画像を保存するフォルダーを作成します。(CreateDirectory API)
画像のサムネイルを表示するindex.htmlファイルをオープンします。 msg_chk関数よりWindowsメッセージをチェックしWM_PATIN及びWM_KEYDOWNでVK_ESCAPEのメッセージを処理します。
ESCキーが押されている場合(VK_ESCAPE)は自動拡大を終了させます。
現在の座標でマンデルブローを描画します。
color_chkメンバ関数により現在表示されている座標を16分割しそれぞれの使用されている色数を取得します。
16分割のうち最大の色数の領域を取得します。
色数が4以上の場合は、画像をそのままbmpへ保存(save_bmp)、画像を縮小してjpegへ保存(SaveBitmap)、画像へのリンクを作成します。
現在の画像の領域ごとの色数等をMANDEL_PARSEクラスに保存し、MANDEL_TREEクラスに登録します。
色数が4未満又は計算に使用している浮動小数点の型から決まる座標の横幅より小さい場合は、分割された領域へ拡大することはできないと判断し、現在の領域の分割する前の領域に戻り、その領域の分割された領域で色数が最大でかつ描画されたことがない領域へ移動します。
色数が4以上の場合は、分割された領域で一番色数が大きい領域の座標を計算し、前述のmsg_chk関数へジャンプします。
WM_DESTORY
メモリデバイスコンテキストを開放し、ウィンドウを終了させます。
WM_CLOSE
ウィンドウ終了時に呼び出されます。
WM_PAINT
ウィンドウの表示を更新する必要がある場合呼び出されます。
マンデルブロの計算結果は各スレッド用のメモリからウィンドウのバッファ部にコピーされウィンドウに表示されます。
再計算の必要がない場合は、メモリデバイスコンテキスト(バッファ)からウィンドウへコピーします。

SetDlgPro

マンデルブロの座標範囲、画像の大きさ、スレッド数、拡張命令を設定します。

AutoSetDlgProc

フラクタル自動拡大する回数を取得するダイアログボックスを表示します。

mem_free

各スレッドに参照されるマンデルブロのパラメータを保存しているメモリを解放します。

save_bmp

bitmapをbmpファイルへ保存する

SaveBitmap

bitmapを拡大縮小してエンコーダーで指定されるフォーマットのファイルとして保存します。
保存には、GDI+を使用しており、あらかじめエンコーダーを取得しておく必要があります。

GetEncoderClsid

GDI+で使用するエンコーダーの取得します。
引数に与える文字列によって、jpeg,png等の種類を指定できます。

msg_chk

時間のかかる処理にメッセージ処理の機会を与えます。
メッセージをチェックし、WM_PAINTの場合は、メッセージを実行します。
WM_KEWYDOWNの場合はESCキーであるかチェックし、ESCキーの場合はtrueを返します。
呼び出し側は、trueの場合、自動拡大を中止します。

GetWinRect

ウィンドウのクライアント領域の大きさを返します。

CreateStatus

ステータスバーを作成します。

status_bar_str

マウスにより画像の領域を選択中にステータスバーに表示する文字列を作成する

mandel_draw_current

指定された座標及び画像サイズのマンデルブロを作成します。
この関数では指定数のスレッドを作成(mandel_draw_child関数)し、マルチスレッドでマンデルブロの計算をします。 座標の縦方向を指定したスレッド数で分割して同時計算させている。

mandel_draw_child

選択されている拡張命令(FPU,SSE,AVX等)によりマンデルブロを計算する関数を呼び出します。

simd.cpp

IsWindowsVersionOrGreater

OSがAVXに対応しているか確認するために指定バージョン以上かどうかを返します。

IsWindows7SP1OrGreater

OSがAVXに対応しているか確認するためにWindows 7 Service Pack 1バージョン以上かどうかを返します。

mandel_draw_fpu2

FPUは1スレッドあたり、1ピクセルごとに計算する。
FPUレジスタは8個あるが、スタック構造なので、直接レジスタの場所を指定できず、例えばスタックトップとスタックトップからn個目といった指定の仕方をする。
オペランドの一方がスタックトップにある必要があるので、都合が悪い時はfxch命令でスタックトップとスタックトップからn個目の値を交換する。
レジスタに値をロードするとスタックにプッシュされる。スタックがあふれるとエラーが発生するので、スタックトップを破棄する必要がある。この場合、計算後に自動的に破棄する命令(fcomp等)を使うか、fincstp、fstp st(0)等を用いる。
Windowsの64bit版ではFPUを使わないことを奨励している。
Intel自身も高速化のしにくいFPUよりSIMDを使用することを奨励している。

mandel_draw_sse・mandel_draw_sse2

SSEは1スレッドあたり、水平方向4ピクセルを単精度で同時に計算、SSE2は水平方向2ピクセルを倍精度で同時に計算する。
座標等の初期値はスカラー命令(1個の浮動小数点を扱う命令)でXMMレジスタに取り込みシャッフル命令でXMMレジスタの各要素(8個又は4個)にコピーする。
以降パックド命令(複数のデータを同時に扱う命令)を使い計算をする。
式が4以上になったときのカウント値を各ピクセルごとに抽出する必要がある。この値は各ピクセルごとに異なる。
4以上がどうかをcmpltps,cmpltpdで比較する。4以上の場合対応する要素のビットが全部0になる。未満の場合はビットが1となる。
同時に扱っているピクセルが全部4以上になった場合をチェックするために、movmskps又はmovmskpd命令で各要素の最上位ビットをdxレジスタに転送している。dxレジスタに対してtestを命令を実行すると、ゼロフラグがセットされているときは各要素全部が4以上なので、ループを抜けることができる。
要素のビットをandでマスクし、4以上の場合は整数の0、未満の場合は1となるようにし、カウンタをadd命令で加算する。こうすれば4未満のピクセルはカウントされ、4以上のピクセルはカウントされないので、同時に扱っているピクセルのループ回数が同一にできSSE又はSSE2命令で記述が可能となる。

mandel_draw_avx・mandel_draw_avxd

AVXは1スレッドあたり、水平方向8ピクセルを単精度で同時に計算又はは水平方向4ピクセルを倍精度で同時に計算する。
AVXは浮動小数点に限りスカラー値をYMMレジスタの各要素にコピーすることができる。vbroadcastss,vbroadcastsd命令(AVX2では整数でも可能) 以降パックド命令(複数のデータを同時に扱う命令)を使い計算をする。
式が4以上になったときのカウント値を各ピクセルごとに抽出する必要がある。この値は各ピクセルごとに異なる。
4以上がどうかをvpcmpltps,pcmpltpdで比較する。4以上の場合対応する要素のビットが全部0になる。未満の場合はビットが1となる。
同時に扱っているピクセルが全部4以上になった場合をチェックするために、vpest命令を使います。この命令は2つの256bit値のandをとり、その結果に基づきゼロフラグ、キャリーフラグをセットします。ゼロフラグがセットされているときは各要素全部が4以上なので、ループを抜けることができる。
比較結果に基づきvmaskmovps,vmaskmovpd命令により各要素ごとに1をロードするかしないかを制御している。これにより比較結果により1又は0が設定されるので、カウンタをadd命令で加算する。こうすれば4未満のピクセルはカウントされ、4以上のピクセルはカウントされないので、同時に扱っているピクセルのループ回数が同一にできAVX命令で記述が可能となる。
なおAVXには整数を扱う命令が少ないので、浮動小数点でカウントをし、カウント後、vcvtps2dq,vcvtpd2dqで32bit整数に変換する。
AVX命令には整数を扱う命令が少ないので、YMMレジスタの上位下位を別々にSEE命令によりRGBに変換している。AVX2を用いれば高速に処理できるが、該当するCPUを持っていないのでAVXで記述している。

isAVX

CPUがAVX命令をサポートしているかどうかを返します。

color_count

色数を返す

color_chk

画像の領域ごとの色数をカウントする

hi_chk

一番色数の多い領域を返す

cpu.cpp cpu.h

基本的には、CPUの物理CPU数・ソケット数等を取得(32/64bit)のCPUクラスにスレッドの割り付け関数(cpu.h)を追加しています。ここでは追加部分のみ記載しております。

CPU::alloc_cpu

指定したスレッド数に対する論理CPU番号の配列を返します。
スレッド数がコア数以下の場合は、それぞれ異なるコアにスレッドを割り当てます。
スレッド数がコア数以上の場合は、異なるコアに優先して割り当てコア数が最大に達したらCPUのセカンドスレッドに割り当てます。

CPU::getWinCpu

指定したコアのスレッド(CPU)にスレッド(プログラム)を割り当てます。パッケージが複数ある場合は、パッケージ間で連続したコア番号があるとみなしで指定します。

ソースコードと実行ファイル

実行ファイルとソースファイルのダウンロード(mandel23.zip 213kByte)
ZIPファイルに含まれるファイル

mandel23.cpp
mandel23.exe            Visual C++ 2013でビルド Windows XP以上で実行可能
mandel23_0097.exe       Visual C++ 97(5.0)でビルド Windows 95以上で実行可能
mandel23_2005.exe       Visual C++ 2005でビルド WIndows 98以上で実行可能
mandel.ico
mandel.rc
cpu.cpp
cpu.h
resource.h
simd.cpp
undervc2005.h