-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Description
Board
ESP32P4 Dev Module, 4D Systems ESP32-P4 MIPI Displays
Device Description
Only the SD card
Hardware Configuration
None
Version
v3.3.4
Type
Task
IDE Name
Arduino IDE
Operating System
Windows 11
Flash frequency
80MHz
PSRAM enabled
yes
Upload speed
921600
Description
The ESP32-P4 SD_MMC library seems to be underperforming at both 4-bit SDIO and 1-bit SDIO. We expect it at least perform around 2.5x the speed of 1-bit SDIO.
Using the same SD card, we performed a benchmark test for 4-bit and 1-bit SDIO for ESP32-P4 and 1-bit SDIO for ESP32-S3:
- FAT Type: FAT32
- Bytes per sector: 512
- Sectors per cluster: 128
- Cluster size (bytes): 65536
- Total clusters: 243168
- Volume size (MB): 2914
- SD Card Type: SDHC
- SD Card Size: 15203 MB
- Test File Size: 524288 bytes
- Test Buffer Size: 4096 bytes
We got the following results (all in MB/s):
| Test | ESP32-P4 (4-bit SDIO) | ESP32-P4 (1-bit SDIO) | ESP32-S3 (1-bit SDIO) |
|---|---|---|---|
| Sequential Write | 4.21 | 0.70 | 2.74 |
| Sequential Read | 2.50 | 1.77 | 3.66 |
| Random Write | 0.54 | 0.53 | 0.59 |
| Random Read | 0.14 | 0.10 | 0.15 |
I found this discussion that was started months ago but there isn't any progress: #11719
We are particularly interested in read speed since our application primarily utilizes resources from the SD card. Notice that using 4-bit SDIO for P4 seems to be slower than S3 at 1-bit SDIO when performing sequential reads.
Sketch
#include "FS.h"
#include "SD_MMC.h"
// Benchmark Configuration
#define TEST_FILE_SIZE (512 * 1024) // 512KB test file (smaller for faster testing)
#define BUFFER_SIZE 4096 // 4KB buffer
#define NUM_ITERATIONS 3 // Number of test iterations
uint8_t testBuffer[BUFFER_SIZE];
// Read a raw sector from SD_MMC
bool readSector(uint32_t sector, uint8_t* buffer) {
return SD_MMC.readRAW(buffer, sector);
}
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10);
}
Serial.println("\nESP32 SD_MMC Benchmark");
Serial.println("======================");
// Initialize test buffer with data
for (size_t i = 0; i < BUFFER_SIZE; i++) {
testBuffer[i] = i % 256;
}
// Initialize SD_MMC
Serial.println("Initializing SD_MMC...");
// SD_MMC.setPins(12, 11, 13); // gen4-ESP32Q-43 series (only 1-bit)
// if (!SD_MMC.begin("/sdcard", true)) { // 1 bit
if (!SD_MMC.begin("/sdcard")) {
Serial.println("SD_MMC initialization failed!");
return;
}
Serial.println("SD_MMC initialized successfully.");
uint8_t sector[512];
// --- Step 1: Read MBR ---
if (!readSector(0, sector)) {
Serial.println("MBR read failed");
return;
}
// Partition 1 entry starts at byte 446
uint32_t partLBA =
sector[454] |
(sector[455] << 8) |
(sector[456] << 16) |
(sector[457] << 24);
Serial.printf("Partition starts at LBA: %u\n", partLBA);
// --- Step 2: Read FAT Boot Sector ---
if (!readSector(partLBA, sector)) {
Serial.println("Boot Sector read failed");
return;
}
// Extract FAT info
uint16_t bytesPerSector = sector[11] | (sector[12] << 8);
uint8_t sectorsPerCluster = sector[13];
uint16_t reservedSectors = sector[14] | (sector[15] << 8);
uint8_t fatCount = sector[16];
uint32_t fatSize;
uint16_t fat16Size = sector[22] | (sector[23] << 8); // FAT16
uint32_t fat32Size = sector[36] |
(sector[37] << 8) |
(sector[38] << 16) |
(sector[39] << 24); // FAT32
if (fat16Size != 0) {
fatSize = fat16Size;
} else {
fatSize = fat32Size;
}
uint32_t totalSectors =
(sector[19] | (sector[20] << 8)); // FAT16 field
if (totalSectors == 0) {
totalSectors = sector[32] |
(sector[33] << 8) |
(sector[34] << 16) |
(sector[35] << 24); // FAT32 field
}
uint32_t dataStart =
partLBA + reservedSectors + fatCount * fatSize;
uint32_t dataSectors = totalSectors - (reservedSectors + fatCount * fatSize);
uint32_t clusterCount = dataSectors / sectorsPerCluster;
// FAT12/16 FS type string is at offset 0x36 (54)
char fatId16[9];
memcpy(fatId16, §or[54], 8);
fatId16[8] = 0;
// FAT32 FS type string is at offset 0x52 (82)
char fatId32[9];
memcpy(fatId32, §or[82], 8);
fatId32[8] = 0;
const char* fatType;
if (strncmp(fatId32, "FAT32", 5) == 0) {
fatType = "FAT32";
} else if (strncmp(fatId16, "FAT16", 5) == 0) {
fatType = "FAT16";
} else if (strncmp(fatId16, "FAT12", 5) == 0) {
fatType = "FAT12";
} else {
fatType = "Unknown";
}
Serial.printf("Detected FS: %s\n", fatType);
// Cluster size in bytes
uint32_t clusterBytes = bytesPerSector * sectorsPerCluster;
Serial.println("=== SD CARD FILESYSTEM INFO ===");
Serial.printf("FAT Type: %s\n", fatType);
Serial.printf("Bytes per sector: %u\n", bytesPerSector);
Serial.printf("Sectors per cluster: %u\n", sectorsPerCluster);
Serial.printf("Cluster size (bytes): %u\n", clusterBytes);
Serial.printf("Total clusters: %u\n", clusterCount);
Serial.printf("Volume size (MB): %u\n", (totalSectors * bytesPerSector) / (1024 * 1024));
// Print card information
printCardInfo();
// Run benchmark tests
runBenchmark();
}
void loop() {
// Add option to re-run benchmark
Serial.println("\nPress 'r' to run benchmark again");
while (!Serial.available()) {
delay(100);
}
char command = Serial.read();
if (command == 'r' || command == 'R') {
runBenchmark();
}
// Clear serial buffer
while (Serial.available()) {
Serial.read();
}
}
void printCardInfo() {
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %llu MB\n", cardSize);
}
float testWriteSpeed(const char* filename, size_t fileSize) {
File file = SD_MMC.open(filename, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return 0;
}
unsigned long startTime = micros();
size_t bytesWritten = 0;
while (bytesWritten < fileSize) {
size_t toWrite = min((size_t)BUFFER_SIZE, fileSize - bytesWritten);
size_t written = file.write(testBuffer, toWrite);
if (written == 0) break;
bytesWritten += written;
}
file.close();
unsigned long endTime = micros();
float duration = (endTime - startTime) / 1000000.0;
float speed = (bytesWritten / (1024.0 * 1024.0)) / duration;
return speed;
}
float testReadSpeed(const char* filename) {
File file = SD_MMC.open(filename, FILE_READ);
if (!file) {
Serial.println("Failed to open file for reading");
return 0;
}
uint8_t readBuffer[BUFFER_SIZE];
unsigned long startTime = micros();
size_t bytesRead = 0;
while (file.available()) {
size_t read = file.read(readBuffer, BUFFER_SIZE);
if (read == 0) break;
bytesRead += read;
}
file.close();
unsigned long endTime = micros();
float duration = (endTime - startTime) / 1000000.0;
float speed = (bytesRead / (1024.0 * 1024.0)) / duration;
return speed;
}
float testRandomReadSpeed(const char* filename) {
File file = SD_MMC.open(filename, FILE_READ);
if (!file) {
Serial.println("Failed to open file for random read");
return 0;
}
uint8_t readBuffer[512];
unsigned long startTime = micros();
const int numReads = 500;
size_t fileSize = file.size();
for (int i = 0; i < numReads; i++) {
size_t pos = random(fileSize - sizeof(readBuffer));
file.seek(pos);
file.read(readBuffer, sizeof(readBuffer));
}
file.close();
unsigned long endTime = micros();
float duration = (endTime - startTime) / 1000000.0;
float totalData = (numReads * sizeof(readBuffer)) / (1024.0 * 1024.0);
return totalData / duration;
}
float testRandomWriteSpeed(const char* filename) {
File file = SD_MMC.open(filename, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for random write");
return 0;
}
// Ensure file is large enough
file.seek(TEST_FILE_SIZE - 1);
file.write(0);
file.flush();
unsigned long startTime = micros();
const int numWrites = 300;
for (int i = 0; i < numWrites; i++) {
size_t pos = random(TEST_FILE_SIZE - BUFFER_SIZE);
file.seek(pos);
file.write(testBuffer, BUFFER_SIZE);
}
file.close();
unsigned long endTime = micros();
float duration = (endTime - startTime) / 1000000.0;
float totalData = (numWrites * BUFFER_SIZE) / (1024.0 * 1024.0);
return totalData / duration;
}
void runBenchmark() {
Serial.println("\n=== SD_MMC Benchmark Started ===");
Serial.printf("Test File Size: %u bytes\n", TEST_FILE_SIZE);
Serial.printf("Buffer Size: %u bytes\n", BUFFER_SIZE);
Serial.printf("Iterations: %u\n", NUM_ITERATIONS);
// Test sequential write
Serial.println("\n--- Sequential Write Test ---");
float writeSum = 0;
for (int i = 0; i < NUM_ITERATIONS; i++) {
char filename[32];
snprintf(filename, sizeof(filename), "/test_write_%d.bin", i);
float speed = testWriteSpeed(filename, TEST_FILE_SIZE);
Serial.printf(" Iteration %d: %.2f MB/s\n", i + 1, speed);
writeSum += speed;
SD_MMC.remove(filename);
delay(50);
}
float avgWriteSpeed = writeSum / NUM_ITERATIONS;
// Create test file for read tests
const char* readTestFile = "/read_test.bin";
testWriteSpeed(readTestFile, TEST_FILE_SIZE);
delay(200);
// Test sequential read
Serial.println("\n--- Sequential Read Test ---");
float readSum = 0;
for (int i = 0; i < NUM_ITERATIONS; i++) {
float speed = testReadSpeed(readTestFile);
Serial.printf(" Iteration %d: %.2f MB/s\n", i + 1, speed);
readSum += speed;
delay(50);
}
float avgReadSpeed = readSum / NUM_ITERATIONS;
// Test random read
Serial.println("\n--- Random Read Test ---");
float randomReadSum = 0;
for (int i = 0; i < NUM_ITERATIONS; i++) {
float speed = testRandomReadSpeed(readTestFile);
Serial.printf(" Iteration %d: %.2f MB/s\n", i + 1, speed);
randomReadSum += speed;
delay(50);
}
float avgRandomRead = randomReadSum / NUM_ITERATIONS;
// Test random write
Serial.println("\n--- Random Write Test ---");
float randomWriteSum = 0;
for (int i = 0; i < NUM_ITERATIONS; i++) {
char filename[32];
snprintf(filename, sizeof(filename), "/random_write_%d.bin", i);
float speed = testRandomWriteSpeed(filename);
Serial.printf(" Iteration %d: %.2f MB/s\n", i + 1, speed);
randomWriteSum += speed;
SD_MMC.remove(filename);
delay(50);
}
float avgRandomWrite = randomWriteSum / NUM_ITERATIONS;
// Clean up
SD_MMC.remove(readTestFile);
// Print summary
Serial.println("\n=== Benchmark Summary ===");
Serial.printf("Average Sequential Write: %.2f MB/s\n", avgWriteSpeed);
Serial.printf("Average Sequential Read: %.2f MB/s\n", avgReadSpeed);
Serial.printf("Average Random Read: %.2f MB/s\n", avgRandomRead);
Serial.printf("Average Random Write: %.2f MB/s\n", avgRandomWrite);
Serial.println("\nBenchmark completed!");
}Debug Message
ESP-ROM:esp32p4-eco2-20240710
Build:Jul 10 2024
rst:0x1 (POWERON),boot:0x30f (SPI_FAST_FLASH_BOOT)
SPI mode:DIO, clock div:1
load:0x4ff33ce0,len:0x11dc
load:0x4ff29ed0,len:0xcac
load:0x4ff2cbd0,len:0x33a0
entry 0x4ff29ed0
ESP32 SD_MMC Benchmark
======================
Initializing SD_MMC...
SD_MMC initialized successfully.
Partition starts at LBA: 2048
Detected FS: FAT32
=== SD CARD FILESYSTEM INFO ===
FAT Type: FAT32
Bytes per sector: 512
Sectors per cluster: 8
Cluster size (bytes): 4096
Total clusters: 3883520
Volume size (MB): 2914
SD Card Type: SDHC
SD Card Size: 15203 MB
=== SD_MMC Benchmark Started ===
Test File Size: 524288 bytes
Buffer Size: 4096 bytes
Iterations: 3
--- Sequential Write Test ---
Iteration 1: 4.20 MB/s
Iteration 2: 3.98 MB/s
Iteration 3: 4.21 MB/s
--- Sequential Read Test ---
Iteration 1: 2.47 MB/s
Iteration 2: 2.48 MB/s
Iteration 3: 2.47 MB/s
--- Random Read Test ---
Iteration 1: 0.14 MB/s
Iteration 2: 0.14 MB/s
Iteration 3: 0.14 MB/s
--- Random Write Test ---
Iteration 1: 0.52 MB/s
Iteration 2: 0.54 MB/s
Iteration 3: 0.54 MB/s
=== Benchmark Summary ===
Average Sequential Write: 4.13 MB/s
Average Sequential Read: 2.47 MB/s
Average Random Read: 0.14 MB/s
Average Random Write: 0.53 MB/s
Benchmark completed!
Press 'r' to run benchmark again
Other Steps to Reproduce
No response
I have checked existing issues, online documentation and the Troubleshooting Guide
- I confirm I have checked existing issues, online documentation and Troubleshooting guide.