
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でドローン作るのも面白そうですね。


コメント