SD_MMC writing and reading

Salakhov
Posts: 11
Joined: Sat Aug 24, 2019 2:08 pm

SD_MMC writing and reading

Postby Salakhov » Sat Feb 22, 2020 8:30 am

Using ESP32 WROVER KIT and library SD_MMC
I have increased buffer size from 512 to 5120 bytes.

Results are:

Lexar 1800x 32GB HCII
11:57:17.800 -> 10485760 bytes read for 8773 ms
11:57:22.137 -> 10485760 bytes written for 4333 ms

SanDisk Ultra 32GB HCI
12:05:13.484 -> 10485760 bytes read for 6835 ms
12:05:16.999 -> 10485760 bytes written for 3522 ms

It looks strange.
Writing is faster than reading.

Espressif shows other data.

viewtopic.php?f=13&t=2055


I think SD_MMC library is not correct working.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: SD_MMC writing and reading

Postby ESP_Angus » Mon Feb 24, 2020 10:29 pm

Hi Salakhov,

Agree this looks strange. Although there are some complexities if you are, for example, reading fragmented data across multiple SD sectors but writing unfragmented, SD cards have some internal "write buffer" so they may be telling you the write is complete even though they're still flushing it to persistent storage internally.

Are you able to share the code you're using to measure these numbers, please?


Angus

Salakhov
Posts: 11
Joined: Sat Aug 24, 2019 2:08 pm

Re: SD_MMC writing and reading

Postby Salakhov » Tue Feb 25, 2020 2:57 pm

Hi.

The code:

Code: Select all

/*
 * Connect the SD card to the following pins:
 *
 * SD Card | ESP32
 *    D2       12
 *    D3       13
 *    CMD      15
 *    VSS      GND
 *    VDD      3.3V
 *    CLK      14
 *    VSS      GND
 *    D0       2  (add 1K pull up after flashing)
 *    D1       4
 */

#include "FS.h"
#include "SD_MMC.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.name(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("  SIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

void createDir(fs::FS &fs, const char * path){
    Serial.printf("Creating Dir: %s\n", path);
    if(fs.mkdir(path)){
        Serial.println("Dir created");
    } else {
        Serial.println("mkdir failed");
    }
}

void removeDir(fs::FS &fs, const char * path){
    Serial.printf("Removing Dir: %s\n", path);
    if(fs.rmdir(path)){
        Serial.println("Dir removed");
    } else {
        Serial.println("rmdir failed");
    }
}

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while(file.available()){
        Serial.write(file.read());
    }
}

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("File written");
    } else {
        Serial.println("Write failed");
    }
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\n", path);

    File file = fs.open(path, FILE_APPEND);
    if(!file){
        Serial.println("Failed to open file for appending");
        return;
    }
    if(file.print(message)){
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
    Serial.printf("Renaming file %s to %s\n", path1, path2);
    if (fs.rename(path1, path2)) {
        Serial.println("File renamed");
    } else {
        Serial.println("Rename failed");
    }
}

void deleteFile(fs::FS &fs, const char * path){
    Serial.printf("Deleting file: %s\n", path);
    if(fs.remove(path)){
        Serial.println("File deleted");
    } else {
        Serial.println("Delete failed");
    }
}

void testFileIO(fs::FS &fs, const char * path){
    File file = fs.open(path);
    static uint8_t buf[5120];
    size_t len = 0;
    uint32_t start = millis();
    uint32_t end = start;
    if(file){
        len = file.size();
        size_t flen = len;
        start = millis();
        while(len){
            size_t toRead = len;
            if(toRead > 5120){
                toRead = 5120;
            }
            file.read(buf, toRead);
            len -= toRead;
        }
        end = millis() - start;
        Serial.printf("%u bytes read for %u ms\n", flen, end);
        file.close();
    } else {
        Serial.println("Failed to open file for reading");
    }


    file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("Failed to open file for writing");
        return;
    }

    size_t i;
    start = millis();
    for(i=0; i<2048; i++){
        file.write(buf, 5120);
    }
    end = millis() - start;
    Serial.printf("%u bytes written for %u ms\n", 2048 * 5120, end);
    file.close();
}

void setup(){
    Serial.begin(115200);
    if(!SD_MMC.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD_MMC.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD_MMC card attached");
        return;
    }

    Serial.print("SD_MMC 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_MMC Card Size: %lluMB\n", cardSize);

    listDir(SD_MMC, "/", 0);
    createDir(SD_MMC, "/mydir");
    listDir(SD_MMC, "/", 0);
    removeDir(SD_MMC, "/mydir");
    listDir(SD_MMC, "/", 2);
    writeFile(SD_MMC, "/hello.txt", "Hello ");
    appendFile(SD_MMC, "/hello.txt", "World!\n");
    readFile(SD_MMC, "/hello.txt");
    deleteFile(SD_MMC, "/foo.txt");
    renameFile(SD_MMC, "/hello.txt", "/foo.txt");
    readFile(SD_MMC, "/foo.txt");
    testFileIO(SD_MMC, "/test.txt");
    Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
    Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
}

void loop(){

}
Lexar microSD card after quick formatting.

18:48:30.692 -> 10485760 bytes read for 8720 ms
18:48:34.899 -> 10485760 bytes written for 4221 ms

In any case reading has to be from 3 to 5 times faster.


Thanks

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: SD_MMC writing and reading

Postby ESP_Angus » Mon Mar 16, 2020 9:50 am

Hi,

I don't really know much about the Arduino SD_MMC libraries, but I think probably there is write buffering happening somewhere. Try including the "file.close()" line inside the measurement of write time, so any cost of flushing writes to the filesystem is included in the benchmark.

It's also possible the SDMMC card has a write buffer which is larger than 1MB, if you increase the write size to (for example) 256MB by increasing the number of write iterations, does the write speed go down?

Angus

ESP_igrr
Posts: 2072
Joined: Tue Dec 01, 2015 8:37 am

Re: SD_MMC writing and reading

Postby ESP_igrr » Mon Mar 16, 2020 9:33 pm

Hi Salakhov,

Reads using 'fread' (which is what file.read uses inside, I think) are indeed relatively slow due to buffering behavior. Newlib only reads 128 bytes at a time from the lower level driver, which removes any gain in using larger buffers. The way you can work around this is using a C function 'read' instead, as done here: https://github.com/espressif/esp-idf/bl ... mon.c#L796
This allows you to read more than one sector at a time.

Who is online

Users browsing this forum: No registered users and 71 guests