a = b +c; を、Accumulator、Memory-memory、Stack、Load-storeの命令セットスタイルでどうなるか考えて考えようという問題。a,b,cはメモリ上の変数です。Load-storeの命令スタイルは、教科書にでてくるMIPSのスタイルです。
Accumulator
Accumulatorの場合は3命令で実行できます。
load AddressB # bをアキュムレータにロード add AddressC # cとアキュムレータに入っているbを足して、結果をアキュムレータに入れる store AddressA # アキュムレータをaにストア
アキュムレータが1個しか無い場合は、プログラムを実行するのに命令の数が増えてしまいます。
Memory-memory
Memory-memoryの命令セットを使う場合、オペランドにメモリを直接指定できるため、1命令で実行できます。
add AddressA AddressB AddressC
Stack
Stack型のプロセッサというのは、結局スタックのトップをAccumulatorとして使っているので、Accumulatorと似たような命令になります。
push AddressB # bをスタックにpush push AddressA # aをスタックにpush add # 加算 pop AddressA # 加算の結果をpopしてaへ
各スタイルの比較
この条件で比較してみます。
- オペコードは1バイト
- メモリアドレスは2バイト
- データオペランドは4バイト
- 全ての命令はバイトの長さの整数値
- 最適化無し
style | instructions | code bytes | data bytes |
---|---|---|---|
Accumulator | 3 | 3+3+3 | 4+4+4 |
Memory-memory | 1 | 7 | 12 |
Stack | 4 | 3+3+1+3 | 4+4+0+4 |
Load-Store | 4 | 4+4+3+4 | 4+4+0+4 |
命令数に関しては、Memory-memoryが1命令なので一番小さくなります。コードのバイト数に関しても、Memory-memoryが一番小さくなります。a = b + c の場合、データ数はa,b,cの12バイトになるのは全てのスタイルで共通です。
これだけ見るとMemory-memoryが一番良いように思えるのですが、実際にはMemoryへのアクセスは内部レジスタよりも何桁も遅いため、トータルの処理速度は必ずしも速くなりません。a = b + c + d;のような場合、中間の計算結果もメモリに書き戻して、再度読み込むので処理自体は遅くなります。
Load-Storeスタイルに比べて、AccumulatorやStackは若干コードサイズが小さくなります。これは、add $2, $3, $4の命令からオペランドが省略できるというだけの話で、コードサイズの代わりに直交性を失っています。結果的には同じ計算をするにも命令数が増えてしまい、トータルの命令サイズではむしろ大きくなります。言語の組み込みの型で大半の処理が終わる場合は良いのですが、構造体の定義をしようとしただけで少し無理をするコードが必要になります。
AccumulatorやStackのメリットとしては、物理的なレジスタの要求が少ないので、ハードウェアが単純になったり、エミュレーションが楽になったりします。数値計算が多いときや、途中結果をどんどん捨てていっても良い場合は、AccumulatorやStack型でも十分な性能が出せます。PostScriptやHPの電卓でStack型が採用され、今も使われているのはこういった理由が大きいと想像しています。
Accumulatorを使った計算の例(2008/10/24追加)
こちらにAccumulatorを使ったもう少し複雑な例があります。
http://journal.mycom.co.jp/column/architecture/113/index.html
こちらのコラム全体が非常に良くまとまっています。コンピュータアーキテクチャの勉強にお勧めです。
コンピュータアーキテクチャの話