Help us understand the problem. What is going on with this article?

AVRマイコンの整数型の実行速度について検証してみた

More than 1 year has passed since last update.

Arduino StudioのC++では整数型はbyteintlongの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型の対照実験のときはbyteintになったりする。
普通に変数を2つ定義しただけだと恐らく、最適化の過程でabはなかったことにされるのでvolatileをつけて最適化を防止する。

こちらのページを参考に、
avr-objdump -m avr -D -z testspec.ino.standard.hex
で逆アセンブルをした。

image.png

今回は対照実験なのでこのコードの詳細を調べたりはしない(ので全文は載せません)。

byte型の場合

void setup() {
  volatile byte a = 2;
  volatile byte b = 3;
  a += b;
}
void loop() {
}

このコードを逆アセンブルし、先程の対照実験のコードと比較した。

image.png

上の部分は、jmpのジャンプ先の番地が変わっているが、jmpのプログラムの番地が変わることに関してはコード量が増えて後半のコードが後ろに押し出されただけなので正直どうでもいい。

image.png

ここでもjmpなどのプログラムの番地がずれているために差ができている。しかし、それ以外にも、右側のコードには以下の4行のコードが追加されている。
image.png

機械語未経験者なので詳しいことは分からないが、

  1. ( レジスタY+1 ) 番地のメモリからレジスタ25に1バイトロード
  2. ( レジスタY+2 ) 番地のメモリからレジスタ24に1バイトロード
  3. レジスタ24にレジスタ25の値を加算する
  4. レジスタ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の飛び先が違うだけの差分は省略する。

差分の中でも、計算に用いるコードは実質以下の通り。

image.png

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型の場合

もうほとんど結果は見えているけど。

image.png

byte型のときに比べて4倍、int型のときに比べて2倍の量である、加算命令4回になっている。実行には合計28クロックかかる。

結論

AVRでは、byte型が使えるときは実行速度のためにbyte型を積極的に使うべき。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away