リンクフリーを近日中にとりやめる予定です

すでにリンクを貼っていただいている方、ご一報頂きたくお願い申し上げます。


ごく少数ですが、リンクをお断りする場合があります



ブログ内 風景光景カテゴリー

続編記事などをご希望の方は こちらへどうぞ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[未掲載分] 「除算が遅い」の補足 (5)

前話の終盤、拡張された機能を有効にするお手軽な方法を挙げた。Microsoft の Visual Studio でアプリを作成する際、



/arch:SSE2 オプションを指定することにより、ある程度アプリが高速化される。
西暦1999年頃リリースされたPentium III 以降、CPU に XMM レジスタが増設された。つまり、新機能が追加された。新機能を活かすことで高速化に繋がる。アプリをビルドする際、初期設定のままでは「新機能を積極的に利用する」にならない。

なぜ、/arch:SSE2 オプション/arch:AVXが選択制なのだろうか???
デフォルトで有効に設定されても良いような感じもするのだが・・・

※ 「デフォルト」は財政破綻、債務不履行等の意味で用いられることもあるが、ここでは「初期設定」や「標準の状態」を指しています。


お知らせ
活動休止にともない、この記事を事前に予約投稿してあります。
トップ記事の固定を目的としています


※ これを下書きしたのは2012年夏の終わり頃です。
ご覧いただいている時期によっては状況が異なっている可能性があります。
下書きした頃の環境は、OS - Microsoft Windows 7、開発環境 - Visual Studio 2008 , C/C++ 言語。

新機能を活かすことで高速化に繋がるのは良いとして、
・ハードウェアレベル、ソフトウェアレベルで対応しているかの確認
を見落としがちである。

旧世代のCPUにはその機能が備わっていない。また、将来的にその機能が廃止されるかもしれない。実例を挙げると、32ビット環境で永い間用いられてきた FPU ( 浮動小数点演算処理装置 ) や MMX が廃止の方向にあり、64ビット版の Windows ではサポート対象外となった件などなど・・・

SSE ( SSE2 から SSE4.2 ) や AVX 命令など後からを追加された機能を活用したアプリを作る場合、
PC にその機能が備わっているか、OS がサポートしているか等、
ハード・ソフトともに対応しているかのチェックを怠らないことが重要。具体的には、
・CPU にその機能が実装されている
・OS が公式に対応している
を確認する。それらの条件を満たしていないとアプリが不正終了したり、OS がフリーズや暴走する原因となる。

とはいえ、自分のマシンで自分のみ使うようなアプリならばこのような確認も不要・・・

アプリを作る側の環境は固定できるが、利用する側は様々である。作る側が「動作環境として SSE3 以降」や 「AVX 機能搭載機種」など細かく書いたとしても、利用する側がそれを理解できるとは限らない。
そこで、ハード、ソフトどちらかが対応していないならば、「要件を満たしていない」等のダイアログボックスを表示し、アプリを終了するなどの策を施すのが良い。利用する側に親切である。

具体的にはWinMain もしくは tWinMain 等アプリのメイン関数冒頭にハード、ソフトをチェックするコードを加える。
ハードウェアレベルでサポートされているか調べるには CPUID 命令 を使う。



戻り値のうち、特定のビットが立っているか否かを調べることでその機能を有しているか判別できる。詳細は
・IA-32 インテル アーキテクチャ ソフトウェア・デベロッパーズ・マニュアル
・インテル エクステンデッド・メモリ64 テクノロジ・ソフトウェア・デベロッパーズ・ガイド等に載っています。それら資料の入手に関しては過去記事車輪の再発明 (8)で触れました。

1993年頃登場した Pentium 以降の CPU ならば CPUID 命令が通用する。古めの Visual Studio ではCPUID 命令に相当する関数が付属していなかった。そのため、アセンブリ言語 ( インラインアセンブラ ) で CPUID 命令に相当する部分を書く必要があった。
Visual Studio 2005 以降であれば、 _cpuid 関数 などの組み込み関数が用意されている。

ハードウェア寄りな話が苦手なヒトもいることだろう。たしかに、CPUID 命令の発行やどのビットを調べれば良いかといった部分が面倒かもしれない。かつて、車輪の再発明 (10)でも触れた方法も使える。Windows に限られてしまうが、IsProcessorFeaturePresent 関数 を呼び出して、戻り値を調べる方法。



この関数は、OS が標準装備している機能を呼び出す。よって、Visual Studio のバージョンに左右されない。
※ 1996年頃に登場した Windows NT 4.0 以降で標準装備されている。

実行環境が SSE2 命令に対応しているかを調べるには PF_XMMI64_INSTRUCTIONS_AVAILABLE を指定する。
PF_△△△_AVAILABLE や PF_■■■_ENABLED といった引数 ( 関数を呼び出す時に指定する値 ) は Windows SDK のバージョンが上がる毎に新しい CPU に対応した引数が追加されてゆく。うまくコンパイルできない時はWinNT.h 等のヘッダーファイルを開くこき確認すると良い。



例えば、SSE3 命令に対応しているかを確認したいとしよう。IsProcessorFeaturePresent 関数 を呼び出す部分で PF_SSE3_INSTRUCTIONS_AVAILABLE を指定する。
Visual Studio 2005 をセットアップした段階で付属している Winbase.h や WinNt.h を開いてみると PF_SSE3_INSTRUCTIONS_AVAILABLE が含まれていない。

Visual Studio とは別に Windows SDK (Platform SDK や Win32 SDK とも呼ばれていた) が公開されている。新しいバージョンのWindows が登場するとそれに対応した SDK がリリースされる。確認したところ、西暦2008年にリリースされた Windows SDK 6.1 ( Windows SDK for Windows Server 2008 and .NET Framework 3.5 ) に含まれているヘッダーファイルから PF_SSE3_INSTRUCTIONS_AVAILABLE の定義が加っている。



ということで、Visual Studio 2005 に Windows SDK 6.1 ( もしくはそれより新しいバージョンのSDK ) を導入することでSSE3 判定用の引数、PF_SSE3_INSTRUCTIONS_AVAILABLE を使える。参考までに加えておくが、Visual Studio 2005 と Windows SDK 7.0 以降の SDK は相性が悪い部分がある・・・

つぎに重要なことはソフトウェアレベル、つまり OS がサポートしているか。細かく言えば、
OS がアプリの実行権を切り替える際、各レジスタの退避・復元を確実に行うか否か
がサポートされるか否かの境目である。SSE2 に絞って言えば、XMM レジスタの退避・復元が確実に行われれば良いことになる。

これに関しては、 _cpuid 関数IsProcessorFeaturePresent 関数のようなOK か NG かをお手軽に判別する関数が用意されていない。おそらく、実行時、どのバージョンの Windows で実行されているかを判断し、得られたバージョン情報にもとづいて続行か終了かを決めるようなコードを自力で書くことになる。

これを書いていた時点で、
OS のバージョンを取得するにはGetVersionEx 関数 VerifyVersionInfo 関数を用いるのが簡単である。これら関数を用いることで現在動作している Windows のバージョンに関する情報を得ることができる。得られたメジャー番号、マイナー番号やサービスパックの適用情報をもとに、動作対象か否かを判別できるハズ。
ほかにも、OS のバージョンを取得するにはGetFileVersionInfo 関数でシステム DLL のバージョンをチェックする手もあるのだが・・・
なお、これらの関数は 古くから ( Windows 95 や NT 3.5 の頃から ) のものであり、これをご覧いただいている時点では非推奨や廃止となっているかもしれません。その場合、代替の関数が提供されてることでしょう・・・

Windows のバージョンと対応できる拡張機能を判定する部分を自力で書く際には、同じバージョンの Windows であってもマイナーバージョンが違えばサポート外な件もあるので注意したい。
例えば、これを書いていた時点でPC に搭載されている CPU は Core i7 ( Ivy bridge )。Ivy bridge の ひと世代前の CPU ( Sandy bridge ) から Intel AVX 命令が実行できるように拡張された。
もともと、SSE4.2 の後継として SSE5 に注目が集まっていた。しかし、レジスタの幅が128ビットから256ビットなどの仕様が変更された AVX が採用された。
2012年の時点で Windows 7 と Windows 8 が流通している。AVX 機能を活用したアプリを作りたいとして、OS が Windows 7 であっても「Microsoft Windows 7 Service Pack 1」(SP1) が適用されていれば可能である。いわゆる無印、発売当初の Windows 7 では AVX 機能はサポート外だった。
AVX から扱えるようになった256ビット幅 のレジスタは YMM レジスタと呼ばれ、そのうち下位128ビットは SSE の頃からの XMM レジスタと共用。初期の Windows 7 では YMM レジスタ のうち上位 128ビット分の退避・復元が正しく行われないのがサポート外の原因とされている・・・

時を遡って、SSE がサポートされたのは Windows 98 からであって Windows 95 ではサポート外であった・・・

どのバージョンの OS からどの機能が使えるかを簡単に書いておくと、
SSE や SSE2 ならば Windows 98 以降
AVX ならばSP1 適用済の Windows 7 や Windows 8 以降
となる。

さらに、MSDN等で/arch:SSE2 オプションの項目を眺めると、
コンパイラは ~~~ その他の命令も使用 ~~~ 例としては ~~~ CMOV 命令 ~~~
との記述がある。ここも見落としがち・・・

そもそも、CMOV 命令とは何ぞや!?!?
と感じるヒトもいることだろう。簡単に言えば、「条件分岐」を迅速に処理するために追加された命令。

比較して、条件成立時は△△、そうでない場合は ■■ を代入したいとする。C/C++言語を想定するなら
if (条件式) 変数 = △△; else 変数 = ■■;
もしくは
変数 = (条件式)?△△:■■;
と書く。両者とも同じ意味である。一般的に、if 文と三項演算子どちらで書いても同じマシン語コードが生成される。
後者の三項演算子で書いた方がスマートで楽なのだが、可読性で不利。他のヒトが読む場合や、書いてからある程度時間が経過した頃に自身でメンテナンスを行う際、即座にコードの意味を把握できないかもしれない。不慣れなヒトが三項演算子で書かれたコードに直面した際、戸惑ってしまうことだろう・・・

「条件分岐」を機械が実行する流れは以下のような感じ。
(1) 条件を比較
(2) 条件が成立しないならば (5) へジャンプ。
(3) 変数 に △△ を代入。
(4) (6) へジャンプ。
(5) 変数 に ■■ を代入。
(6) それ以降の命令・・・

機械の中ではこのように 5 段階の処理が行われる。(2) と (4) でジャンプが行われる。「ジャンプ」と記した部分は飛び跳ねるではなく、「あっちへ行け、こっちへ戻れ」といった意味である。
なぜ、この辺りが速い遅いに関わってくるのか判り難いだろうか。例えば、実生活の場において、いつもの通り道が工事中で渋滞しているとしよう。迂回して遠回りすればいつもより遅くなる。
古くから「比較命令と条件分岐が遅くなる原因」と言われてきた。そこで、分岐を減らすために追加されたのが CMOV 命令である。

いっとき、CMOV 命令を使うか否かで数倍の速度差があると言われていた。昨今、CPU の進化にともない「比較命令と条件分岐命令の組み合わせ」でもそこそこ速やかに実行されるようになってきている。それよりも、ここで頭に入れておきたいのは、CMOV 命令の速さではなく、CMOV 命令の互換に関してである。

x86 と呼ばれる Intel の i386 ( 80386 ) の流れを汲む CPU と互換プロセッサーが存在する。ここでの「互換」とは「同じ命令体系を理解実行できる」という意味。かつては物理的にも互換性があり、Intel 製の CPU ソケットに他社製の CPU を装着可能な時期もあった。現在では物理的な互換性は無い。
互換プロセッサーとしてメジャーなのはAMD の Athlon や Phenom シリーズである。ほかにもマイナーな互換プロセッサーが存在する。マイナーな互換プロセッサー の中には SSE 命令に対応しているがCMOV 命令に対応していないモノがある。
Intel や AMD に絞って言えば、SSE 命令に対応している CPU は CMOV 命令にも対応している。だからといって、「SSE 命令対応だからCMOV 命令も使える」と決め付けるべきではない。

/arch:SSE2 オプションでは自動的にCMOV 命令も使われる。残念ながら「SSE2 命令を使うがCMOV 命令は使わない」という選択肢はない。したがって、より丁寧なアプリを目指すならば、アプリのメイン関数冒頭で
・SSE2 命令に対応しているか?
のほかに
・CMOV 命令も備っているか?
を確認するのコードを加えると良い。そうすれば、CMOV 命令に対応していない互換プロセッサーで実行した際の未定義な命令、無効な命令などで異常終了やOS のフリーズを避けることができる。「正常に動く」「同じ質を保つ」がアタリマエ。高速化される代償として不正終了等が発生するようなアプリとなってしまうのは本末転倒・・・

CMOV 命令が扱えるかの確認はIsProcessorFeaturePresent 関数ではなく、 _cpuid 関数 やCPUID 命令に頼ることになる。これを書いている時点で PF_△△△_AVAILABLE を眺めるかぎり CMOV に該当する定数が含まれていない。

さてさて、書籍や Web サイトを眺めていると
拡張された機能を使うことで以前よりも数倍速くなった
といった旨の記述も多く魅かれる。しかし、不慣れなヒトには少し難しい内容かも。それに比べお手軽な
拡張された機能を有効にするだけでは期待したほど高速化されないかもしれない。

古くは、Microsoft Visual Studio 6.0 と Visual C++ Toolkit 2003 を組み合わせでも/arch:SSE2 オプションを指定できた。コンパイラのバージョンが上がるごとに良好なコードが生成されるように改良されている。とはいえ、劇的な高速化は望めない。
Microsoft 以外のコンパイラではさらなる最適化を施すような製品も販売されている。CPU を製造している Intel からもコンパイラがリリースされている。それに比べ Microsoft の Visual Studio 付属のコンパイラだと高速化の対象はスカラ演算に限られてしまう。
そこで、コンパイラ任せではなく、明示的にSSE2関連の組み込み関数使って最適化を目指すという選択肢もある。
つまり、/arch:SSE2 オプションを指定するのではなく、
高速化したい部分のみ拡張機能を活かすコードに書き換える
手法を試みたくなるものだ・・・

長くなりましたので今回はこの辺で・・・

本日も最後までご覧いただきありがとうございます。

「つまらなかった」「判り辛った」という方もご遠慮なくコメント欄へどうぞ

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

検索サイトからお越しの方へ
検索サイトからお越しの方は、ブラウザのアドレス欄vitalaboloveおよび、fc2.comが含まれているかご確認ください。
含まれていない場合、偽サイトを閲覧なされている可能性があります。

偽サイトは、当ブログの文字部分や画像部分が有害サイトへのバナーと置き換わっているようです。
プロフィール

Author:Vitalabolove
ご訪問ありがとうございます。
店長を任されておりますVitalaboloveです。

コメントはお気軽に。
今のところリンクフリーですが、あと数日でとりやめます。

画像データ、文言の引用は事前連絡くださるようお願い申し上げます。事前連絡の際は、左下、メールフォームを経由をご利用ください。

最新記事
カレンダー
09 | 2017/10 | 11
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -
カテゴリ
ランキング
いつも応援いただきありがとうございました。ただいま休養中につきランキングへ参加していません・・・

フリーエリア
内緒話などはおきてがみをご利用ください。
月別アーカイブ
メールフォーム
掲載された記事について、ご不明な点はここからお問い合わせください

名前:
メール:
件名:
本文:

最新コメント
最新トラックバック
スパムと思われるトラックバックは削除しました
QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。