Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to get best performance from LittleFS #1033

Closed
ashiroji opened this issue Oct 22, 2024 · 3 comments
Closed

How to get best performance from LittleFS #1033

ashiroji opened this issue Oct 22, 2024 · 3 comments

Comments

@ashiroji
Copy link

ashiroji commented Oct 22, 2024

Hello,
In the current project, I'am using LittleFS (through Zephyr OS) with an external flash memory to store sensor data in a specific file format.
I am using the following Flash memory GD25LB256 (256Mbits) configured in SPI (it supports QSPI too and the HW connections are already made)

I would like to be able to buffer 1 minute of data (29,29Kbytes) and then store it in the flash.
The file format used contains an ASCII header (the first 512 bytes) and data.
Every time the file is appended, the header must be updated to maintain consistency.
The typical scenario is following:

  1. At start, create file with header + 0 data
  2. Every minute, update file with 1 minute of data and update header accordingly.

At the moment, we identify 3 operations:

  • File creation : create file and header for the first time
  • Data update: open file in append mode and write data
  • Header update. open file in read/write mode, read old header, update it and write back to flash

Unit testing each function independently gives reasonable execution time:

  file write file read file seek file open file close
header creation 18ms N/A N/A 1ms 0.6ms
data append 330ms N/A N/A 1.2ms 1ms
header update 16ms 0.8ms 9us 1ms 0.6ms

But when combining all functions together, the header update operation execution time grows exponentially.

  1 2 3 4 5
fs_open (ms) 0,96625 1,131437 1,296406 1,461515 1,626156
fs_read (ms) 1,065453 1,065578 1,149046 0,981859 1,065375
fs_seek (us) 9,281 9,281 9,281 9,281 9,281
fs_write (ms) 15,574031 16,230734 16,623593 17,148656 17,585843
fs_close (s) 1,065068656 1,621073343 2,176495781 2,722240796 3,291354078
I suspect that the increase in time is related to the increase in the file size and to some mechanism in LittleFS itself but I'm not very familiar with it.

I've read through a lot of the issues online (especially here) and I'm not sure if my problem is due to a bad configuration or that I'm hitting a bottleneck in LittleFS implementation.

If you can offer some expertise and architectural advice to handle this in a better way that would be appreciated.
It would also be helpful, if you can show me how to use LittleFS with a simulated flash

Thank you for your time and help

@geky
Copy link
Member

geky commented Nov 1, 2024

Hi @ashiroji, thanks for creating an issue.

I suspect you're running into the same issue as in #27, that is littlefs doesn't really implement random writes efficiently. Writes to the beginning of files (such as headers) will cause the entire file to be rewritten.

It's interesting to note you wouldn't have seen this with the separate unit tests as you need both the data and header updates.

This is a known issue, and there's work ongoing to improve this, but it's also a pretty significant/long-term change (rip out and replace the core file data-structure).


As for workarounds, the best option is probably to store the header and data as separate files, and concatenate later if you need to match a specific file format.

Depending on how you transfer files from the device, you could even do this concatenation transparently to the recipient of the file.


It would also be helpful, if you can show me how to use LittleFS with a simulated flash

There are quite a few options here.

One option is to start with lfs_filebd, which maps littlefs's block device API onto a local file, modifying/extending as necessary.

Another option is to fork littlefs's test/bench runner, which is what littlefs uses to run its test suite locally, simulate powerloss, etc. It's powerful but also very strictly provided "as is", has little/no documentation, susceptible to change, etc.

The best way to get started with the test/bench runner would probably be to look at some of the existing tests (tests/test_dirs.toml for example), run the tests, and poke around the test runner to see how it works:

$ make test - j
$ make test-runner -j && ./scripts/test.py runners/test_runner test_dirs
$ ./scripts/test.py --help

It would be nice to document this part of the build system better, but right now it's been low priority.

@ashiroji
Copy link
Author

ashiroji commented Nov 4, 2024

Thank you very much for your help.
I'll make some tests based on your workaround idea (separate files) and see what works best in my project.

@ashiroji
Copy link
Author

Hi again,
Using the separate files and increasing the different buffers allocated to littleFS:
read-size = <256>;
prog-size = <256>;
cache-size = <256>;
lookahead-size = <256>;

I get a much better performance, here are the numbers for the fusion of both files:

fuse data #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 #16 #17 #18 #19 #20 #21 Total
fs_open header(ms) 3,622921 N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A 3,622921
fs_open data(ms) 3,811171 N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A 3,811171
fs_read data(ms) 15,989953 17,271187 17,081265 15,365796 16,701921 16,701375 15,175734 16,131828 15,941468 15,743406 16,134078 16,321375 14,982906 15,943937 15,751093 14,793234 14,993593 14,8015 14,032984 13,470171 0,007125 313,335929
fs_write header(ms) 106,922187 103,05839 103,554593 90,226062 104,223218 105,395359 90,916812 104,399671 105,150812 92,190843 105,704218 105,226187 92,367265 106,114515 106,695609 93,768515 107,847609 107,856843 93,772125 110,138765 0,007343 2035,536941
fs_close data(ms) N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A 0,018687 0,018687
fs_close header(ms) N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A 1,681593 1,681593
fs_unlink data(ms) N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A N/A 4,410906 4,410906
                                            2362,418148

The downside of this method, is that maximum possible file to create is only half of the available memory, in order to have enough space for the copy afterwards.

Thank you for your help. Looking forward for the new version of littleFS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants