それでは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);
}
数字をまとめたのが表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);
}
ここで驚くべきは、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円の機材だからご容赦いただきたく |
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を半分使い切ってしまう。
ところが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用のDhrystoneとWhetstoneを公開して下さっていたので、ありがたくこれを利用させていただいた。
さて、Dhrystoneの方は、dhry.hをファイルとして落とし、Sketchと同じ場所におく。そして、dhry21a.inoの中身をそのままSketchにコピーする(ファイルとして落とし、Arduino IDEで開いても良い)。Whetstoneの方はヘッダファイルはないので、whetstone.inoをコピーするだけだ。Dhrystoneの方は、これをそのままビルドするとちょっと怖い警告メッセージが出てくるが(Photo33)、実行には支障がない。
ただ、これをこのまま実行すると、Arduino Unoには手ごろだが、Galileo Gen2で実行するとあまりに実行時間が短すぎてベンチマークが成立しない(Photo34)。
そこでsetup()の中にある
Number_Of_Runs = 300000;
を
Number_Of_Runs = 3000000;
のように、一桁増やして実行した。Arduino Unoだと結構実行時間がかかるが、ちゃんと動作する。どちらのベンチマークも、起動後にシリアルモニタを開くと、結果が表示される(Photo35,36)。
結果であるが、まず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はあまり期待できないという結果になった。例えばセンサーデータをフィルタリングしてから送り出す、なんて用途の場合には整数演算でそれを実装する方法を考えたほうがいいかもしれない。