今回はCPUのbit数についてまとめます。
8bitCPU、16bitCPU、32bitCPUという言葉を聞いたことがあると思います。
これらがどういう意味なのかを説明し、そのあとに実装上の注意点を書いていきます。
CPUのbit数の意味
CPUのbit数は、一度に処理ができるサイズ、という表現もされますが、具体的にはレジスタのサイズやバスのサイズになります。
一度に処理できるサイズが大きいということは、それだけ処理が早くなるということです。
もともと4bitCPUから始まって、CPUの高性能化に伴って、8bitCPU、16bitCPU、32bitCPUが登場してきました。
bit数が大きくなるということは、レジスタのbit数やバスの本数が増えるということなので、内部の構造も複雑になってきます。
ですので、最初は小さいbit数から始まって、技術の向上に伴いbit数が大きくなっていった、ということですね。
現在は、比較的小規模なマイコンレベルであれば32bitCPUが主流になっています。
スマートフォンなど、高機能な機器では64bitのCPUが使用されることが多くなっています。
動作の違い(16bitと32bitの比較)
それでは、ここからC言語で書いた場合を例として、bit数の違いによる動作の違いを見ていきます。
今回は、現在のマイコンの主流である32bitと、少し前に主流だった16bitの違いを見ていきます。
扱える型の違い
整数型の場合、16bitCPUの場合はshort型、32bitCPUの場合はlong型まで一度に扱うことができます。
したがって、short型までのパラメータを扱う場合は、16bitCPUでも32bitCPUでも違いはありません。
しかし、long型になると状況が変わってきます。
加算の場合を例にとって考えると、32bitCPUの場合は1コマンドで処理が完了できますが、16bitCPUの場合、
- 下位16bitの加算処理
- 桁上がりの処理
- 上位16bitの加算処理
といった処理をする必要があります。
処理の数だけ動作が遅くなっていきますので、16bitCPUの場合はできるだけshort型以下になるように気を付けて実装する必要があります。
浮動小数点型の場合も同様です。
floatを扱う場合、32bitCPUであれば1レジスタで扱うことができますが、16bitCPUの場合は2レジスタで扱うことになります。
さらに、16bitCPUの場合は、内部のライブラリを使って関数コールのような処理がされることが多いので、long型の処理よりもさらに処理に時間を要することが多いです。
16bitCPUで精度よりも処理速度を優先する場合、short型やlong型にキャストしてから演算処理が実施されることがあるほどでした。(もちろん、キャストしても必要な精度が保てる場合に限りますが)
32bitCPUでは、1レジスタで処理できる分ある程度早く処理ができますし、最近のマイコンには浮動小数の演算器がついていることもあるので、かなり早く処理ができることが多いです。
なお、32bitCPUでもdouble型を使うことはほとんどありません。
32bitCPUでもdoubleを使うためには2レジスタ使う必要があるので処理が遅くなりますし、floatでも有効数字5~6桁程度あるので、精度として十分なことが多いです。
intのサイズ
intのサイズはCPUのbit数によって変わります。
そのままbit数と一致していて、16bitCPUの場合は2byte、32bitCPUの場合は4byteとなります。
16bitCPUから32bitCPUへの移行を行う場合は、サイズが変わることを念頭に置いて対応する必要があります。
(ですので、私は最初からint型を使用しないようにしています。)
なお、なぜか64bitCPUになると、int型のサイズは4byteのままで、long型が8byteに変わるので、この点も注意が必要です。
扱えるアドレスの違い
アドレスの処理も基本的に1レジスタで行うので、扱えるアドレスの範囲も異なります。
16bitCPUの場合、65536byteまでですので64KBまで、32bitCPUの場合、4294967296byteまでですので4GBまでとなります。
では、16bitCPUで64KBまでしか扱えないかというとそういうわけではなく、2レジスタを使用してアドレスを拡張することができます。
基本的にはコンパイラがある程度自動で解釈して記述してくれますが、C言語の場合で明確に指示をする場合はfarポインタを使ってコンパイラに指示を送ることができます。
(書き方はコンパイラによって異なる可能性があるので、ここでは細かく書きません。)
今はもうfarポインタを使うことはほとんどないとは思いますが、そういうものがあるということを知っておいて、16bitCPUを扱うことになったときに思い出していただきたいと思います。
注意点としては、64KBの境界を越えて同時にアクセスしようとするとき、思っていない動作をすることがあります。
例えば、memcpyで64KBの境界を超えるようなアドレスを指定した場合、memcpyがfarポインタに対応していなければ、アドレスの上位16bitが更新されずに思っていないアドレスが指定される可能性もあるので、注意が必要です。
アドレス境界
16bitCPUでは2byteごとに、32bitCPUでは4byteごとにアドレス境界があることを意識する必要があります。
例えば以下の構造体を定義し、実体を作ったとします。
struct name{
long a;
short b;
short c;
char d;
};
構造体内で定義されているサイズは9byteですが、実際に確保されるサイズは、16bitCPUの場合は10byte、32bitCPUの場合は12byteとなります。
アドレス境界までは領域を確保されるということですね。
また、境界以外から始まるパラメータはきちんと動作しない可能性があるので注意が必要です。
例えば、奇数のアドレスに配置されたlong型のパラメータは正しく動作しない可能性があります。
通常はコンパイラがきちんと正しいアドレスに配置してくれるのでほとんど意識する必要はありませんが、自分でアドレスを指定する場合などは注意が必要です。
まとめ
以上、C言語におけるCPUbit数の違いによる動作の違いを説明してきました。
今回説明した注意点は以下の通りです。
- 一度に扱える型の大きさの違い(処理速度に直結)
- intのサイズ
- 扱えるアドレス範囲の違い
- アドレス境界
今はもう16bitCPUを扱うことはほとんどなくなってきていると思いますのでそれほど違いを気にする必要はないのかもしれませんが、いざ扱う機会が出てきたときに思い出していただけると嬉しいです。
また、どのbit数のCPUを使っていても知っておかないといけない内容もありますので、しっかり理解していただきたいです。