-
Notifications
You must be signed in to change notification settings - Fork 798
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
Open speed and cache sizes #783
Comments
Do you have the tools to perform runtime code profiling? |
Hi @TjoBitDk these are some interesting results, thanks for sharing. One of main bottlenecks in littlefs is it's
The way littlefs stores metadata is in small, 2-block logs. These are quick to append to, but need to be "compacted" when they are full. The current compaction algorithm is fairly naive, being built with a bad assumption that these blocks are "small", and leads to runaway read operations. The reason for the spikes is that this cost is only paid when the metadata blocks are full. If they still have space append is pretty fast.
This may be caused by the block-level garbage collector, though it would take more investigation to confirm.
Just to check, are changing cache size independently of program size? littlefs generally always benefits from a smaller program size. Larger program sizes limit the granularity of what can be written and requires adding padding. This can lead to more compactions and wasted erases when files aren't aligned.
This is an unfortunate consequence of cache size determining inline-file limits. They were merged at one point to reduce the config options, but this was probably a mistake and they should be disentangled. Understanding why it behaves this way requires understanding how inline files work in littlefs. Normally, files are allocated and stored at the block level. But this is very wasteful for small files, so small files can be "inlined" directly into the metadata blocks. This is normally a net benefit, but very large inline files can increase the amount of metadata written, leading to more compactions. The measurements you have show this quite nicely. The threshold for inline files is currently controlled by two things:
The 1/8th block_size is probably why you're seeing a change in behavior at 512 bytes (4096/8). In theory the inline file threshold doesn't need to be bound to cache_size, as long as it's As for the Hopefully this at least helps explain what's going on. |
Hi Geky, Thanks for you technical explanation. Thomas |
@TjoBitDk, Thanks for sharing your findings and starting this great discussion. I hope that helps, and again, sorry for the slightly off-topic post. |
Sorry about the late response.
Currently, no. Unfortunately. Compaction only triggers when the block is full so there is no way to delay compaction. A wishlist item I've had would be to add a garbage collection routine that checks how full each metadata block is, and eagerly compacts if it's above a user-configured threshold. But this has been lower priority for me personally vs trying to solve the underlying issue in the on-disk data-structure. Such a "metadata-threshold" garbage collector tackles a different form of garbage than @evpopov's proposal. There's two forms of "garbage" that need to be collected in littlefs. Outdated metadata in metadata blocks, and unused blocks for block allocation. Unfortunately I don't think @evpopov's proposal or #610 would help in the case, though they may help with the larger spike you are seeing. It would be interesting to see both of the garbage collectors in commong gc routine that could be called in the background. I'll update @evpopov gc discussion with more info (#791) It is probably worth mentioning the Lines 261 to 265 in 6a53d76
This config optional artificially limits the metadata block size to put an upper limit on how long compact operations take. It can improve the speed, but wastes storage in metadata blocks. It's heavy handed, as it is a temporary workaround, but may be helpful in this case if latency is worth the storage tradeoff. |
Hi @TjoBitDk, thanks for sharing this. This is a really valuable analysis. |
I'm using NAND flash memory (W25N01GVZEIR) where each block of memory consists of 64 pages and each page contains 2048 bytes. Below shows the speeds to writing 1000 rows to a file 1 row at a time. A few questions:
|
Hi @CalebDueck, thanks for sharing.
The metadata commit layers are a bit complex, as commits can trigger a number of different operations (and a bit messy as the code has evolved over time). The begining of compaction is here, in This can end up:
(Internally, I think the easiest thing to do would be to take advantage of the fact that Triggering compaction could be done by:
If you do get something working, do make a PR, even if it's not user-ready it could be a good starting point and would be appreciated.
This is a good question. This means you end up needing effectively ~ Multiple files can share metadata pairs, though the metadata related to a file will never span multiple metadata pairs. Each file can be uniquely identified by its metadata pair + a pair-local id.
I don't think so. The metadata logs reside on disk. If anything uninitializing littlefs would lead to more work, since it needs to scan for free blocks on the first block allocation after |
Hi @geky, thanks for the details. I've talked with my team and we will put this on hold as we have found that it is running quickly enough for our needs as of right now. Thanks for all your help! |
|
Hi
We have done a lot of performance test with LittleFS and some of the results don’t add up. Maybe someone can tell the why this is so and explain what we see.
We are using a STM32 H7 and a Micron Data flash running with a 66 MHz SPI clock.
We use LittleFS 2.4.5 but have tried with the latest 2.5.1 and the result is somehow similar. But it is a bit faster, and chop offs a few 100 ms. where file creation is worst.
File creation time (Open and Sync)
We have concentrated about file creation times (So an open and sync). We tested with different cache/prog sizes and file sizes. We created 100 files in the root folder and measured the time in milliseconds for doing an open and sync. Then we write data to the file and close it. Then create a new until we reach 100 files. If we tested with 128 file size, 128 byte was written to each file.
As expected, the file creation time goes up when more and more files are in the folder. But some of the create time takes like seconds to do. I have checked, the time is not used in the data flash driver. This also changes on the file sizes and cache size.
Some measurements with file size 0, 128, 256 and 512 bytes
All above 512 bytes looks the same. Even if every file has a size of 11KB
For file size 0, there are some spikes and file no 92 takes almost 1.4 seconds.
If the file size is the cache buffer size, it seems to perform very well, but outside we se these spikes!
File size above 512 file no 81 takes more than 3.5 seconds. (Up until 11 KB file size)
Ran with this config.
Block size: 4_KB
Read size: 32
Program size 256
Lookahead size 256
Cache size 256
Why do we see this? Is there a good explanation?
Here is the same teste but with different cache sizes. With higher cache size we got frequently more spikes?
Cache and program buffer size impact:
When we look at the cache and program buffer sizes, we see this. Same test but I just log the time for the file that toke the longest to create. Increment the file size with 32 bytes for each loop.
Pseudo code:
Loop until file size is 1 KB
Format
Mount
Loop 100 times: Start time – open – sync – diff time – write file size data to file – close – Long highest diff
Increment file size with 32 bytes
Is this what to excpet?
Best regards
Thomas
The text was updated successfully, but these errors were encountered: