Arduino-C言語のポインタ演算は本当に速いのか?

  • URLをコピーしました!

当記事では、「C言語のポインタ演算は本当に速いのか?」について検証してみたいと思います。

Arduinoボードで、検証用のプログラムで比較して、ポインタ演算を使用することで、具体的にどの程度、高速になるのか調べてみることにしました。

目次

Arduinoボードのポインタ演算

C言語学習サイト・書籍で有名な『苦しんで覚えるC言語』で、「配列とポインタの奇妙な関係」を解説しています。

その中で、ポインタ変数を配列のように使用する方法が紹介されており、現在ではコンパイラが最適化されており、わざわざポインタ演算を使わなくても++で増加するポインタ演算のような書き方に置き換えてくれるとのことでした。

ただし、組み込み用のC言語コンパイラでは、組み込み用CPU(マイコン)の構造が単純な分、プログラムの書き方で、速度にそのまま反映されやすくなっているとのことです。

そこで、ArduinoボードのArduino Unoに、これから紹介する「配列を使った演算」、「ポインタ演算1」、「ポインタ演算2」それぞれのサンプルプログラムで演算時間を算出して比較してみたいと思います。

サンプルプログラム(サンプルスケッチ)

用意するもの
  1. Arduino Uno
  2. USBケーブル
  3. PC(プログラム書き込み・シリアルモニタ表示)

配列を使った演算

void setup() {
  Serial.begin(9600);//シリアル通信を9600bpsで初期化
}

void loop() {
  int *data;
  int i,average=0,array[500];
  int time_us1,time_us2;

  for(i=0;i<500;i++) {//array[500]に全て1を代入
    array[i] = 1;
  }
  
  data = array;//ポインタ変数「data」に配列「array」のアドレスを代入

  time_us1 = micros();//プログラム実行からの経過時間(us)を計測
    
  for(i=0;i<500;i++) {//配列の各数字を全て足す
    average = average + data[i];
  }

  time_us2 = micros();//プログラム実行からの経過時間(us)を計測

  Serial.print("ポインタ演算の時間:");//文字列「ポインタ演算の時間:」を送信
  Serial.println(time_us2 - time_us1);//数字「ポインタ演算の時間」を送信、改行
  Serial.print("平均値:");//文字列「平均値:」を送信
  Serial.println(average / 500);//数字「平均値」を送信、改行
  
  delay(1000);//1000msec待機(1秒待機)
}
シリアルモニタ例

ポインタ演算の時間:376
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1

「配列を使った演算」のサンプルプログラムでは、ポインタ変数「data」に配列「array」のアドレスを代入した後、ポインタ変数「data」を配列のように使って500個のデータを全て足していきます。

この演算の前後に、micros()関数でプログラム実行からの経過時間(us)を計測しておき、この差分から「配列を使った演算」の時間を算出します。

ポインタ演算1

void setup() {
  Serial.begin(9600);//シリアル通信を9600bpsで初期化
}

void loop() {
  int *data;
  int i,average=0,array[500];
  int time_us1,time_us2;

  for(i=0;i<500;i++) {//array[500]に全て1を代入
    array[i] = 1;
  }
  
  data = array;//ポインタ変数「data」に配列「array」のアドレスを代入

  time_us1 = micros();//プログラム実行からの経過時間(us)を計測
    
  for(i=0;i<500;i++) {//配列の各数字を全て足す
    average = average + *(data + i);
  }

  time_us2 = micros();//プログラム実行からの経過時間(us)を計測

  Serial.print("ポインタ演算の時間:");//文字列「ポインタ演算の時間:」を送信
  Serial.println(time_us2 - time_us1);//数字「ポインタ演算の時間」を送信、改行
  Serial.print("平均値:");//文字列「平均値:」を送信
  Serial.println(average / 500);//数字「平均値」を送信、改行
  
  delay(1000);//1000msec待機(1秒待機)
}
シリアルモニタ例

ポインタ演算の時間:376
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1
ポインタ演算の時間:384
平均値:1
ポインタ演算の時間:380
平均値:1

「ポインタ演算1」のサンプルプログラムでは、「配列を使った演算」のサンプルプログラムとほぼ同じですが、配列「array」の数字を全て足す計算を以下のように「*(ポインタ変数 + 要素番号)」を使ったポインタ演算に変更しています。

for(i=0;i<500;i++) {//配列の各数字を全て足す
  average = average + *(data + i);
}

この演算の前後に、micros()関数でプログラム実行からの経過時間(us)を計測しておき、この差分から「配列を使った演算」の時間を算出します。

ポインタ演算2

void setup() {
  Serial.begin(9600);//シリアル通信を9600bpsで初期化
}

void loop() {
  int *data;
  int i,average=0,array[500];
  int time_us1,time_us2;

  for(i=0;i<500;i++) {//array[500]に全て1を代入
    array[i] = 1;
  }

  time_us1 = micros();//プログラム実行からの経過時間(us)を計測
  
  for(data=array;data!=&array[500];data++) {//配列の各数字を全て足す
    average = average + *data;
  }
  
  time_us2 = micros();//プログラム実行からの経過時間(us)を計測

  Serial.print("ポインタ演算の時間:");//文字列「ポインタ演算の時間:」を送信
  Serial.println(time_us2 - time_us1);//数字「ポインタ演算の時間」を送信、改行
  Serial.print("平均値:");//文字列「平均値:」を送信
  Serial.println(average / 500);//数字「平均値」を送信、改行
  
  delay(1000);//1000msec待機(1秒待機)
}
シリアルモニタ例

ポインタ演算の時間:316
平均値:1
ポインタ演算の時間:316
平均値:1
ポインタ演算の時間:324
平均値:1
ポインタ演算の時間:316
平均値:1
ポインタ演算の時間:324
平均値:1
ポインタ演算の時間:316
平均値:1
ポインタ演算の時間:316
平均値:1
ポインタ演算の時間:320
平均値:1
ポインタ演算の時間:316
平均値:1
ポインタ演算の時間:316
平均値:1

「ポインタ演算2」のサンプルプログラムでは、「ポインタ演算2」の配列「array」の数字を全て足す計算を以下のようなポインタ演算に変更しています。かなり複雑ですが、こちらの方が一般的なポインタ演算とされており、高速で動作するとされています。

for(data=array;data!=&array[500];data++) {//配列の各数字を全て足す
  average = average + *data;
}

この演算の前後に、micros()関数でプログラム実行からの経過時間(us)を計測しておき、この差分から「配列を使った演算」の時間を算出します。

ポインタ演算の比較

それぞれのサンプルプログラムで計測した配列の数字をすべて足す時間を算出しましたが、さらに100回分のデータを平均化した数値を計算してみました。

ポインタ演算の比較

配列を使った演算:380.6usec
ポインタ演算1:380.6usec
ポインタ演算2:317.72usec

「配列を使った演算」と「ポインタ演算1」を比較すると演算時間は、同じでしたが、「ポインタ演算2」では、62.88usec(16.5%)高速になったことがわかりました。

ただし、今回のサンプルプログラムで計算した演算時間は「500回の足し算」になります。仮に「1回の足し算」だと0.12576usec速くなるということになります。

Arduinoの場合、「ポインタ演算2」を使うことで早くなるようですが、プログラム自体は見づらくなってしまうので、速度にシビアなプログラムでなければ、「配列を使った演算」を使用するほうが良いと思います。

おすすめのArduinoボードはどれ?

当記事『Arduino-C言語のポインタ演算は本当に速いのか?』では、Arduino Unoを使用したサンプルプログラムでポインタ演算の時間を比較してきました。

やはり、たくさんの種類のあるArduinoボードの中でも、最も基本的なエディションのArduino Unoがおすすめなのですが、Arduino Unoと電子部品を組み合わせたキットも存在します。

電子工作初心者にとっては、いちいち電子部品を別途購入する必要がないので非常に有用です。以下の記事で初心者でもわかりやすいように、ランキング形式でおすすめのArduino Unoを紹介しているので、ぜひご覧ください。

また、以下の記事で、安価でWi-Fi/Bluetoothに対応している「ESP32開発ボード」についてもまとめてみました。

このボードは、Arduinoボードではありませんが、Arduino IDEでソフト開発ができるため、電子工作でIoTを実現したい方におすすめです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次