Raspberry Pi Pico(ラズパイPico)のデュアルコア(multicore)を使って並列処理・逐次処理の計算速度を比較してみました。
ラズパイPicoはArduinoIDEから使う前提です。ArduinoIDEからならマルチコアも割と簡単に使えます。
▼ラズパイPicoでデュアルコアを使う方法▼
ライプニッツの公式からの円周率計算をシングル/デュアルでそれぞれ計算して、処理速度を比較してみたいと思います。
\begin{align} \sum_{i=0}^\infty \left(-1\right)^{n-1}\frac{1}{2n-1}=\frac{1}{1}-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\frac{1}{9}-\cdots = \frac{\pi}{4}\\\\ \end{align}▲奇数の逆数をとって、和と差を繰り返していくと円周率の1/4に近づいていく公式です。この円周率計算の処理速度を比較してみます。
まずはシングルコア/逐次処理で普通にプログラムして処理速度を計測。(2023/05⊿1:シリアル通信が開始されるまでプログラム待機されるようにソース修正。シリアル通信が開始されるまでオンボードLEDを点滅しながら待機、通信開始後にプログラムが起動するよう変更)
// 2021/06/01 imo lab. // 2023/05/03 起動待機処理追加 // https://garchiving.com/ uint32_t core0_Timer, core0_dt; double s; void setup() { Serial.begin(115200); pinMode(25, OUTPUT); while (!Serial) { digitalWrite(25, digitalRead(25) == LOW ? 1 : 0); delay(200); } delay(1000); Serial.printf("core0:start....\n\n"); } void loop() { core0_Timer = micros(); s = 0; for (uint32_t n = 1; n <= 1000000; n++) { if (n % 2 == 1) s += 1.0 / (2 * n - 1.0); if (n % 2 == 0) s -= 1.0 / (2 * n - 1.0); } s *= 4.0; core0_dt = micros() - core0_Timer; Serial.printf("%+5s%.5f\n", "ans:", s); Serial.printf("time:%7d ms\n\n", core0_dt / 1000); }
▲Arduino言語でそのままラズパイPicoへ書き込み可能です。和と差を百万回繰り返して円周率を計算してます。
▼結果▼
ざっくりした時間計測ですが、シングルコアでおよそ4秒ほどの処理時間です。ちなみにこのソースをUNOで動かすと52秒ほどかかりました。
続いて並列処理(デュアルコア)です。(2023/05⊿1:シリアル通信が開始されるまでプログラム待機されるようにソース修正。シリアル通信が開始されるまでオンボードLEDを点滅しながら待機、通信開始後にプログラムが起動するよう変更)
// 2021/06/01 imo lab. // 2023/05/03 起動待機処理追加 // https://garchiving.com/ uint32_t core0_Timer, core0_dt; uint32_t core1_Timer, core1_dt; uint32_t total_Timer, elapsedTime; volatile bool F; double s0, s1, s; //core0////////////////////////////////////////////////////////////////////// void setup() { Serial.begin(115200); pinMode(25, OUTPUT); while (!Serial) { digitalWrite(25, digitalRead(25) == LOW ? 1 : 0); delay(200); } delay(1000); Serial.printf("core0:start....\n"); } void loop() { total_Timer = micros(); core0_Timer = total_Timer; for (uint32_t n = 1; n <= 1000000; n++) { if (n % 2 == 1) s0 += 1.0 / (2 * n - 1.0); } core0_dt = micros() - core0_Timer; while (!F); s = (s0 + s1) * 4; elapsedTime = micros() - total_Timer; Serial.printf(" core0 time:%6d ms%7s%9.5f\n", core0_dt / 1000, "s0:", s0); Serial.printf(" core1 time:%6d ms%7s%9.5f\n", core1_dt / 1000, "s1:", s1); Serial.printf("elapsed time:%6d ms%7s%9.5f\n\n\n", elapsedTime / 1000, "ans:", s); while (1); } //core1////////////////////////////////////////////////////////////////////// void setup1() { while (!Serial) { //digitalWrite(25, digitalRead(25) == LOW ? 1 : 0); delay(200); } delay(1000); Serial.printf("core1:start....\n\n"); } void loop1() { core1_Timer = micros(); double temp = 0; for (uint32_t n = 1; n <= 1000000; n++) { if (n % 2 == 0) temp -= 1.0 / (2 * n - 1.0); } s1 = temp; F = 1; core1_dt = micros() - core1_Timer; while (1); }
少し見難いですが、積み上げ計算の和の部分と差の部分をcore0/core1で分担して計算処理してます。互いの積み上げ計算が完了したら、core0 で最終処理をして円周率を計算してます。
▼結果▼
▲2秒ほどで計算終了。シングルコアの処理と比較しておよそ1.8倍ほどの速度向上。core0のほうが若干早く演算が終了し、core1の終了待ってる感じですかね。繰り返し処理を単純に2coreで分担しているので処理時間としては、おおむね想定通りです。
デュアルコアですが思いのほか簡単に使えますね。ただ残念ながら何に使えばいいのか、あまりいい使い道が思い浮かびません。Picoでドローン作るのも面白そうですね。
コメント