CPUの動作に関するより詳しい解説ページ

  • (by K, 2006.09.02)
  • 発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)

CPUによる自動スタック切り替え機能の詳細ついて

  • (by K, 2006.09.02)
  • おもに 21-7 の内容の補足
    • ついでにINT命令、IRETD命令の詳細の理解
  • この内容は理解を深め知的好奇心を満足させてくれますが、話が長くなる上にややこしくしたくはなかったので、本文中では割愛しました。それをここに書きます。

  • スタックの切り替えは、INT命令実行時(割り込み時も含む)とIRETD命令の実行時におきます。
  • INT命令の実行時にCPUは以下の処理をします。
    if (現在がアプリモードで、割り込み処理ルーチンがOSモードの場合) {
        スタックを切り替えるために、ESPとSSの値をTSSのss0とesp0から読み込む。
        IRETD命令実行時にもとのSSとESPに戻せるように、 PUSH(旧SS); PUSH(旧ESP); を実行する。
            (これらの値は切り替わった先のOS用スタックに積まれることになる。)
    }
    PUSH(EFLAGS); PUSH(CS); PUSH(EIP); CLI(); を実行する。
    EIPとCSの値をIDTから読み込む。
    if (エラーコードのある例外の場合) {
        PUSH(エラーコード); を実行する。
    }
  • IRETD命令の実行時にCPUは以下の処理をします。
    POP(EIP); POP(CS); POP(EFLAGS); を実行する。
    if (それまでがOSモードでしかもPOPしたCSがアプリモードの場合) {
        スタックを切り替えるために、 POP(ESP); POP(SS); を実行する。
            ( POP(ESP); した時点でESPが変わってしまうと次の POP(SS); がうまくいかなくなるので、
              回路を工夫して、POPした値がESPに代入されるタイミングを遅らせてある。)
    }

整数除算命令の重さについて

  • (by K, 2008.08.23)
  • 整数除算命令(DIV、IDIV)の実行にはやたらとクロックがかかるのは本文に書いたとおりです。これは単なる割り算(/)だけではなくて、あまりを求めるときにも実行される命令です(%)。
  • 一方で、掛け算はどうかというと、2のベキ倍(2倍、4倍、8倍、16倍・・・)はシフト命令を使うことで非常に少ないクロックで実行できるのは知っている人も多いと思いますが、整数乗算命令(IMUL)はPentiumPro/PentiumII以降なら(つまりCPUのクロックが300MHzを超えているような機種なら)シフト命令並みの速度で計算できるようなっています。したがってかつての高速化テクニックを使って、18倍を9倍×2倍とかに直すよりも単にIMULを使ったほうが高速だったりします。
  • 掛け算命令がCPUの進歩と共に高速化されたにもかかわらず、割り算が高速化されていないのには理由があります。桁数の多い掛け算の筆算を考えてみてほしいのですが、掛け算では筆算の各行を計算するときに、他の行の結果が必要になったりはしません。他の行の計算結果が必要なるのは、最後に全部の和を出すときだけです。だからCPUの中ではそれぞれの行の算出を別々の回路で同時にやらせることが可能で、演算時間の高速化を達成しています。
  • 一方で割り算はこうは行きません。桁数の多い割り算の場合、一つの桁に対して、割ったあまりを出しおわらないと、次の桁の計算が始められないのです。このため掛け算のときのような高速化が出来ません。

テーブルの使用について

  • (by K, 2008.08.23)
  • 割り算などの計算を高速化するために、計算結果を配列に入れておき、計算の代わりに配列から値を取り出すという手法があります(九九の表から掛け算結果を求めるような感じ)。これはかつてはかなり有効な方法でしたが、現在ではうまくいかない場合も出てきています。
  • というのも、最近のCPUはとても高速に動作しているのにもかかわらず、メモリはあまり高速ではないからです。そのために「キャッシュメモリ」なんてものがCPUに取り付けられ、メモリのスピードにCPUの足が引っ張られないように工夫しているくらいです。
  • 割り算くらいの演算量であれば、わざわざテーブルを作るのは得策ではないかもしれません。なぜなら、テーブルは当然のことながらメモリ上に置かれ、テーブルから値を取り出すのには(キャッシュメモリ上にテーブルのコピーが無ければ)ものすごく長い時間がかかるからです。この時間のかかり方は、DIV命令の遅さとは比べ物にならないとんでもないものです。
  • もちろん以前その付近のメモリを読み書きしたことがあれば、キャッシュメモリはその値を覚えているはずなので、テーブルを引くのには時間は要りません。だからそういう場合はテーブルは効果絶大です。しかしキャッシュメモリの容量はかなり小さいので、他の番地のメモリの内容を覚えているうちにテーブルの内容を忘れてしまうことは十分にありえます。
  • また仮にキャッシュメモリにいつも入っているとしても、それはつまりその分だけ他のメモリの内容をキャッシュメモリに入れておく機会を奪っていることになるので、プログラムの他の場所でキャッシュミスヒットが発生し、実際のメモリから値を読みに行くことになるでしょう。それなら整数除算命令を素直に使うほうが総合的には速いかもしれません。ということで、結局除算程度の処理ではテーブルによる高速化はあまりうまくいきません。テーブルはもっとずっと重たい計算(たとえば平方根とか)でこそ効果があるでしょう。

こめんと欄


コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2008-08-23 (土) 13:23:29 (3804d)