Arduino StudioのC++では整数型はbyte
、int
、long
の3種類 ( unsigned
とか抜くと多分この3種類 ) が扱えるが、大きい型で計算したときにパフォーマンスにどのくらい影響があるのかというのを調べた。
調べた理由
- Arduinoで音声関連のリアルタイム処理をしたかったが、その前にどこまで計算量の多さに気をつける必要があるのかを調べたかった。 ( 実験の結果によっては可能な限りbyte型を使うようにする必要もある。 )
Google力が足りなかった
前提条件
やろうと思っている音声関連処理はArduino Uno R3を利用する。Arduino UnoはATmega328というマイコンで、
- クロック周波数は16MHzである → 24KHzの音声を処理する場合1標本につき使用できるのは約666クロック分 ( 少ない、節約する必要があるかもしれない )
- メインメモリ ( データ格納 ) はSRAMである → メモリアクセスの遅延はほとんど影響しない?
- RISC型CPUであり、機械語の1命令が1クロックで実行できる → 機械語の量が処理時間とほぼ比例する
実験
以上のことから、処理時間を調べるためには実機で動作させるよりも生成された機械語の量を見るほうが確実であると考えた。そのため、それぞれ違う型で整数型の足し算a = a + b
のみを行うC言語のコードを書き、コンパイル後の機械語の詳細を調べた。
対照実験
void setup() {
volatile byte a = 2;
volatile byte b = 3;
}
void loop() {
}
上の例はbyte
型の場合の対照実験である。int
型の対照実験のときはbyte
がint
になったりする。
普通に変数を2つ定義しただけだと恐らく、最適化の過程でa
とb
はなかったことにされるのでvolatile
をつけて最適化を防止する。
こちらのページを参考に、
avr-objdump -m avr -D -z testspec.ino.standard.hex
で逆アセンブルをした。
今回は対照実験なのでこのコードの詳細を調べたりはしない(ので全文は載せません)。
byte型の場合
void setup() {
volatile byte a = 2;
volatile byte b = 3;
a += b;
}
void loop() {
}
このコードを逆アセンブルし、先程の対照実験のコードと比較した。
上の部分は、jmp
のジャンプ先の番地が変わっているが、jmp
のプログラムの番地が変わることに関してはコード量が増えて後半のコードが後ろに押し出されただけなので正直どうでもいい。
ここでもjmpなどのプログラムの番地がずれているために差ができている。しかし、それ以外にも、右側のコードには以下の4行のコードが追加されている。
機械語未経験者なので詳しいことは分からないが、
- ( レジスタY+1 ) 番地のメモリからレジスタ25に1バイトロード
- ( レジスタY+2 ) 番地のメモリからレジスタ24に1バイトロード
- レジスタ24にレジスタ25の値を加算する
- レジスタ24の値を ( レジスタY+2 ) 番地のメモリに書き込む
という処理をしているらしい。機械語ではよくあるa=a+bの処理である。
ここを参考にクロック数を計算してみたところこの部分で7クロック必要であることがわかった。
int型の場合
void setup() {
volatile int a = 2;
volatile int b = 3;
a += b;
}
void loop() {
}
Arduino StudioのC++ではint
は2バイト型である。
対照実験のコードやjmp
の飛び先が違うだけの差分は省略する。
差分の中でも、計算に用いるコードは実質以下の通り。
Arduinoはリトルエンディアンなので、2バイトの変数の場合は上位8ビットが後ろ側に来る。
このコードで言うと、
( Y+1 ) 番地には変数bの下位8ビットが、
( Y+2 ) 番地には変数bの上位8ビットが、
( Y+3 ) 番地には変数aの下位8ビットが、
( Y+4 ) 番地には変数aの上位8ビットが、それぞれ格納されているらしい。
また、ここでは加算命令が2回行われている。最初のadd命令で下位8ビット同士を足し算し、次にadc命令で、先程の計算の繰り上がりを考慮しながら上位8ビット同士を足し算している。
どうやら、AVRマイコンは8bitの値しか計算に使用できず、16ビットの値を計算するには2回に分けて行う必要があるらしい。この部分の命令の実行には合計14クロック必要である。int
型の場合、byte
型に比べて型の大きさが2倍になったぶん計算にかかる時間も2倍になっている。
long型の場合
もうほとんど結果は見えているけど。
byte
型のときに比べて4倍、int
型のときに比べて2倍の量である、加算命令4回になっている。実行には合計28クロックかかる。
#結論
AVRでは、byte
型が使えるときは実行速度のためにbyte
型を積極的に使うべき。