Arduino言語との速度比較のために、Rustでループ処理部分をベンチマークしてみた
プラットホームはembassyを使って、そのソースコードに埋め込み、表示パターンは階調表示のあるテストパターン、routine.rsは表示データの初期化を行うだけですが
コードは二つに分割
<main.rs>
//
// % cargo run --bin raspico
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_rp::gpio::{Level, Output};
use {defmt_rtt as _, panic_probe as _};
mod routine;
use routine::DispData;
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let mut r1 = Output::new(p.PIN_2, Level::High);
let mut r2 = Output::new(p.PIN_5, Level::High);
let mut g1 = Output::new(p.PIN_3, Level::High);
let mut g2 = Output::new(p.PIN_8, Level::High);
let mut b1 = Output::new(p.PIN_4, Level::High);
let mut b2 = Output::new(p.PIN_9, Level::High);
let mut a = Output::new(p.PIN_10, Level::Low);
let mut b = Output::new(p.PIN_16, Level::Low);
let mut c = Output::new(p.PIN_18, Level::Low);
let mut d = Output::new(p.PIN_20, Level::Low);
let _e = Output::new(p.PIN_22, Level::Low);
let mut clk = Output::new(p.PIN_11, Level::Low);
let mut lat = Output::new(p.PIN_12, Level::High);
let mut oe = Output::new(p.PIN_13, Level::High);
let a_array: [u8; 16] = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1];
let b_array: [u8; 16] = [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1];
let c_array: [u8; 16] = [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1];
let d_array: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1];
let mut disp_data = DispData {
_array64: [0; 64],
array4d: [[[[0; 64]; 32]; 3]; 2],
};
let new_disp_data = disp_data.new();
loop {
for i in 0..16 {
// row number
if a_array[i] == 0 {
a.set_low();
} else {
a.set_high();
};
if b_array[i] == 0 {
b.set_low();
} else {
b.set_high();
};
if c_array[i] == 0 {
c.set_low();
} else {
c.set_high();
};
if d_array[i] == 0 {
d.set_low();
} else {
d.set_high();
};
for b in 0..15 {
// shading
lat.set_high();
for k in 0..64 {
// column data
clk.set_low();
//
if new_disp_data.array4d[0][2][i][k] > b {
r1.set_high();
} else {
r1.set_low();
}
if new_disp_data.array4d[0][2][16 + i][k] > b {
r2.set_high();
} else {
r2.set_low();
}
if new_disp_data.array4d[0][1][i][k] > b {
g1.set_high();
} else {
g1.set_low();
}
if new_disp_data.array4d[0][1][16 + i][k] > b {
g2.set_high();
} else {
g2.set_low();
}
if new_disp_data.array4d[0][0][i][k] > b {
b1.set_high();
} else {
b1.set_low();
}
if new_disp_data.array4d[0][0][16 + i][k] > b {
b2.set_high();
} else {
b2.set_low();
}
clk.set_high();
}
oe.set_high();
lat.set_low();
oe.set_low();
}
}
}
}
<routine.rs>
表示データの作成をするだけ、
static ARRAY64: [u8; 64] = [
0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3, 2, 1, 1, 0, 0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3, 2, 1,
1, 0, 0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3, 2, 1, 1, 0, 0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3,
2, 1, 1, 0,
];
pub struct DispData {
pub _array64: [u8; 64],
pub array4d: [[[[u8; 64]; 32]; 3]; 2],
}
impl DispData {
pub fn new(&mut self) -> Self {
let mut dis_data = DispData {
_array64: ARRAY64,
array4d: [[[[0; 64]; 32]; 3]; 2],
};
for j in 0..3 {
for i in 0..32 {
dis_data.array4d[0][j][i] = ARRAY64;
}
}
dis_data
}
}
<実行速度>
およそ10ms(Arduino言語ではおよそ14msだから3割程度高速化)、繰り返し(メモリ読み出しとgpio書き込み)回数は一回のリフレッシュループで、
64*3*16*32 = 98,304(16行*3色*パネル上下*16階調)ということで一回のr/g/bデータの書き込みでおよそ1μs費やしている勘定で、クロック数ではおよそ130クロックとなります、階調処理での条件分岐が処理時間では大きそうですね
写真は行アドレス信号のDなので一周期がリフレッシュ周期
結果だけ見るとRustの優位性はそれほど無いように見えるかもしれないけど、メモリ管理やコンパイラの出来の良さ、複数タスク管理をasync/awaitで簡単に実現できるから他の言語で実現(Arduino言語ならRTOS使うことになるけれども、async/awaitのほうが軽量だと)するよりもアドバンテージはあると思う
P.S. 2024/12/24
この程度(3割)の改善ならば、pico 2を使えば簡単に逆転できそうだよね、消費電力とか別にすればね、ハードの進化は偉大なりか
admin