
概要
M5Stack Core 2 (ESP32)には異なる種類のメモリが存在していて、それぞれの目的に応じて使用されています。メモリの種類によって転送速度にどのくらいの違いがあるのか、また、M5GFXを利用したグラフィックの処理で、スプライトを出力する pushSprite() がどう影響するのかを調べてみました。
2025-06-15 ベンチマークテスト (2-B) 追加しました
きっかけ
大量のスプライトを描画するプログラムを作成していたのですが、どうしても期待するパフォーマンスを得られず、どのくらいの速度が出るのかと気になってきました。createCanvas()やpushSprite()で実験をしていたところ、画像のサイズによって挙動が変わるポイントがあり、メモリのしくみについて調べることにしました。
ESP32のメモリは非常に複雑怪奇で、正直うまく理解できていません。以下のブログがとても詳しく解説しているので、こちらを参考にしてみてください。
ESP32のメモリレイアウトの理解と最適化
https://zenn.dev/paradoia/articles/ce34af18e74392
今回扱うメモリの種類
ESP32のメモリには大きく分けて、内蔵メモリと外部メモリがあります。内蔵メモリはプログラムが動作する領域のほか、データを保存する領域やその両方を兼ねた領域があります。データを保存する領域にはDMA対応の部分とそうでない部分があり、DMAではCPUを介さずに直接メモリ間で転送できるので高速なアクセスが可能です。
外部メモリにはPSRAM (SPI RAM)があり、M5Stack Core2 では8MBが使用可能です。また、プログラム中で const xxx PROGMEM のように変数を記述すると、読み出し専用のFlashメモリの方に保存されるようになります。容量の大きい画像データなどをFlash領域に保存すると、メモリを圧迫せずに済むので便利です。
ベンチマーク内容
今回は以下のテストを実施しました。
(1) 確保したメモリがどの領域に作成されたのか
malloc()で実際にどの領域にメモリを確保したのか、サイズごとに調査しました。
(2) メモリコピーの時間計測
(2-B) メモリコピーの時間計測 ☆追記☆
内部メモリ(DMAあり)、内部メモリ(DMAなし)、PSRAM、FLASH、それぞれの領域間での転送速度を調査しました。またサイズによって違いがあるのかテストしました。
(3) canvas.createSprite()するサイズで使用するメモリの場所が変わるかどうか
元々スプライトの描画パフォーマンスを調べていて今回のテストに至りました。作成するサイズで違いがあるのかをテストしました。
(4) pushSprite()の時間計測
内部メモリ、内部メモリ(PSRAM、FLASH)、ディスプレイ、それぞれの領域間でのスプライトの転送速度を、画像のサイズ別に調査しました。また、pushSprite()で透明色を指定した場合の違いについても調査しました。
今回使用したプログラム
今回ベンチマークに使用したプログラムは GitHub にアップしました。
表に出てくる名称の説明
“Type” について
今回のベンチマーク結果の表で Type とある行は、どのような方法でメモリを確保したかを意味しています。
表中の名称 | 実際の命令 |
---|---|
malloc() | malloc() |
ps_malloc() | ps_malloc() |
MALLOC_CAP_INTERNAL | heap_caps_malloc(size, MALLOC_CAP_INTERNAL) |
MALLOC_CAP_DMA | heap_caps_malloc(size, MALLOC_CAP_DMA) |
MALLOC_CAP_SPIRAM | heap_caps_malloc(size, MALLOC_CAP_SPIRAM) |
malloc()はメモリの種類を指定せずにメモリを確保する関数で、ps_malloc()はPSRAMにメモリを確保します。またheap_caps_malloc()はメモリの種類を指定して確保します。
“From/To/Location” について
今回のベンチマーク結果で From, To, Location とある行は、メモリが実際にどこに確保されたかを意味しています。これはメモリのアドレスを元に、どこに確保されたかを判定しています。たとえば以下のようなものです。
in_SRAM_1_DMA /Data
in_SRAM_2_DMA /Data
in_SRAM_0_none /Inst
ex_SRAM_rw /Data
ex_Flash_r_a /Data
こちらの表の説明を短くしたもので、先頭の in_ は内部メモリ(internal)、ex_ は外部メモリ(external)、最後の /data はデータ用の領域、/inst は命令用の領域を意味しています。間の SRAM_0,1,2 は3つあるSRAMのどれかで、_a,b,c はさらに細かく分かれたエリアを示しています。_r,rwは読み込み専用かR/W可かです。簡潔に表記しようとしたところ、余計にわかりづらくなってしまいました。ほんと意味不明な書き方ですいません。
プログラムを開く メモリ判定ルーチン
String get_memory_region(void* ptr) {
String region = "";
uint32_t addr = (uint32_t)ptr;
// Embeded Memory
if (addr >= 0x3FF80000 && addr <= 0x3FF81FFF) region = "RTC_FAST_Memory /Data"; // 8KB
else if (addr >= 0x3FF90000 && addr <= 0x3FF9FFFF) region = "in_ROM_1 /Data";// 64KB "Internal ROM 1";
else if (addr >= 0x3FFAE000 && addr <= 0x3FFDFFFF) region = "in_SRAM_2_DMA /Data";// 200KB "Internal SRAM 2 DMA";
else if (addr >= 0x3FFE0000 && addr <= 0x3FFFFFFF) region = "in_SRAM_1_DMA /Data";// 128KB "Internal SRAM 1 DMA";
else if (addr >= 0x40000000 && addr <= 0x40007FFF) region = "in_ROM_0_a /Inst";// 32KB "Internal ROM 0";
else if (addr >= 0x40008000 && addr <= 0x4005FFFF) region = "in_ROM_0_b /Inst";// 352KB "Internal ROM 0";
else if (addr >= 0x40070000 && addr <= 0x4007FFFF) region = "in_SRAM_0_cache /Inst";// 64KB "Internal SRAM 0 Cache";
else if (addr >= 0x40080000 && addr <= 0x4009FFFF) region = "in_SRAM_0_none /Inst";// 128KB "Internal SRAM 0 -";
else if (addr >= 0x400A0000 && addr <= 0x400AFFFF) region = "in_SRAM_1_a /Inst";// 64KB "Internal SRAM 1 (Inst)";
else if (addr >= 0x400B0000 && addr <= 0x400B7FFF) region = "in_SRAM_1_b /Inst";// 32KB "Internal SRAM 1 (Inst)";
else if (addr >= 0x400B8000 && addr <= 0x400BFFFF) region = "in_SRAM_1_c /Inst";// 32KB "Internal SRAM 1 (Inst)";
else if (addr >= 0x400C0000 && addr <= 0x400C1FFF) region = "RTC_FAST_Memory /Inst";// 8KB
else if (addr >= 0x50000000 && addr <= 0x50001FFF) region = "RTC_SLOW_Memory /Data+Isnt";// 8KB
// External Memory
else if (addr >= 0x3F400000 && addr <= 0x3F7FFFFF) region = "ex_Flash_r_a /Data";// 4MB "External Flash";
else if (addr >= 0x3F800000 && addr <= 0x3FBFFFFF) region = "ex_SRAM_rw /Data";// 4MB "External RAM (PSRAM)";
else if (addr >= 0x400C2000 && addr <= 0x40BFFFFF) region = "ex_Flash_r_b /Inst";// 11512KB "External Flash";
else if (addr == 0) region = "Error";
else region = "Other";
return region;
}
ベンチマーク結果
ベンチマーク結果(1) 確保したメモリがどの領域に作成されたのか
malloc()で実際にどの領域にメモリを確保したのか、サイズごとに調査しました。結果を見ると、1152バイトの場合は内部メモリに確保していますが、4608バイト以上では外部のPSRAMに確保してますね。空きメモリは余裕があるのに、わざわざPSRAMを使っているのが不思議なところでした。
Type | Size | Location | Address |
---|---|---|---|
malloc() | 1,152 | in_SRAM_2_DMA /Data | 3FFB2524 |
malloc() | 4,608 | ex_SRAM_rw /Data | 3F800884 |
malloc() | 18,432 | ex_SRAM_rw /Data | 3F800884 |
malloc() | 38,400 | ex_SRAM_rw /Data | 3F800884 |
malloc() | 64,000 | ex_SRAM_rw /Data | 3F800884 |
malloc() | 115,200 | ex_SRAM_rw /Data | 3F800884 |
malloc() | 153,600 | ex_SRAM_rw /Data | 3F800884 |
続いて他の場合も見ていきましょう。「表の続きを見る」をクリックすると表が表示されます。
表の続きを見る
Type | Size | Location | Address |
---|---|---|---|
ps_malloc() | 1,152 | ex_SRAM_rw /Data | 3F800884 |
ps_malloc() | 4,608 | ex_SRAM_rw /Data | 3F800884 |
ps_malloc() | 18,432 | ex_SRAM_rw /Data | 3F800884 |
ps_malloc() | 38,400 | ex_SRAM_rw /Data | 3F800884 |
ps_malloc() | 64,000 | ex_SRAM_rw /Data | 3F800884 |
ps_malloc() | 115,200 | ex_SRAM_rw /Data | 3F800884 |
ps_malloc() | 153,600 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_INTERNAL | 1,152 | in_SRAM_0_none /Inst | 40092148 |
MALLOC_CAP_INTERNAL | 4,608 | in_SRAM_0_none /Inst | 40092148 |
MALLOC_CAP_INTERNAL | 18,432 | in_SRAM_0_none /Inst | 40092148 |
MALLOC_CAP_INTERNAL | 38,400 | in_SRAM_0_none /Inst | 40092148 |
MALLOC_CAP_INTERNAL | 64,000 | in_SRAM_2_DMA /Data | 3FFC6188 |
MALLOC_CAP_INTERNAL | 115,200 | Error | 0 |
MALLOC_CAP_INTERNAL | 153,600 | Error | 0 |
MALLOC_CAP_DMA | 1,152 | in_SRAM_2_DMA /Data | 3FFB2934 |
MALLOC_CAP_DMA | 4,608 | in_SRAM_2_DMA /Data | 3FFB2964 |
MALLOC_CAP_DMA | 18,432 | in_SRAM_2_DMA /Data | 3FFC6188 |
MALLOC_CAP_DMA | 38,400 | in_SRAM_2_DMA /Data | 3FFC6188 |
MALLOC_CAP_DMA | 64,000 | in_SRAM_2_DMA /Data | 3FFC6188 |
MALLOC_CAP_DMA | 115,200 | Error | 0 |
MALLOC_CAP_DMA | 153,600 | Error | 0 |
MALLOC_CAP_SPIRAM | 1,152 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_SPIRAM | 4,608 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_SPIRAM | 18,432 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_SPIRAM | 38,400 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_SPIRAM | 64,000 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_SPIRAM | 115,200 | ex_SRAM_rw /Data | 3F800884 |
MALLOC_CAP_SPIRAM | 153,600 | ex_SRAM_rw /Data | 3F800884 |
PROGMEM | 10 | ex_Flash_r_a /Data | 3F40C274 |
ps_malloc()はPSRAMに確保する命令なので、その通りPSRAMに確保されています。MALLOC_CAP_DMAはDMA転送可能な内部メモリに確保します。115,200バイト以上でエラーになっていますが、これはこのサイズの連続したメモリが確保できなかったからでしょう。以下はプログラム実行時の、空きメモリの量と、確保可能な連続した最大のサイズの結果です。たしかに足らないようです。
MALLOC_CAP_INTERNAL: 318,772 / 110,580
MALLOC_CAP_DMA : 261,756 / 110,580
MALLOC_CAP_SPIRAM : 4,192,124 / 4,128,756
不思議なのが MALLOC_CAP_INTERNAL は64,000バイトで別の場所にメモリを確保している点です。heap_caps_malloc()は場所を指定して確保する関数ですが、優先順位があるんでしょうかね。そのほかPSRAM、PROGMEMについては期待通りの結果でした。
ベンチマーク結果(2) メモリコピーの時間計測
内部メモリ(DMAあり)、内部メモリ(DMAなし)、PSRAM、FLASH、それぞれの領域間での転送速度を調査しました。
From | To | Size | Time(us) | MB/s | Result | From(type) | To(type) |
---|---|---|---|---|---|---|---|
dma | dma | 18,432 | 48 | 366.2 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | internal | 18,432 | 193 | 91.1 | Pass | in_SRAM_2_DMA /Data | in_SRAM_0_none /Inst |
dma | spiram | 18,432 | 51 | 344.7 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
internal | dma | 18,432 | 183 | 96.1 | Pass | in_SRAM_0_none /Inst | in_SRAM_2_DMA /Data |
internal | internal | 18,432 | 328 | 53.6 | Pass | in_SRAM_0_none /Inst | in_SRAM_0_none /Inst |
internal | spiram | 18,432 | 191 | 92.0 | Pass | in_SRAM_0_none /Inst | ex_SRAM_rw /Data |
spiram | dma | 18,432 | 49 | 358.7 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | internal | 18,432 | 198 | 88.8 | Pass | ex_SRAM_rw /Data | in_SRAM_0_none /Inst |
spiram | spiram | 18,432 | 730 | 24.1 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | dma | 18,432 | 73 | 240.8 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 18,432 | 76 | 231.3 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
これを見ると違いが明確に現れますね。
DMA転送は350MB/sくらいで、DMAではない内部メモリの転送は56MB/sほど出ています。PSRAMとPROGMEM (Flash)は意外と速いように見えますが、これはサイズが小さいためキャッシュが影響している可能性があります。もう少し大きいサイズの結果も見てみましょう。「表を開く」をクリックすると全ての結果が表示されます。
表を開く
From | To | Size | Time(us) | MB/s | Result | From(type) | To(type) | 備考 |
---|---|---|---|---|---|---|---|---|
dma | dma | 1,152 | 3 | 366.2 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data | |
dma | dma | 4,608 | 12 | 366.2 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data | |
dma | dma | 18,432 | 48 | 366.2 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data | |
dma | dma | 38,400 | 100 | 366.2 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data | |
dma | dma | 64,000 | 167 | 365.5 | Pass | in_SRAM_2_DMA /Data | in_SRAM_1_DMA /Data | |
dma | dma | 115,200 | 0 | inf | **Fail** | Error | Error | |
dma | dma | 153,600 | 0 | inf | **Fail** | Error | Error | |
dma | internal | 1,152 | 12 | 91.6 | Pass | in_SRAM_2_DMA /Data | in_SRAM_0_none /Inst | |
dma | internal | 4,608 | 48 | 91.6 | Pass | in_SRAM_2_DMA /Data | in_SRAM_0_none /Inst | |
dma | internal | 18,432 | 193 | 91.1 | Pass | in_SRAM_2_DMA /Data | in_SRAM_0_none /Inst | |
dma | internal | 38,400 | 402 | 91.1 | Pass | in_SRAM_2_DMA /Data | in_SRAM_0_none /Inst | |
dma | internal | 64,000 | 167 | 365.5 | Pass | in_SRAM_2_DMA /Data | in_SRAM_1_DMA /Data | *1 |
dma | internal | 115,200 | 0 | inf | **Fail** | Error | Error | |
dma | internal | 153,600 | 0 | inf | **Fail** | Error | Error | |
dma | spiram | 1,152 | 3 | 366.2 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data | |
dma | spiram | 4,608 | 12 | 366.2 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data | |
dma | spiram | 18,432 | 51 | 344.7 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data | |
dma | spiram | 38,400 | 1,331 | 27.5 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data | *2 |
dma | spiram | 64,000 | 4,436 | 13.8 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data | *2 |
dma | spiram | 115,200 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data | |
dma | spiram | 153,600 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data | |
internal | dma | 1,152 | 11 | 99.9 | Pass | in_SRAM_0_none /Inst | in_SRAM_2_DMA /Data | |
internal | dma | 4,608 | 45 | 97.7 | Pass | in_SRAM_0_none /Inst | in_SRAM_2_DMA /Data | |
internal | dma | 18,432 | 183 | 96.1 | Pass | in_SRAM_0_none /Inst | in_SRAM_2_DMA /Data | |
internal | dma | 38,400 | 381 | 96.1 | Pass | in_SRAM_0_none /Inst | in_SRAM_2_DMA /Data | |
internal | dma | 64,000 | 167 | 365.5 | Pass | in_SRAM_2_DMA /Data | in_SRAM_1_DMA /Data | *1 |
internal | dma | 115,200 | 0 | inf | **Fail** | Error | Error | |
internal | dma | 153,600 | 0 | inf | **Fail** | Error | Error | |
internal | internal | 1,152 | 20 | 54.9 | Pass | in_SRAM_0_none /Inst | in_SRAM_0_none /Inst | |
internal | internal | 4,608 | 82 | 53.6 | Pass | in_SRAM_0_none /Inst | in_SRAM_0_none /Inst | |
internal | internal | 18,432 | 328 | 53.6 | Pass | in_SRAM_0_none /Inst | in_SRAM_0_none /Inst | |
internal | internal | 38,400 | 381 | 96.1 | Pass | in_SRAM_0_none /Inst | in_SRAM_2_DMA /Data | *1 |
internal | internal | 64,000 | 167 | 365.5 | Pass | in_SRAM_2_DMA /Data | in_SRAM_1_DMA /Data | *1 |
internal | internal | 115,200 | 0 | inf | **Fail** | Error | Error | |
internal | internal | 153,600 | 0 | inf | **Fail** | Error | Error | |
internal | spiram | 1,152 | 11 | 99.9 | Pass | in_SRAM_0_none /Inst | ex_SRAM_rw /Data | |
internal | spiram | 4,608 | 45 | 97.7 | Pass | in_SRAM_0_none /Inst | ex_SRAM_rw /Data | |
internal | spiram | 18,432 | 191 | 92.0 | Pass | in_SRAM_0_none /Inst | ex_SRAM_rw /Data | |
internal | spiram | 38,400 | 1,531 | 23.9 | Pass | in_SRAM_0_none /Inst | ex_SRAM_rw /Data | *2 |
internal | spiram | 64,000 | 4,440 | 13.7 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data | *2 *1 |
internal | spiram | 115,200 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data | |
internal | spiram | 153,600 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data | |
progmem | dma | 1,152 | 4 | 274.7 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data | |
progmem | dma | 4,608 | 18 | 244.1 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data | |
progmem | dma | 18,432 | 73 | 240.8 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data | |
progmem | dma | 38,400 | 783 | 46.8 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data | *3 |
progmem | dma | 64,000 | 2,610 | 23.4 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data | *3 |
progmem | dma | 115,200 | 0 | inf | **Fail** | ex_Flash_r_a /Data | Error | |
progmem | dma | 153,600 | 0 | inf | **Fail** | ex_Flash_r_a /Data | Error | |
progmem | spiram | 1,152 | 4 | 274.7 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | |
progmem | spiram | 4,608 | 18 | 244.1 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | |
progmem | spiram | 18,432 | 76 | 231.3 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | |
progmem | spiram | 38,400 | 2,000 | 18.3 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | *2 |
progmem | spiram | 64,000 | 6,798 | 9.0 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | *2 (?) |
progmem | spiram | 115,200 | 12,271 | 9.0 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | *2 (?) |
progmem | spiram | 153,600 | 16,301 | 9.0 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data | *2 (?) |
spiram | dma | 1,152 | 3 | 366.2 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data | |
spiram | dma | 4,608 | 12 | 366.2 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data | |
spiram | dma | 18,432 | 49 | 358.7 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data | |
spiram | dma | 38,400 | 743 | 49.3 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data | *2 |
spiram | dma | 64,000 | 2,430 | 25.1 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data | *2 |
spiram | dma | 115,200 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error | |
spiram | dma | 153,600 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error | |
spiram | internal | 1,152 | 12 | 91.6 | Pass | ex_SRAM_rw /Data | in_SRAM_0_none /Inst | |
spiram | internal | 4,608 | 48 | 91.6 | Pass | ex_SRAM_rw /Data | in_SRAM_0_none /Inst | |
spiram | internal | 18,432 | 198 | 88.8 | Pass | ex_SRAM_rw /Data | in_SRAM_0_none /Inst | |
spiram | internal | 38,400 | 1,042 | 35.1 | Pass | ex_SRAM_rw /Data | in_SRAM_0_none /Inst | *2 |
spiram | internal | 64,000 | 2,431 | 25.1 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data | *2 *1 |
spiram | internal | 115,200 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error | |
spiram | internal | 153,600 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error | |
spiram | spiram | 1,152 | 3 | 366.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | |
spiram | spiram | 4,608 | 14 | 313.9 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | |
spiram | spiram | 18,432 | 730 | 24.1 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | *2 *4 |
spiram | spiram | 38,400 | 4,054 | 9.0 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | *2 |
spiram | spiram | 64,000 | 6,764 | 9.0 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | *2 (?) |
spiram | spiram | 115,200 | 12,167 | 9.0 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | *2 (?) |
spiram | spiram | 153,600 | 16,207 | 9.0 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data | *2 (?) |
サイズを大きくしていったときに変化があった部分を見ていきましょう。まずdma→dmaのErrorのところは、確保できるサイズを超えただけなので無視していいです。(以下同様)
備考 *1 のdma→internalで急激に速度が上がってるのは、転送先のメモリがDMA側に確保されてしまったからだと思われます。From(type)が転送元、To(type)が転送先の実際に確保された領域を意味しているのですが、”in_SRAM_0_none /Inst” だったものが “in_SRAM_1_DMA /Data” に変わっています。
備考 *2 の→spiramへの転送では、38,400バイト以降で急激に速度が低下しています。これはおそらく速い方がキャッシュの影響で、遅い方が本来のPSRAMの速度なのではないかと思ってます。そう考えると25MB/sあたりがPSRAMの速度なのでしょう。備考 *3 もおそらく同じ理由です。
spiram→spiramの結果をみると9MB/sほどで、読み書き両方で同じバスが使用されるためにさらに遅くなっているのだと思われます。PSRAM同士でコピーするような使い方はやめた方がいいですね。
備考 *4 のPSRAM同士の場合、速度低下が18,432バイトの段階で起こっています。もしかすると読み書き両方でキャッシュを使ってしまったからなのかなという気がしています。
最後にPROGMEM (Flash)の結果ですが、一番高速なはずのprogmem→dmaでキャッシュの影響が少ない64,000バイトの結果を見ると、23MB/sになっています。この辺がFlashの転送速度だと思われます。
まとめ
・DMA転送 350MB/s くらい
・DMAではない内部メモリ 56MB/s くらい
・PSRAM 25MB/s くらい
・PROGMEM (Flash) 23MB/s くらい
・PSRAMやFlashはキャッシュの恩恵により小サイズならもっと速い
ベンチマーク結果(2-B) メモリコピーの時間計測 詳細
内蔵メモリ(DMA)と外部メモリ(PSRAM)の組み合わせ別に、データサイズを更に細かくして測定してみました。使用したプログラムは こちら です。縦軸は転送スピードで上に行くほど高速です。横軸は転送したデータのバイト数です。

これを見ると16KBと32KBのあたりでキャッシュが作用している様子がよくわかりますね。青い線は内蔵メモリ(DMA)→内蔵メモリ(DMA)なので、サイズに関係なく高速です。最初が特に高いところは測定誤差でしょう。オレンジ色と灰色の線はメモリ(DMA)←→外部メモリ(PSRAM)の結果です。キャッシュの範囲を超えたあたりから本当の速度になっていきます。黄色はPSRAM同士です。32KBではなく16KBに変化点があるのが興味深いですね。WriteとReadでキャッシュが別々になってるってことなんですかね。全てのデータは以下の通りです。
全ての結果を表示する
Size | dma->dma | dma->spiram | spiram->dma | spiram->spiram |
---|---|---|---|---|
512 | 488.3 | 488.3 | 488.3 | 488.3 |
1,024 | 488.3 | 488.3 | 488.3 | 325.5 |
1,536 | 366.2 | 366.2 | 366.2 | 366.2 |
2,048 | 390.6 | 390.6 | 390.6 | 325.5 |
2,560 | 406.9 | 406.9 | 406.9 | 305.2 |
3,072 | 366.2 | 366.2 | 366.2 | 325.5 |
3,584 | 379.8 | 379.8 | 379.8 | 310.7 |
4,096 | 390.6 | 390.6 | 390.6 | 300.5 |
4,608 | 366.2 | 366.2 | 366.2 | 313.9 |
5,120 | 375.6 | 375.6 | 375.6 | 305.2 |
5,632 | 383.7 | 383.7 | 383.7 | 316.0 |
6,144 | 366.2 | 366.2 | 366.2 | 308.4 |
6,656 | 373.4 | 373.4 | 373.4 | 302.3 |
7,168 | 379.8 | 379.8 | 379.8 | 310.7 |
7,680 | 366.2 | 366.2 | 366.2 | 305.2 |
8,192 | 372.0 | 372.0 | 372.0 | 312.5 |
8,704 | 377.3 | 360.9 | 377.3 | 307.4 |
9,216 | 366.2 | 366.2 | 366.2 | 303.1 |
9,728 | 371.1 | 371.1 | 371.1 | 299.3 |
10,240 | 375.6 | 361.7 | 361.7 | 295.9 |
10,752 | 366.2 | 366.2 | 366.2 | 301.6 |
11,264 | 370.4 | 370.4 | 370.4 | 298.4 |
11,776 | 374.4 | 362.3 | 362.3 | 295.5 |
12,288 | 366.2 | 366.2 | 366.2 | 293.0 |
12,800 | 369.9 | 369.9 | 369.9 | 297.7 |
13,312 | 373.4 | 362.7 | 362.7 | 295.2 |
13,824 | 366.2 | 366.2 | 366.2 | 293.0 |
14,336 | 369.5 | 369.5 | 369.5 | 297.2 |
14,848 | 372.6 | 363.1 | 363.1 | 295.0 |
15,360 | 366.2 | 366.2 | 366.2 | 293.0 |
15,872 | 369.2 | 369.2 | 369.2 | 280.3 |
16,384 | 372.0 | 363.4 | 363.4 | 289.4 |
16,896 | 366.2 | 366.2 | 366.2 | 73.6 |
17,408 | 368.9 | 368.9 | 368.9 | 43.6 |
17,920 | 371.5 | 363.6 | 363.6 | 31.2 |
18,432 | 366.2 | 351.6 | 358.7 | 24.4 |
18,944 | 368.7 | 347.4 | 354.2 | 20.2 |
19,456 | 363.8 | 350.1 | 356.8 | 17.5 |
19,968 | 366.2 | 352.7 | 359.3 | 15.6 |
20,480 | 368.5 | 348.8 | 355.1 | 14.2 |
20,992 | 364.0 | 351.2 | 357.5 | 13.1 |
21,504 | 366.2 | 347.6 | 353.6 | 12.1 |
22,016 | 368.4 | 349.9 | 355.9 | 11.4 |
22,528 | 364.1 | 352.2 | 358.1 | 10.7 |
23,040 | 366.2 | 348.8 | 354.4 | 10.2 |
23,552 | 368.2 | 351.0 | 356.5 | 9.7 |
24,064 | 364.3 | 347.7 | 358.6 | 9.4 |
24,576 | 366.2 | 349.8 | 355.1 | 9.0 |
25,088 | 368.1 | 351.9 | 357.1 | 9.0 |
25,600 | 364.4 | 348.8 | 353.8 | 9.0 |
26,112 | 366.2 | 350.7 | 355.8 | 9.0 |
26,624 | 368.0 | 347.8 | 357.6 | 9.0 |
27,136 | 364.5 | 349.7 | 354.5 | 9.0 |
27,648 | 366.2 | 351.6 | 356.3 | 9.1 |
28,160 | 367.9 | 348.8 | 353.4 | 9.1 |
28,672 | 364.6 | 350.6 | 355.1 | 9.1 |
29,184 | 366.2 | 347.9 | 356.8 | 9.1 |
29,696 | 367.8 | 349.6 | 354.0 | 9.1 |
30,208 | 364.7 | 351.3 | 355.7 | 9.1 |
30,720 | 366.2 | 344.7 | 353.0 | 9.1 |
31,232 | 367.7 | 338.5 | 350.4 | 9.1 |
31,744 | 364.7 | 325.5 | 340.2 | 9.1 |
32,256 | 366.2 | 327.3 | 341.8 | 9.0 |
32,768 | 367.7 | 322.2 | 339.7 | 9.0 |
33,280 | 364.8 | 149.0 | 208.8 | 9.0 |
33,792 | 366.2 | 97.7 | 152.7 | 9.0 |
34,304 | 367.6 | 73.2 | 121.2 | 9.0 |
34,816 | 364.9 | 60.9 | 102.5 | 9.0 |
35,328 | 366.2 | 51.4 | 88.4 | 9.0 |
35,840 | 367.5 | 44.6 | 77.9 | 9.0 |
36,352 | 364.9 | 39.4 | 69.8 | 9.0 |
36,864 | 366.2 | 35.4 | 63.3 | 9.0 |
37,376 | 367.5 | 32.7 | 58.2 | 9.0 |
37,888 | 365.0 | 30.4 | 53.9 | 9.0 |
38,400 | 366.2 | 28.4 | 50.2 | 9.0 |
38,912 | 367.4 | 26.5 | 47.2 | 9.0 |
39,424 | 365.0 | 25.1 | 44.9 | 9.0 |
39,936 | 366.2 | 23.6 | 42.4 | 9.0 |
40,448 | 367.4 | 22.5 | 40.4 | 9.0 |
40,960 | 365.1 | 21.5 | 39.0 | 9.0 |
41,472 | 366.2 | 20.6 | 37.2 | 9.0 |
41,984 | 367.3 | 19.8 | 35.7 | 9.0 |
42,496 | 365.1 | 19.0 | 34.5 | 9.0 |
43,008 | 366.2 | 18.4 | 33.4 | 9.0 |
43,520 | 367.3 | 17.8 | 32.2 | 9.0 |
44,032 | 365.2 | 17.2 | 31.4 | 9.0 |
44,544 | 366.2 | 16.7 | 30.3 | 9.0 |
45,056 | 367.3 | 16.3 | 29.5 | 9.0 |
45,568 | 365.2 | 15.8 | 28.7 | 9.0 |
46,080 | 366.2 | 15.4 | 28.0 | 9.0 |
46,592 | 367.2 | 15.1 | 27.4 | 9.0 |
47,104 | 365.2 | 14.7 | 26.8 | 9.0 |
47,616 | 366.2 | 14.5 | 26.3 | 9.0 |
48,128 | 367.2 | 14.3 | 26.0 | 9.0 |
48,640 | 365.3 | 14.0 | 25.5 | 9.0 |
49,152 | 366.2 | 13.7 | 25.1 | 9.0 |
次にキャッシュの影響を受けないようにコピー前にキャッシュをクリアし、データもランダムな値にして同様の実験をしてみました。
Size | dma->dma | dma->spiram | spiram->dma | spiram->spiram |
---|---|---|---|---|
4,096 | 355.1 | 24.7 | 24.6 | 12.7 |
8,192 | 355.1 | 24.7 | 24.7 | 12.3 |
12,288 | 366.2 | 25.1 | 25.2 | 9.9 |
16,384 | 363.4 | 24.0 | 24.0 | 8.9 |
20,480 | 361.7 | 20.8 | 21.0 | 9.0 |
24,576 | 366.2 | 19.4 | 19.5 | 9.0 |
28,672 | 364.6 | 18.3 | 18.4 | 9.0 |
32,768 | 363.4 | 17.5 | 17.7 | 9.0 |
36,864 | 362.4 | 17.1 | 18.3 | 9.0 |
40,960 | 365.1 | 16.6 | 18.9 | 9.0 |
内部メモリ(DMA)同士は 360MB/s くらい、外部メモリ(PSRAM)同士は 9MB/s くらい、内部と外部間は 24MB/s くらいのようです。
(3) canvas.createSprite()するサイズで使用するメモリの場所が変わるかどうか
canvas.createSprite() で作成するサイズで、確保されるメモリの場所に違いがあるのかをテストしました。usePsram(false)でPSRAMは使用しない設定にしています。
Size | Width | Height | Result | Address | Region |
---|---|---|---|---|---|
1152 | 24 | 24 | Pass | 3FFB2524 | in_SRAM_2_DMA /Data |
4608 | 48 | 48 | Pass | 3FFB2524 | in_SRAM_2_DMA /Data |
18432 | 96 | 96 | Pass | 3FFC6188 | in_SRAM_2_DMA /Data |
38400 | 160 | 120 | Pass | 3FFC6188 | in_SRAM_2_DMA /Data |
64000 | 200 | 160 | Pass | 3FFC6188 | in_SRAM_2_DMA /Data |
115200 | 240 | 240 | **Fail** | 0 | error |
153600 | 320 | 240 | **Fail** | 0 | error |
この結果、全て内部メモリの高速なDMA可能なエリアに作成されていることがわかりました。M5Stack Core 2の320×240ピクセルの画面全てが収まるキャンバスは大きすぎて内部メモリには作成できないので、大きなサイズはPSRAMに作成するしかありません。
ベンチマーク結果(4) pushSprite()の時間計測
最後にスプライトをキャンバスまたはLCDに出力する pushSprite() の速度を計測してみました。画像サイズ 96×96ピクセル、16ビットカラーでの結果です。
From | To | Color | Width | Height | Size | Time | MB/s | Result | From(used) | To(used) |
---|---|---|---|---|---|---|---|---|---|---|
dma | display | 16 | 96 | 96 | 18,432 | 3695 | 4.8 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 96 | 96 | 18,432 | 50 | 351.6 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 96 | 96 | 18,432 | 997 | 17.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 96 | 96 | 18,432 | 3847 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 96 | 96 | 18,432 | 52 | 338.0 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 96 | 96 | 18,432 | 1592 | 11.0 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 96 | 96 | 18,432 | 3900 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 96 | 96 | 18,432 | 100 | 175.8 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 16 | 96 | 96 | 18,432 | 1032 | 17.0 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
結果は先ほどのメモリコピーの場合と同じ傾向がみられました。DMA同士では爆速、PSRAMは低速な転送速度になりました。また、ディスプレイ(&M5.Display)への出力は転送元にかかわらず 4.5MB/s にとどまっています。
PROGMEMからの転送速度はpushSprite()ではなくpushImage()でテストしています。
続いて、以下は透明色を指定して pushSprite() をしたときの結果です。透明色を指定していない場合は単純にメモリをコピーするだけなので(きっと)高速ですが、透明色を指定した場合は各ピクセル毎に演算が必要になり、速度低下が予想されます。
From | To | Color | Width | Height | Size | Time | MB/s | Result | From(used) | To(used) |
---|---|---|---|---|---|---|---|---|---|---|
dma | display | 16 | 96 | 96 | 18,432 | 4070 | 4.3 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 96 | 96 | 18,432 | 954 | 18.4 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 96 | 96 | 18,432 | 997 | 17.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 96 | 96 | 18,432 | 4071 | 4.3 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 96 | 96 | 18,432 | 970 | 18.1 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 96 | 96 | 18,432 | 1649 | 10.7 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
テストの結果、出力先がディスプレイの場合は影響は軽微なものの、DMAでの転送の場合は 18MB/s と大幅な低下がみられました。必要がなければ透明色は指定しない方がいいですね。残念ながら私がやりたかったのはスプライトの透過処理だったので、他に高速化する方法を考えなくてはなりません。
参考までに全てのサイズの結果を以下に示します。
表を表示する pushSprite() 透明色指定なし
From | To | Color | Width | Height | Size | Time | MB/s | Result | From(used) | To(used) |
---|---|---|---|---|---|---|---|---|---|---|
dma | display | 16 | 24 | 24 | 1,152 | 239 | 4.6 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 24 | 24 | 1,152 | 5 | 219.7 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 24 | 24 | 1,152 | 248 | 4.4 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 24 | 24 | 1,152 | 5 | 219.7 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 24 | 24 | 1,152 | 248 | 4.4 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 24 | 24 | 1,152 | 6 | 183.1 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
dma | display | 16 | 48 | 48 | 4,608 | 930 | 4.7 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 48 | 48 | 4,608 | 14 | 313.9 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 48 | 48 | 4,608 | 246 | 17.9 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 48 | 48 | 4,608 | 967 | 4.5 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 48 | 48 | 4,608 | 14 | 313.9 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 48 | 48 | 4,608 | 250 | 17.6 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 48 | 48 | 4,608 | 986 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 48 | 48 | 4,608 | 22 | 199.8 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 16 | 48 | 48 | 4,608 | 248 | 17.7 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
dma | display | 16 | 96 | 96 | 18,432 | 3695 | 4.8 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 96 | 96 | 18,432 | 50 | 351.6 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 96 | 96 | 18,432 | 997 | 17.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 96 | 96 | 18,432 | 3847 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 96 | 96 | 18,432 | 52 | 338.0 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 96 | 96 | 18,432 | 1592 | 11.0 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 96 | 96 | 18,432 | 3900 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 96 | 96 | 18,432 | 100 | 175.8 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 16 | 96 | 96 | 18,432 | 1032 | 17.0 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
dma | display | 16 | 160 | 120 | 38,400 | 7690 | 4.8 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 160 | 120 | 38,400 | 102 | 359.0 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 160 | 120 | 38,400 | 3167 | 11.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 160 | 120 | 38,400 | 8013 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 160 | 120 | 38,400 | 736 | 49.8 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 160 | 120 | 38,400 | 5914 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 160 | 120 | 38,400 | 8180 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 160 | 120 | 38,400 | 924 | 39.6 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 16 | 160 | 120 | 38,400 | 3962 | 9.2 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
dma | display | 16 | 200 | 160 | 64,000 | 12812 | 4.8 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 200 | 160 | 64,000 | 169 | 361.2 | Pass | in_SRAM_2_DMA /Data | in_SRAM_1_DMA /Data |
dma | spiram | 16 | 200 | 160 | 64,000 | 7549 | 8.1 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 200 | 160 | 64,000 | 13273 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 200 | 160 | 64,000 | 2459 | 24.8 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 200 | 160 | 64,000 | 9845 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 200 | 160 | 64,000 | 13549 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 200 | 160 | 64,000 | 2665 | 22.9 | Pass | ex_Flash_r_a /Data | in_SRAM_2_DMA /Data |
progmem | spiram | 16 | 200 | 160 | 64,000 | 10001 | 6.1 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
dma | display | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | Error | Display |
dma | dma | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | Error | Error |
dma | spiram | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data |
spiram | display | 16 | 240 | 240 | 115,200 | 23881 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error |
spiram | spiram | 16 | 240 | 240 | 115,200 | 17705 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 240 | 240 | 115,200 | 24286 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | ex_Flash_r_a /Data | Error |
progmem | spiram | 16 | 240 | 240 | 115,200 | 17942 | 6.1 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
dma | display | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | Error | Display |
dma | dma | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | Error | Error |
dma | spiram | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data |
spiram | display | 16 | 320 | 240 | 153,600 | 32081 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error |
spiram | spiram | 16 | 320 | 240 | 153,600 | 23573 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
progmem | display | 16 | 320 | 240 | 153,600 | 32338 | 4.5 | Pass | ex_Flash_r_a /Data | Display |
progmem | dma | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | ex_Flash_r_a /Data | Error |
progmem | spiram | 16 | 320 | 240 | 153,600 | 23883 | 6.1 | Pass | ex_Flash_r_a /Data | ex_SRAM_rw /Data |
表を表示する pushSprite() 透明色指定あり
From | To | Color | Width | Height | Size | Time | MB/s | Result | From(used) | To(used) |
---|---|---|---|---|---|---|---|---|---|---|
dma | display | 16 | 24 | 24 | 1,152 | 312 | 3.5 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 24 | 24 | 1,152 | 312 | 3.5 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 24 | 24 | 1,152 | 66 | 16.6 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
dma | display | 16 | 48 | 48 | 4,608 | 1116 | 3.9 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 48 | 48 | 4,608 | 246 | 17.9 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 48 | 48 | 4,608 | 246 | 17.9 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 48 | 48 | 4,608 | 1116 | 3.9 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 48 | 48 | 4,608 | 246 | 17.9 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 48 | 48 | 4,608 | 246 | 17.9 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
dma | display | 16 | 96 | 96 | 18,432 | 4070 | 4.3 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 96 | 96 | 18,432 | 954 | 18.4 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 96 | 96 | 18,432 | 997 | 17.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 96 | 96 | 18,432 | 4071 | 4.3 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 96 | 96 | 18,432 | 970 | 18.1 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 96 | 96 | 18,432 | 1649 | 10.7 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
dma | display | 16 | 160 | 120 | 38,400 | 8162 | 4.5 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 160 | 120 | 38,400 | 1963 | 18.7 | Pass | in_SRAM_2_DMA /Data | in_SRAM_2_DMA /Data |
dma | spiram | 16 | 160 | 120 | 38,400 | 3168 | 11.6 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 160 | 120 | 38,400 | 8177 | 4.5 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 160 | 120 | 38,400 | 2611 | 14.0 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 160 | 120 | 38,400 | 5903 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
dma | display | 16 | 200 | 160 | 64,000 | 13441 | 4.5 | Pass | in_SRAM_2_DMA /Data | Display |
dma | dma | 16 | 200 | 160 | 64,000 | 3260 | 18.7 | Pass | in_SRAM_2_DMA /Data | in_SRAM_1_DMA /Data |
dma | spiram | 16 | 200 | 160 | 64,000 | 7549 | 8.1 | Pass | in_SRAM_2_DMA /Data | ex_SRAM_rw /Data |
spiram | display | 16 | 200 | 160 | 64,000 | 13459 | 4.5 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 200 | 160 | 64,000 | 5549 | 11.0 | Pass | ex_SRAM_rw /Data | in_SRAM_2_DMA /Data |
spiram | spiram | 16 | 200 | 160 | 64,000 | 9839 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
dma | display | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | Error | Display |
dma | dma | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | Error | Error |
dma | spiram | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data |
spiram | display | 16 | 240 | 240 | 115,200 | 24015 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 240 | 240 | 115,200 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error |
spiram | spiram | 16 | 240 | 240 | 115,200 | 17705 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
dma | display | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | Error | Display |
dma | dma | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | Error | Error |
dma | spiram | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | Error | ex_SRAM_rw /Data |
spiram | display | 16 | 320 | 240 | 153,600 | 31719 | 4.6 | Pass | ex_SRAM_rw /Data | Display |
spiram | dma | 16 | 320 | 240 | 153,600 | 0 | inf | **Fail** | ex_SRAM_rw /Data | Error |
spiram | spiram | 16 | 320 | 240 | 153,600 | 23575 | 6.2 | Pass | ex_SRAM_rw /Data | ex_SRAM_rw /Data |
全画面表示をした場合、320×240 (16bit) をPSRAM→Displayに転送すると 32ms 程度かかります。FPSに換算するとだいたい 31fps くらいですね。遅いPSRAMを使っても 30fps は確保できそう。速いDMAメモリを分割して出力してやれば速いかなとも思ったのですが、結局はディスプレイがボトルネックになっているので、どちらを使っても関係なさそうです。逆に複数のスプライトを重ね合わせるような処理をする場合は、DMA上で処理しておく方がよさそうですね。
まとめ
・ディスプレイ(LCD)への出力 4.5MB/s くらい
・320×240ピクセルで 31fps くらいが限界(?)
・PSRAMが20MB/sくらいでDMAなら350MB/s以上出る
・しかし最終的にはディスプレイがボトルネックなのでどっちでもいい
余談1: インスタンスの作成方法の違いでハマった件
以下のプログラムは一見同じ動作をするように見えますが、実際に createCanves() で 320×240 のキャンバスを作成してみると挙動が違うことがわかります。
M5Canvas canvas(&M5.Display);
setup() {
canvas.createSprite(320, 240);
}
loop() {
canvas.pushSprite(0, 0);
}
M5Canvas canvas;
setup() {
canvas.createSprite(320, 240);
}
loop() {
canvas.pushSprite(&M5.Display, 0, 0);
}
前者の場合はメモリ不足により createSprite() は失敗します。ですが後者は成功します。違いが何かというと、後者ではPSRAMにメモリを確保しているため成功していました。PSRAMを使用するかどうかは usePsram() で指定できますが、未指定の場合のデフォルト動作が変わるようです。最初原因がわからず、しばらく何が違うんだー!?と混乱してました。
余談2: TablePressプラグインを使ってみた
今回初めて TablePress というWordPressのプラグインを使ってみました。WordPress標準の表はとても使いにくく、大量のデータがある場合でもスプレッドシートから一気にコピーすることはできません。それならMarkDownで出力すれば表は簡単に作れそうです。しかし今回はソート機能を付けたいのです。調べてみるとTablePressというのがあったのでインストールしてみました。
表の作成は記事の編集画面ではなく、専用の画面で作成します。表を作成した後に記事で読み込むイメージです。この編集画面のすごいところは、なんとExcelからコピペができるところ。期待に100%応えてくれました!

と、関心していたのですが、数値を右寄せしようとして気づきました。セル内の位置を変更する機能が無い!? こんなに良くできたプラグインなのに、こんな基本的な機能がなぜないのか。調べてみるとCSSを追加するらしいです。あーこれは技術的な理由よりも思想的なやつだな…。
CSSの追加方法をネットで調べてみると難しいことをしている記事がたくさん出てきました。面倒だったので「カスタムHTML」でこう書いてみました。
<style>
.tablepress-id-1 .column-2 { text-align: right; }
.tablepress-id-1 .column-3 { text-align: center; }
</style>
右寄せヨシ!