→ CPUID命令によるCPUの性能・機能の把握 (CodeZine) として公開されています。 完成図 †
はじめに †この記事ではインテル系の CPU に実装されている CPUID 命令がどのように働いているかを考察します。 今回、デモプログラム(littleCPUID) を作成しました。このプログラムは実際に使用しているシステムから CPUID 命令を実行し、その結果を出力します。 対象読者 †
必要な環境 †Visual C++ version 6 SP6(MFC)で開発を行っています。 実行環境としては Windows 2000/XP を想定しています。現行では Windows 9x 系では実行出来ないはずです。これは 2000/XP のみで使用される API を使用しているためで、それらを明示的に Loadlibrary すれば対応出来るのですが、面倒なのでやっていません。 CPUID (CPU Identification) †システムのプロパティ表示の不思議 †まずは下記の「システムのプロパティ」を見てもらいたい。 CPU 名が表示されているのですが、CPU 名の頭に余計な空白が入っています。 この現象はインテル社の Pentium 4 以降の CPU でのみで発生しており、AMD 社の CPU では 発生していません。 実はこの空白の原因は CPUID 命令の動作に起因した現象と考えています。 CPUID(CPU Identification) 命令とは †CPUID 命令(プロセッサ識別) は、CPU の基本的な命令コードである MOV 命令(データ移動) や ADD 命令(加算)や、IN/OUT 命令(外部入出力)と同じくインテル系の CPU には必ず実装されている命令コードです。 CPUID 命令は主に以下の目的で使用されます。
CPUID 命令の動作 †インテルのサイトで配布されている 日本語技術資料の 「IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 中巻」に以下の記述があります。
レジスタ EAX に所定のインデックス値を入れ CPUID 命令を実行することで EAX、EBX、ECX、EDX に結果を出力します。 インデックス値に対して以下の値が返却されます。 以下の説明で CPUID(xxx) と表現されていますが、これは EAX=xxx を設定し CPUID 命令を実行した結果を指します。 CPUID 情報 †CPUID(2) 以降は、Intel がほぼ独占状態のようです。
一般的に Vendor ID で製造 CPU 会社を判断し、プロセッサ・シグネチャで Pentium III/Pentium 4/Athlon 等の CPU の種類を判断します。 CPU の種類で CPU の機能を判断してはならず(例えば「Pentium III なら全て MMX をサポートしているはずだ」という決め打ち)、「Function Flags」にて CPU の機能を検出し判断します。例えば CPUID(1) の EDX の値の 23 bit 目が立っている場合、MMX を サポートしていると判断します。 拡張 CPUID 情報 †拡張 CPUID は AMD 社が活用している領域です。Pentium 4 より Intel も活用し始めたようです。
CPUID 命令の実際 †実際に CPUID 命令を実行してみましょう。実際に実行するにあたっては Windows 標準搭載されている debug.exe 互換の EXDEB というソフトウェアを使用します。コマンド プロンプトより EXDEB を実行し、以下の手順にて動作確認をします。
以下が上記の実行例になります。 D:\>exdeb <Enter> Extended Debugger Ver1.85 94,97/07 by Sey_ju_row CPU [Pentium II] FPU [Present] Machine [PC-98 DOS/V] -r1 <Enter> EAX=000F:0000 EBX=0000:0000 ECX=0000:0000 EDX=6974:0000 ESP=0000:0000 EBP=0000:0000 ESI=0000:0000 EDI=0000:0000 DS=5F51 ES=0000 SS=5F51 FS=0000 GS=0000 CS=5F51 IP=0100 -N11--I--------- 5F51:0100 66B800000000 MOV EAX,00000000 -a <Enter> 5F51:0100 mov eax, 0 5F51:0106 cpuid 5F51:0108 int 3 5F51:0109 -u <Enter> 5F51:0100 66B800000000 MOV EAX,00000000 5F51:0106 0FA2 CPUID 5F51:0108 CC INT 3 5F51:0109 0000 ADD [BX+SI],AL 5F51:010B 0000 ADD [BX+SI],AL 5F51:010D 0000 ADD [BX+SI],AL 5F51:010F 0000 ADD [BX+SI],AL 5F51:0111 0000 ADD [BX+SI],AL -g <Enter> EAX=0000:0001 EBX=6874:7541 ECX=444D:4163 EDX=6974:6E65 ESP=0000:0000 EBP=0000:0000 ESI=0000:0000 EDI=0000:0000 DS=5F51 ES=0000 SS=5F51 FS=0000 GS=0000 CS=5F51 IP=0108 -N11--I--------- 5F51:0108 CC INT 3 -q <Enter> D:\> 上記のケースは CPUID のインデックス値が 0 の時に以下の値が返却されました。 EAX = 0x1 EBX = 0x68747541 ("htuA") ECX = 0x444D4163 ("DMAc") EDX = 0x69746E65 ("itne") EBX:EDX:ECX (リトルエンディアン)の順に並べると ASCII で「AuthenticAMD」になります。この文字列によりこの CPU は AMD 社が開発したものと判断できます。 EAX が 1 であるということは CPUID のインデックス値は 0 と 1 のみ有効であるということが分かります。これは昔から AMD 社の CPU は 0x80000000 以上の Extended CPUID の値を中心に情報を返却する方針を取っているからのようです。余談ですが、後にインテル社も Pentium 4 辺りから Extended CPUID をサポートすることになります。 以下は、同様に Intel 社製の CPU(Pentium 4) で上記のプログラムを実行した結果です。 EAX = 0x2 EBX = 0x756E6547 ("uneG") ECX = 0x6C65746E ("letn") EDX = 0x49656E69 ("Ieni") EBX:EDX:ECX (リトルエンディアン)の順に並べると ASCII で「GenuineIntel」になります。この文字列によりこの CPU はインテル社が開発したものと判断できます。また EAX が 2 であるということは CPUID のインデックス値は 0、1、2 が有効であるということが分かります。 littleCPUID の実装 †littleCPUID によるデモ †littleCPUID には3つの機能を実装しています。
littleCPUID のアルゴリズム †CPUID の特性上、動的に変更されることはほとんど無いはずです。よって起動時に CPUID 命令で取得できるデータを取れるだけ取って、そのデータを使いまわすことにしました。以下のアルゴリズムで全ての CPUID の値を取得しています。
「システムのプロパティ表示の不思議」の考察 †以下は上記の「システムのプロパティ」を表示している PC の CPUID です。 idx=[00000000], eax=[00000002], ebx=[756E6547], ecx=[6C65746E], edx=[49656E69] idx=[00000001], eax=[00000F27], ebx=[00010809], ecx=[00000400], edx=[BFEBFBFF] idx=[00000002], eax=[665B5101], ebx=[00000000], ecx=[00000000], edx=[007B7040] idx=[80000000], eax=[80000004], ebx=[00000000], ecx=[00000000], edx=[00000000] idx=[80000001], eax=[00000000], ebx=[00000000], ecx=[00000000], edx=[00000000] idx=[80000002], eax=[20202020], ebx=[20202020], ecx=[20202020], edx=[6E492020] idx=[80000003], eax=[286C6574], ebx=[50202952], ecx=[69746E65], edx=[52286D75] idx=[80000004], eax=[20342029], ebx=[20555043], ecx=[30342E32], edx=[007A4847] ちなみに 0x20 は ASCII 文字で表すと空白(' ')を示します。どうやら「システムのプロパティ」は拡張 CPUID の「プロセッサ・ブランド・ストリング(80000002-80000004h)」が影響しているようです。上記のレジスタの値より「プロセッサ・ブランド・ストリング」を ASCII で表すと以下の文字列になります('['、']' は含みません)。 [ Intel(R) Pentium(R) 4 CPU 2.40GHz] つまり先頭の空白はシステムのプロパティが「プロセッサ・ブランド・ストリング」をそのまま出力していると考えられます。 ちなみに CPUID は Intel 社が開発したものですが拡張 CPUID は、AMD 社が開発しています。AMD 社の「プロセッサ・ブランド・ストリング」は頭に空白は無く、先頭から文字列が始まっています。一方、Intel 社は Pentium 4 より拡張 CPUID を採用していますが、採用当初から先頭が空白になっていたようです。 なぜ先頭を空白にしているかは私には分かりませんでした。多分、技術的以外の問題がそうさせているのではないでしょうか。
まとめ †CPUID 命令により、CPU に実装されている多くの機能をソフトウェアより知ることが可能であることが分かりました。 CPUID 命令を使用することにより「予め」ソフトウェア側で CPU の性能・機能を知ることが出来、新しい機能を使用するか、古い機能で代用するかを切り分ける用途にも使用できます。 このような場面でも CPUID 命令は使用されている、という一例 †最近、米 AMD 社が米 Intel 社に対して独占禁止法の違反があるとして訴訟を起こしたニュースが話題になっています。この訴訟内容の一つにインテル製のコンパイラが CPUID にて AMD 社製の CPU かどうか検知し、意図的に最適化されないパスに落とし込んでいる、と訴えています。AMD 社の言っていることも尤もなのですが、Intel 社からしても販促の意味合いの強いコンパイラで他社のチューニングまで保障するか、と言われるとそれは難しいことなのかもしれません。 最後にお願いなど †現在、「CPUID 命令の考察」の続きモノとして「マルチコアの検出方法の調査」というのを書いている最中です。CPUID の情報だけでマルチコア、Hyper-Threading、Multi-processing を検出し、物理的に使用している CPU 数を検出するサンプルプログラムの作成をしたら面白いんじゃないか、と考えています。 一応、仕様書に記載している通りに実装する所までは終えています。 ただ今の所、私の手元にはデュアルコアや Multi-processing の実機が無く、実際動作するかどうか確認出来ない状態です。申し訳ないのですが下記の CPU をお持ちの方、是非『Dump』情報の提供に協力してください。CPU-Z のテキストダンプでも可能ですが CrystalCPUID の場合、情報が足りません。
送信方法は CodeZine の送信箱 でも構いませんし、直接、一番下にあるボタンより「メッセージを投稿」より投稿してもらっても構いません。 確認出来次第「マルチコアの検出方法の調査」の方、投稿します。 littleCPUID 自身は Intel/AMD 以外はサポートしていません(Other CPU として扱っています)。他の CPU ももっとサポートしてほしいとお考えのアナタ、サポートはやぶさかではありません。実機を持っていないことにより対応出来ていないだけで、アナタの協力さえあれば対応は可能です。 是非、よろしくお願いします。 参考資料 †インテル †
AMD †
参考にしたサイト †
参考にしたツール類 †
関連書籍 †
|