それではGenuino 101がArduino Unoと比較して、どんな性能や機能を備えているかいくつか比較してみたいと思う。そもそもマイコンだから、主用途はI/Oというケースが一番多い。Analog/DigitalのInput/Outputというわけで、そうしたI/O性能を比較してみることにした。ちなみに評価にはArduino UnoとGenuino 101のほか、Galileo Gen2も用意した。

Digital Output性能

まずは基本となるDigital Outputを試そう。List 1の様なSketchを作り、出力される電圧をオシロスコープで測定した。結果はArduino Uno(Photo24)、Galileo Gen2(Photo25)、Genuino 101(Photo26)の通りだ。

List 1:
#define LED_OUT 11

void setup() {
  pinMode(LED_OUT, OUTPUT);
  }

void loop() {
  digitalWrite(LED_OUT, HIGH);
  digitalWrite(LED_OUT, LOW);
}

Photo23:要するにLチカをやらせて、LEDの根元で電圧を測定

Photo24:Arduino Unoの結果

Photo25:Gallileo Gen2の結果

Photo26:Genuino 101の結果

数字をまとめたのが表1である。TotalというのはOn→Off→Onという一連の周期全体、LED OnはLED点灯の時間、LED OffはLED消滅の時間である。

Total LED On LED Off Loop Process
Time
(μs)
Freq
(KHz)
Time
(μs)
Freq
(KHz)
Time
(μs)
Freq
(KHz)
Time
(μs)
Freq
(KHz)
Arduino Uno 10.560 94.697 5.076 197.006 5.494 182.017 0.418 2.392
Galileo Gen2 2.247 445.038 1.116 896.057 1.131 884.173 0.015 66.667
Genuino 101 5.552 180.115 1.759 568.505 3.793 263.644 2.034 0.492

List 1を見るとloop()の中はDigitalWrite()を2回呼んでいるだけに見えるが、実際の処理は

loop:
  digitalWrite(LED_OUT, HIGH);
  digitalWrite(LED_OUT, LOW);
  goto loop;

となるわけで、LED Onの時間とOffの時間の差は、最後の"goto loop;"を処理する時間である。つまりLED Onの数字が実際のdigitalWrite()に要するもの、最後のLoop Process(LED Offの時間とLED Onの時間の差)が"goto loop;"を処理する時間というわけだ。

この観点で見ると、digitalWrite()に関してはGenuino 101はGalileo Gen2にはおよばないとはいえ、Arduino Unoの3倍近く高速なのだ。しかし、Loop ProcessではArduino Unoと比較して5倍近く遅い結果になっているのはちょっと興味深い。

Analog Output性能

ArduinoはPWM出力を利用してアナログ値の出力も可能である。そこで、構図的にはPhoto23と同じながら、SketchをList 2の様に書き換えてオシロスコープで出力波形を確認してみた。結果はArduino Uno(Photo27)、Galileo Gen2(Photo28)、Genuino 101(Photo29)の通りだ。数字をまとめたのが表2である。

List 2:
#define ANALOG_OUT  11
#define MAX 255
#define MIN 0

void setup() {
  pinMode(ANALOG_OUT, OUTPUT);
}

void loop() {
  analogWrite(ANALOG_OUT, MAX);
  analogWrite(ANALOG_OUT, MIN);
}

Photo27:Arduino Unoの結果

Photo28:Gallileo Gen2の結果

Photo29:Genuino 101の結果

ここで驚くべきは、Galileo Gen2のPWM出力の遅さである。Photo28の右の表記を見ると分かるのだが、Arduino UnoやGenuino 101はμsのオーダーの波形なのに対し、Galileoはmsオーダーになっている。

Total LED On LED Off Loop Process
Time
(μs)
Freq
(KHz)
Time
(μs)
Freq
(KHz)
Time
(μs)
Freq
(KHz)
Time
(μs)
Freq
(KHz)
Arduino Uno 25.324 39.488 12.402 80.632 12.921 77.393 0.519 1.927
Galileo Gen2 5653.000 0.177 1996.000 0.501 3657.000 0.273 1.661 0.602
Genuino 101 7.586 131.822 2.610 383.142 4.773 209.512 2.163 0.462

表2でもこれは明らかで、実際Galileo Gen2ではanalogWrite()には2msほどを要する計算になる。ただGalileo Gen2の場合、PWMが外付け(初代GalileoはI/Oも全部外付けだったが、あまりの遅さにGen2ではQuarkの内蔵GPIOを利用する様に変更されたことで高速化が実現した)だから、これは致し方ないところか。

それはともかくArduino UnoとGenuino 101の比較はDigital Outputの場合とほぼ同じで、analogWrite()はGenuino 101が4倍ほど高速、Loop ProcessはやはりArduino Unoが5倍ほど高速という結果になっている。

Analog Input性能

次はどのくらいのサンプリング速度でADCが読み取りをできるかの比較である。比較の方法はちょっと悩んだのだが、最終的には信号発生器を利用して三角波を生成(Photo30)。これをArduinoのAnalog Inputに入れた(Photo31)うえで、List 3の様なSketchを使い、一定量内部バッファに溜め込み、結果をまとめてUSB経由で転送してExcelにプロットすることで、どの程度の頻度でサンプリングできるかを確認することにした。

Photo30:信号発生器は秋月電子通商で販売されているFG085を利用。これで1KHz周期、振幅5Vの三角波を出力している。波形があまり綺麗でないのは、5400円の機材だからご容赦いただきたく

Photo31:信号発生器の出力はインピーダンス50Ωを想定しているので、100Ωの抵抗2本を並列に繋いで、その両端の電圧をArduinoのA0ポートに入力

List 3:
#define ADC_IN  A0
#define BUFSIZE 512
unsigned short  readValue[BUFSIZE];

void setup() {
  Serial.begin(9600);
}

void loop() {
  int lpCnt;
  for(lpCnt = 0; lpCnt < BUFSIZE; lpCnt++)
  {
    readValue[lpCnt]=(unsigned short)analogRead(ADC_IN);
  }

  Serial.println("*****");
  for(lpCnt = 0; lpCnt < BUFSIZE; lpCnt++)
  {
    Serial.print(readValue[lpCnt]);
    Serial.print(",");
  }
  Serial.println("");
}

グラフ1~3がその結果である。値のバラつきが多いのは、何しろサンプル数が少ないからという事情もあるのだが、そもそもの信号波形もあまり綺麗とは言いがたいので、その影響もある。

一応Excelでは頭から順にデータを読んで、値が下がったら次の波が来たと判断してサンプル位置を戻すという処理を行っている。さて、値のバラつきもさることながら、Arduino UnoやGalileo Gen2ではとにかく絶対的なサンプル数の少なさが問題といえる。

そこで、各々のサンプル位置で何サンプル取得できたかをまとめたのがグラフ4である。

三角波の周波数が1KHz(周期1ms)であることを考えると、それぞれのサンプル性能は次のようになる

  • Arduino Uno:サンプル位置7までは100%(840)、サンプル位置8が92.0%(773)となり、平均サンプル数は1msあたり8.92回、平均サンプル間隔は約112.1μs
  • Galileo Gen2:サンプル位置3までは100%(2143)、サンプル位置4が49.9%(1069)となり、平均サンプル数は1msあたり4.50回、平均サンプル間隔は約222.2μs
  • Genuino 101:サンプル位置33までは100%(210)、サンプル位置34が99.5%(209)、サンプル位置35が70.0%(147)となり、平均サンプル数は1msあたり34.7回、平均サンプル間隔は約28.8μs

Galileo Gen2は外付けADCの割に頑張ったというべきかもしれないが、それでも4.5KHz程度。Arduino Unoで9KHz弱であり、これがGenuino 101では34.7KHzと大幅に高速化されているのは、アナログセンサーの接続を考える場合には嬉しいところだ。

もう一つGenuino 101でうれしいのは、内蔵SRAMが大幅に増えたこと。Arduino UnoでこのSketchをコンパイルすると、こんな警告メッセージが出る(Photo32)。ADCは10bitで値を取得するから、普通に使うとどうしてもShort型(16bit)を要するので、512個分で1KBとSRAMを半分使い切ってしまう。

Photo32:頑張って構造体を使って10bitのFieldとかを用意すれば、もう少し効率は上げられるが、露骨に遅くなりそうなので今回は見送った

ところがGenuino 101は24KBもあるから、もっと多数のバッファを格納用に利用できる。もちろん、絶対量としてはまだ少ないので、長期間の保存にはSDCard Shieldなどを併用して外部のストレージに保存するといった方策を取る必要があるが、利用できるバッファが大きくなったのはプログラミングする側をしては非常にありがたい。

Digital Input性能

同様に、Digital Inputの性能も測定してみる。今度は信号発生器の出力を1KHzの矩形波に切り替えて、その波形をサンプリングしてみた。当然ながら値は0か1しかない形だ。配線は概ねPhoto31に近いが、入力ポートは13番を利用している。

SketchはList 4の通りだ。基本1bitのデータなので、ちゃんとBitField操作をすれば1KBのSRAMで8192個のデータが取得できる計算になるが、どう考えてもサンプル速度に影響が出そうなので、素直にcharで受けて格納している。

List 4:
#define DIGITAL_IN  13
#define BUFSIZE 1024
unsigned char readValue[BUFSIZE];

void setup() {
  Serial.begin(9600);
  pinMode(DIGITAL_IN, INPUT_PULLUP);
}

void loop() {
  int lpCnt;
  for(lpCnt = 0; lpCnt < BUFSIZE; lpCnt++)
  {
    readValue[lpCnt]=(unsigned char)digitalRead(DIGITAL_IN);
  }

  Serial.println("*****");
  for(lpCnt = 0; lpCnt < BUFSIZE; lpCnt++)
  {
    Serial.print(readValue[lpCnt]);
    Serial.print(",");
  }
  Serial.println("");
}

グラフ5~7がそれぞれのサンプリング結果をまとめたものである。ちなみにサンプル位置は、値が0→1に変わったところをサンプル位置0にしている。

これをグラフ4同様に、サンプル位置毎の頻度として示したのがグラフ8である。

ここでは、値が1の場合のみをまとめてみた。結果はどうなったかというと、

  • Arduino Uno:サンプル位置110までは100%(21)、そこから急速に数を減らし、最大サンプル位置は114となる。平均サンプル数は0.5msあたり112.9回、平均サンプル間隔は約4.43μs
  • Galileo Gen2:サンプル位置497までは100%(6)、サンプル位置499が最大で、平均サンプル数は0.5msあたり499.8回、平均サンプル間隔は約1.0μs
  • Genuino 101:サンプル位置291までは100%(8)、サンプル位置380が87.5%(7)、サンプル位置381が12.5%(1)となり、平均サンプル数は0.5msあたり381.1回、平均サンプル間隔は約1.31μs

ということになる。やはりADCを介さない分、どのボードでも高速に取り込みが可能で、Arduino Unoですら225KHz、Galileoでは1MHz近い取り込みができる。Genuino 101は、さすがにGalileoには追いつかないが、それでも760KHz近いサンプリング速度を実現できており、Arduino Unoの3倍以上の効率が期待できる。

プロセッサ演算性能

最後に、プロセッサそのものの演算性能も一応比較しておく。本当ならばEEMBCのCoreMarkとかULPBenchを動かすべきなのだろうが、CoreMarkはメモリ不足でArduino Unoではとても動かないし、ULPBenchを行うために必要なEnergyMonitor HARDWAREもない(EEMBCで販売しているのだが、現在売り切れ中)ということで今回は見送って、なつかしのDhrystone/Whetstoneを実施してみた。

ソースそのものは普通に手に入るのだが、今回ちょっと調べたらGareth Halfacree氏がGitHubでArduino用のDhrystoneWhetstoneを公開して下さっていたので、ありがたくこれを利用させていただいた。

さて、Dhrystoneの方は、dhry.hをファイルとして落とし、Sketchと同じ場所におく。そして、dhry21a.inoの中身をそのままSketchにコピーする(ファイルとして落とし、Arduino IDEで開いても良い)。Whetstoneの方はヘッダファイルはないので、whetstone.inoをコピーするだけだ。Dhrystoneの方は、これをそのままビルドするとちょっと怖い警告メッセージが出てくるが(Photo33)、実行には支障がない。

Photo33:警告メッセージが出てくるが、特に問題なく実行できた

ただ、これをこのまま実行すると、Arduino Unoには手ごろだが、Galileo Gen2で実行するとあまりに実行時間が短すぎてベンチマークが成立しない(Photo34)。

Photo34:これはDhrystoneのもともとある仕様で、ベンチマークが2秒未満で終わってしまう場合には十分な時間動いてないとしてエラーで終了する

そこでsetup()の中にある

Number_Of_Runs = 300000;

Number_Of_Runs = 3000000;

のように、一桁増やして実行した。Arduino Unoだと結構実行時間がかかるが、ちゃんと動作する。どちらのベンチマークも、起動後にシリアルモニタを開くと、結果が表示される(Photo35,36)。

Photo35:これがGalileo Gen2での実行結果の例

Photo36:Whetstoneはこんな具合に延々とベンチマークを実行し続ける

結果であるが、まずDhrystone(グラフ9)、縦棒(左軸)が毎秒何千回Dhrystoneを実行したかの結果、折れ線(右軸)がVAX DMIPS(VAX-11/780でのスコアを1とした時の相対性能)である。とりあえずGalileo Gen2が飛びぬけて高速なのは予想の範囲として、Genuino 101はArduino Unoのほぼ4倍近い整数演算性能を持つという結果になった。

一方、Whetstone(グラフ10)は、縦棒(左軸)がWhetstoneを1回実施するのに必要な経過時間(秒)、折れ線(右軸)がMFLOPS値となる。面白いのは、Genuino 101はArduino Uno R3よりもむしろ浮動小数点演算では劣る(36%ほど低い)結果になっていることだ。

Arduino UnoのATMega328にはFPUは内蔵されていないし、Genuino 101のCurieの方も、ARCコアをFPUなしで組み込んでいるようで、なので基本どちらもソフトウェアでの処理になるが、これに関してはGenuino 101はあまり期待できないという結果になった。例えばセンサーデータをフィルタリングしてから送り出す、なんて用途の場合には整数演算でそれを実装する方法を考えたほうがいいかもしれない。