ESP32でASCIIART(マンデルブロ集合)ベンチマークを実行
◆まえがき
ESP32を使ってArduinoの環境でのASCIIART(マンデルブロ集合)というベンチマークテストをやってみました。きっかけとなったのは、「電子的遺物の世界へようこそ」のサイトのMN1613でマンデルブロ集合という記事でMN1630をアセンブラで動かした場合の結果が公開されていたからです。
このテストは、「はせりんさん」が 番外編:ASCIIART(マンデルブロ集合)ベンチマークというサイトで結果を整理されているのですが、黎明期のパソコンの性能比較を行うために使われていてBASICインタープリターを使うのが前提になっているようです。そんなことで、Cコンパイラを使うArdunoを持ち込むのは場違いなのですが、性能の比較は出来るはずなのでともかくやってみました。
ちなみにこのテストは以前に Arduino UNOを使ってやったことがあります。
◆テストに使ったESP32

ESP32 DEVKIT V1を使いました。D2から出ている線は波形観察用です。
◆プログラム
UNOでテストした時とほとんど同じですが全体を掲載しておきます。
◆実行結果
本来なら、実行時間はxx秒で、UNOを使った場合のNN倍速くなりましたと書きたかったのですが、話はそんなに簡単ではありませんでした。実は何が起こっているのか完全に理解出来ていないのですが順に説明していきます。
1)プログラムの実行時間
画面の最後にマンデルブロ集合を1画面出力するためにかかった時間を出力させています。

1画面を出力する時間は175381μs、つまり約0.175秒でした。Arduino UNOでは1.032秒だったので単純計算では5倍高速になっていることになります。
但しこの時間にはシリアルを出力する時間が含まれていてCPUの演算速度だけを表わしている訳ではありません。そのあたりを明らかにするためにもう少し調べてみました。
2)オシロを使って速度測定
digitalWrite() を使ってIOピンに信号を出すことで、実行時間を測定してみました。具体的には、マンデルブロ集合の計算を行っている部分の実行時間が判るようにするために、プログラムの30行目でD2pin(内蔵LEDピン)にHIGHを出し、51行目でLOWを出力するようにしました。なお、この測定を行う時にはプログラムのシリアルの出力は全てコメントアウトして無効にしておきます。
3)測定結果

280nsくらいの周期でループを回っています。マンデルブロ集合の1画面を描くためのループの実行回数を変数のtotalCyclesを使って数えると10379回だったので、1画面の計算時間は 0.28μs*10379回 = 2.91ms ということになります。
追記:この値は間違ってたようです。詳しくは、次の記事参照下さい。
これめちゃめちゃ速くて嘘っぽい気もするのですが、ともかく計算上はこうなります。
ちなみに、この測定にはdigitalWrite() 実行時間が含まれているので本当はもっと速いのかも知れません。
ともかくこんなに速いので、シリアルに結果が出るスピードを測ったのでは性能測定にならないということは確かです。
4)計算時間の再確認
上記の計算で求めた実行時間をタイマーの値などを参照することでソフト上から測定しようとしたのですが、なぜか上手く行きませんでした。これは推定ですが、シリアルへの出力やタイマーの値の参照などの操作は裏のプロセスで行われていて、表側の処理とは完全に同期されていないのかも知れません。
5)Arduino UNOの波形
同じ条件でArduino UNOの波形を見てみました。

1サイクル7μsくらいかかっています。ESP32と比べると速度は1/25ということになりますが8ビットCPUなのでこんなものでしょうか。あと、3)項にも書きましたがこの測定にはdigitalWrite()の実行時間が含まれているので、実際にはもう少し速いのだと思います。
◆まとめ
オシロで波形測定やってみたものの、digitalWrite()の実行時間が含まれているので本当の時間は良く判らない。ソフトで実行時間を測定しようとしたけどこっちも変な結果が出てしまって良く判らないということで、なんとも締まりの無い結果になってしまいました。ともかく、この記事のプログラムでESP32の性能測定を行うことは難しそうです。
実はここに書いた以外にいろいろやってみたのですが、結果が上手く説明出来ないでいます。プログラムのコードの一部はメインの流れとは非同期に裏のプロセスで実行されているのかも知れません。
ASCIIART(マンデルブロ集合)のベンチマークは、画面へ結果を出力する性能まで含んだテストになっている訳で、パソコン全体の性能を測るテストになっています。それをこの記事でやったように、出力をシリアルに出すように改造すると、シリアルの性能に足を引っ張られてしまってCPU本来の性能が見えなくなってしまうことがある、ということでした。
ESP32を使ってArduinoの環境でのASCIIART(マンデルブロ集合)というベンチマークテストをやってみました。きっかけとなったのは、「電子的遺物の世界へようこそ」のサイトのMN1613でマンデルブロ集合という記事でMN1630をアセンブラで動かした場合の結果が公開されていたからです。
このテストは、「はせりんさん」が 番外編:ASCIIART(マンデルブロ集合)ベンチマークというサイトで結果を整理されているのですが、黎明期のパソコンの性能比較を行うために使われていてBASICインタープリターを使うのが前提になっているようです。そんなことで、Cコンパイラを使うArdunoを持ち込むのは場違いなのですが、性能の比較は出来るはずなのでともかくやってみました。
ちなみにこのテストは以前に Arduino UNOを使ってやったことがあります。
◆テストに使ったESP32

ESP32 DEVKIT V1を使いました。D2から出ている線は波形観察用です。
◆プログラム
UNOでテストした時とほとんど同じですが全体を掲載しておきます。
// マンデルブロ集合をシリアルに出力
// BASICで書かれているベンチマークプログラムをArduino用(C++)に書き換えた
// 参考サイト:ASCIIART(マンデルブロ集合)ベンチマーク http://haserin09.la.coocan.jp/asciiart.html
// 2021/11/26 ラジオペンチ http://radiopench.blog96.fc2.com/
#define monPin 2 // モニターピン(ESP32 DEVKIT V1)
//#define monPin 13 // モニターピン(UNO)
#define NN 10 // 実行回数
void setup() {
byte r;
int i, x, y;
float a, b, ca, cb, t;
unsigned long t0;
unsigned long totalCycles;
pinMode(monPin, OUTPUT);
digitalWrite(monPin, LOW);
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.flush();
t0 = micros();
Serial.println(micros());
for (int j = 0; j < NN; j++) {
Serial.println(j); // 計算回数を表示
totalCycles = 0;
for (y = -12; y <= 12; y++) { // y方向は-12から+12
for (x = -39; x <= 39; x++) { // X方向に一つづつ
digitalWrite(monPin, HIGH);
ca = x * 0.0458;
cb = y * 0.08333;
a = ca;
b = cb;
for (i = 0; i <= 15; i++) { // 1座標毎に16回下記の計算を行い、
t = a * a - b * b + ca;
b = 2.0 * a * b + cb;
a = t;
if ((a * a + b * b) > 4.0) { // 値が4以上だったら発散したとみなして
break; // ループを抜ける
}
}
// totalCycles += i; // 計算回数を累積
if (i > 15) { // イタレーションが16回以上なら
r = 0x20; // スペース
} else if (i > 9) { // 10回以上なら
r = 0x41 + i - 10; // A-Fの文字コード
} else { // それ以外(10未満)なら
r = 0x30 + i; // 0-9の文字コード
}
digitalWrite(monPin, LOW);
Serial.write(r); // 結果の文字を出力
} // next x
Serial.println(); // Xの行末で改行
} // next y
Serial.println(); // ブロックの終わりで改行
}
Serial.print((micros() - t0) / NN); Serial.println("us par cycle");
} // setup
void loop() {
}
◆実行結果
本来なら、実行時間はxx秒で、UNOを使った場合のNN倍速くなりましたと書きたかったのですが、話はそんなに簡単ではありませんでした。実は何が起こっているのか完全に理解出来ていないのですが順に説明していきます。
1)プログラムの実行時間
画面の最後にマンデルブロ集合を1画面出力するためにかかった時間を出力させています。

1画面を出力する時間は175381μs、つまり約0.175秒でした。Arduino UNOでは1.032秒だったので単純計算では5倍高速になっていることになります。
但しこの時間にはシリアルを出力する時間が含まれていてCPUの演算速度だけを表わしている訳ではありません。そのあたりを明らかにするためにもう少し調べてみました。
2)オシロを使って速度測定
digitalWrite() を使ってIOピンに信号を出すことで、実行時間を測定してみました。具体的には、マンデルブロ集合の計算を行っている部分の実行時間が判るようにするために、プログラムの30行目でD2pin(内蔵LEDピン)にHIGHを出し、51行目でLOWを出力するようにしました。なお、この測定を行う時にはプログラムのシリアルの出力は全てコメントアウトして無効にしておきます。
3)測定結果

280nsくらいの周期でループを回っています。マンデルブロ集合の1画面を描くためのループの実行回数を変数のtotalCyclesを使って数えると10379回だったので、1画面の計算時間は 0.28μs*10379回 = 2.91ms ということになります。
追記:この値は間違ってたようです。詳しくは、
これめちゃめちゃ速くて嘘っぽい気もするのですが、ともかく計算上はこうなります。
ちなみに、この測定にはdigitalWrite() 実行時間が含まれているので本当はもっと速いのかも知れません。
ともかくこんなに速いので、シリアルに結果が出るスピードを測ったのでは性能測定にならないということは確かです。
4)計算時間の再確認
上記の計算で求めた実行時間をタイマーの値などを参照することでソフト上から測定しようとしたのですが、なぜか上手く行きませんでした。これは推定ですが、シリアルへの出力やタイマーの値の参照などの操作は裏のプロセスで行われていて、表側の処理とは完全に同期されていないのかも知れません。
5)Arduino UNOの波形
同じ条件でArduino UNOの波形を見てみました。

1サイクル7μsくらいかかっています。ESP32と比べると速度は1/25ということになりますが8ビットCPUなのでこんなものでしょうか。あと、3)項にも書きましたがこの測定にはdigitalWrite()の実行時間が含まれているので、実際にはもう少し速いのだと思います。
◆まとめ
オシロで波形測定やってみたものの、digitalWrite()の実行時間が含まれているので本当の時間は良く判らない。ソフトで実行時間を測定しようとしたけどこっちも変な結果が出てしまって良く判らないということで、なんとも締まりの無い結果になってしまいました。ともかく、この記事のプログラムでESP32の性能測定を行うことは難しそうです。
実はここに書いた以外にいろいろやってみたのですが、結果が上手く説明出来ないでいます。プログラムのコードの一部はメインの流れとは非同期に裏のプロセスで実行されているのかも知れません。
ASCIIART(マンデルブロ集合)のベンチマークは、画面へ結果を出力する性能まで含んだテストになっている訳で、パソコン全体の性能を測るテストになっています。それをこの記事でやったように、出力をシリアルに出すように改造すると、シリアルの性能に足を引っ張られてしまってCPU本来の性能が見えなくなってしまうことがある、ということでした。