Skip to content

Possibly redundant flush when seeking #996

@stefano-zanotti

Description

@stefano-zanotti

When seeking (lfs_file_seek_), LFS needs to flush out whatever is in its cache, and then refresh the cache with the block where the new file position is.
However, if the file is in read mode, and the new position is in the same block as the one currently in cache, it can just change the position variable, without any disk operation.
See here:

littlefs/lfs.c

Lines 3697 to 3720 in d01280e

if (
#ifndef LFS_READONLY
!(file->flags & LFS_F_WRITING)
#else
true
#endif
) {
int oindex = lfs_ctz_index(lfs, &(lfs_off_t){file->pos});
lfs_off_t noff = npos;
int nindex = lfs_ctz_index(lfs, &noff);
if (oindex == nindex
&& noff >= file->cache.off
&& noff < file->cache.off + file->cache.size) {
file->pos = npos;
file->off = noff;
return npos;
}
}
// write out everything beforehand, may be noop if rdonly
int err = lfs_file_flush(lfs, file);
if (err) {
return err;
}

The same could be done when writing too: if the new position is in the same block, we can avoid disk operations, and only trigger them on a subsequent sync/write.
However, LFS currently skips this optimization: it always flushes the file, if the cache is dirty.

I tried removing the check on LFS_F_WRITING, but my software misbehaved in different parts of the code, so I assume this sync is needed for some other reason.

Is it really necessary?
Can this optimizazion be implemented in some way?
Is this optimization impossible for some reason due to the internals of LFS?

In my use case, this missed optimization has a big impact on performance, since LFS is called using a pattern like the following:
SEEK 0, WRITE 2048
SEEK 0, WRITE 2048
SEEK 2048, WRITE 2048
SEEK 2048, WRITE 2048
etc.

That is, each write just needs to append 1KB at the end of the file, but it works with 2KB blocks, so it always rewrites a whole block (padded with 0s), unless it needs to switch to the next one.
This causes LFS to flush to disk even if it was not asked to do so by a sync/close call:
SEEK 0, flush, WRITE 2048
SEEK 0, flush*, WRITE 2048
SEEK 2048, flush, WRITE 2048
SEEK 2048, flush*, WRITE 2048

Instead, the flushes marked with * could be avoided: the data would be still in cache, and it would be overwritten by the following write.
Here I'm assuming that LFS's cache size is 2048, but other flushes could be avoided too, if the cache was bigger.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions