Abandoned data-backed cache, use indirect lfs3_data_t on stack
This abandons the data-backed cache idea due to concerns around
readability and maintainability. Mixing const/mutable buffers in
lfs3_data_t was not great.
Instead, we now just allocate an indirect lfs3_data_t on the stack in
lfs3_file_sync_ to avoid the previous undefined behavior.
This actually results in less stack usage total, due to lfs3_file_t
allocations in lfs3_set/read, and avoid the more long-term memory cost
in lfs3_file_t:
code stack ctx
before: 36832 2376 684
after: 36840 (+0.0%) 2368 (-0.3%) 684 (+0.0%)
Oh. And lfs3_file_sync_ isn't even on the stack hot-path, so this is a
net benefit over the previous cache -> data cast:
code stack ctx
before sa: 36844 2368 684
after sa: 36840 (-0.0%) 2368 (+0.0%) 684 (+0.0%)
Still less cool though.
This commit is contained in:
@@ -11586,7 +11586,7 @@ static inline void lfs3_file_discardcache(lfs3_file_t *file) {
|
||||
#ifndef LFS3_KVONLY
|
||||
file->cache.pos = 0;
|
||||
#endif
|
||||
file->cache.d.size = 0;
|
||||
file->cache.size = 0;
|
||||
}
|
||||
|
||||
#ifndef LFS3_KVONLY
|
||||
@@ -11611,7 +11611,7 @@ static inline lfs3_size_t lfs3_file_cachesize(lfs3_t *lfs3,
|
||||
|
||||
static inline lfs3_off_t lfs3_file_size_(const lfs3_file_t *file) {
|
||||
return lfs3_max(
|
||||
LFS3_IFDEF_KVONLY(0, file->cache.pos) + file->cache.d.size,
|
||||
LFS3_IFDEF_KVONLY(0, file->cache.pos) + file->cache.size,
|
||||
file->b.shrub.r.weight);
|
||||
}
|
||||
|
||||
@@ -11729,17 +11729,17 @@ int lfs3_file_opencfg_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
// the file cache, so make sure not to clobber it
|
||||
if (lfs3_o_iswrset(file->b.h.flags)) {
|
||||
file->b.h.flags |= LFS3_o_UNFLUSH;
|
||||
file->cache.d.u.buffer_ = file->cfg->cache_buffer;
|
||||
file->cache.buffer = file->cfg->cache_buffer;
|
||||
#ifndef LFS3_KVONLY
|
||||
file->cache.pos = 0;
|
||||
#endif
|
||||
file->cache.d.size = file->cfg->cache_size;
|
||||
file->cache.size = file->cfg->cache_size;
|
||||
} else if (file->cfg->cache_buffer) {
|
||||
file->cache.d.u.buffer_ = file->cfg->cache_buffer;
|
||||
file->cache.buffer = file->cfg->cache_buffer;
|
||||
} else {
|
||||
#ifndef LFS3_KVONLY
|
||||
file->cache.d.u.buffer_ = lfs3_malloc(lfs3_file_cachesize(lfs3, file));
|
||||
if (!file->cache.d.u.buffer_) {
|
||||
file->cache.buffer = lfs3_malloc(lfs3_file_cachesize(lfs3, file));
|
||||
if (!file->cache.buffer) {
|
||||
return LFS3_ERR_NOMEM;
|
||||
}
|
||||
#else
|
||||
@@ -11821,9 +11821,9 @@ int lfs3_file_opencfg_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
// small file wrset? can we atomically commit everything in one
|
||||
// commit? currently this is only possible via lfs3_set
|
||||
if (lfs3_o_iswrset(file->b.h.flags)
|
||||
&& file->cache.d.size <= lfs3->cfg->inline_size
|
||||
&& file->cache.d.size <= lfs3->cfg->fragment_size
|
||||
&& file->cache.d.size < lfs3->cfg->crystal_thresh) {
|
||||
&& file->cache.size <= lfs3->cfg->inline_size
|
||||
&& file->cache.size <= lfs3->cfg->fragment_size
|
||||
&& file->cache.size < lfs3->cfg->crystal_thresh) {
|
||||
// we need to mark as unsync for sync to do anything
|
||||
file->b.h.flags |= LFS3_o_UNSYNC;
|
||||
|
||||
@@ -11944,7 +11944,7 @@ static void lfs3_file_close_(lfs3_t *lfs3, const lfs3_file_t *file) {
|
||||
(void)lfs3;
|
||||
// clean up memory
|
||||
if (!file->cfg->cache_buffer) {
|
||||
lfs3_free(file->cache.d.u.buffer_);
|
||||
lfs3_free(file->cache.buffer);
|
||||
}
|
||||
|
||||
// are we orphaning a file?
|
||||
@@ -12165,14 +12165,14 @@ lfs3_ssize_t lfs3_file_read(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
lfs3_ssize_t d = lfs3_min(size, lfs3_file_size_(file) - pos_);
|
||||
|
||||
// any data in our cache?
|
||||
if (pos_ < file->cache.pos + file->cache.d.size
|
||||
&& file->cache.d.size != 0) {
|
||||
if (pos_ < file->cache.pos + file->cache.size
|
||||
&& file->cache.size != 0) {
|
||||
if (pos_ >= file->cache.pos) {
|
||||
lfs3_ssize_t d_ = lfs3_min(
|
||||
d,
|
||||
file->cache.d.size - (pos_ - file->cache.pos));
|
||||
file->cache.size - (pos_ - file->cache.pos));
|
||||
lfs3_memcpy(buffer_,
|
||||
&file->cache.d.u.buffer_[pos_ - file->cache.pos],
|
||||
&file->cache.buffer[pos_ - file->cache.pos],
|
||||
d_);
|
||||
|
||||
pos_ += d_;
|
||||
@@ -12207,13 +12207,13 @@ lfs3_ssize_t lfs3_file_read(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
// try to fill our cache with some data
|
||||
if (!lfs3_o_isunflush(file->b.h.flags)) {
|
||||
lfs3_ssize_t d_ = lfs3_file_readnext(lfs3, file,
|
||||
pos_, file->cache.d.u.buffer_, d);
|
||||
pos_, file->cache.buffer, d);
|
||||
if (d_ < 0) {
|
||||
LFS3_ASSERT(d != LFS3_ERR_NOENT);
|
||||
return d_;
|
||||
}
|
||||
file->cache.pos = pos_;
|
||||
file->cache.d.size = d_;
|
||||
file->cache.size = d_;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -13338,10 +13338,10 @@ lfs3_ssize_t lfs3_file_write(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
// note we need to clear the cache anyways to avoid any
|
||||
// out-of-date data
|
||||
file->cache.pos = pos + size - lfs3_file_cachesize(lfs3, file);
|
||||
lfs3_memcpy(file->cache.d.u.buffer_,
|
||||
lfs3_memcpy(file->cache.buffer,
|
||||
&buffer_[size - lfs3_file_cachesize(lfs3, file)],
|
||||
lfs3_file_cachesize(lfs3, file));
|
||||
file->cache.d.size = lfs3_file_cachesize(lfs3, file);
|
||||
file->cache.size = lfs3_file_cachesize(lfs3, file);
|
||||
|
||||
file->b.h.flags &= ~LFS3_o_UNFLUSH;
|
||||
written += size;
|
||||
@@ -13363,25 +13363,25 @@ lfs3_ssize_t lfs3_file_write(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
//
|
||||
if (!lfs3_o_isunflush(file->b.h.flags)
|
||||
|| (pos >= file->cache.pos
|
||||
&& pos <= file->cache.pos + file->cache.d.size
|
||||
&& pos <= file->cache.pos + file->cache.size
|
||||
&& pos
|
||||
< file->cache.pos
|
||||
+ lfs3_file_cachesize(lfs3, file))) {
|
||||
// unused cache? we can move it where we need it
|
||||
if (!lfs3_o_isunflush(file->b.h.flags)) {
|
||||
file->cache.pos = pos;
|
||||
file->cache.d.size = 0;
|
||||
file->cache.size = 0;
|
||||
}
|
||||
|
||||
lfs3_size_t d = lfs3_min(
|
||||
size,
|
||||
lfs3_file_cachesize(lfs3, file)
|
||||
- (pos - file->cache.pos));
|
||||
lfs3_memcpy(&file->cache.d.u.buffer_[pos - file->cache.pos],
|
||||
lfs3_memcpy(&file->cache.buffer[pos - file->cache.pos],
|
||||
buffer_,
|
||||
d);
|
||||
file->cache.d.size = lfs3_max(
|
||||
file->cache.d.size,
|
||||
file->cache.size = lfs3_max(
|
||||
file->cache.size,
|
||||
pos+d - file->cache.pos);
|
||||
|
||||
file->b.h.flags |= LFS3_o_UNFLUSH;
|
||||
@@ -13394,7 +13394,7 @@ lfs3_ssize_t lfs3_file_write(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
|
||||
// flush our cache so the above can't fail
|
||||
err = lfs3_file_flush_(lfs3, file,
|
||||
file->cache.pos, file->cache.d.u.buffer_, file->cache.d.size);
|
||||
file->cache.pos, file->cache.buffer, file->cache.size);
|
||||
if (err) {
|
||||
goto failed;
|
||||
}
|
||||
@@ -13458,13 +13458,13 @@ int lfs3_file_flush(lfs3_t *lfs3, lfs3_file_t *file) {
|
||||
if (lfs3_o_isunflush(file->b.h.flags)) {
|
||||
#ifdef LFS3_KVONLY
|
||||
err = lfs3_file_flushset_(lfs3, file,
|
||||
file->cache.d.u.buffer_, file->cache.d.size);
|
||||
file->cache.buffer, file->cache.size);
|
||||
if (err) {
|
||||
goto failed;
|
||||
}
|
||||
#else
|
||||
err = lfs3_file_flush_(lfs3, file,
|
||||
file->cache.pos, file->cache.d.u.buffer_, file->cache.d.size);
|
||||
file->cache.pos, file->cache.buffer, file->cache.size);
|
||||
if (err) {
|
||||
goto failed;
|
||||
}
|
||||
@@ -13504,6 +13504,7 @@ static int lfs3_file_sync_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
lfs3_data_t name_data;
|
||||
lfs3_rattr_t shrub_rattrs[1];
|
||||
lfs3_size_t shrub_rattr_count = 0;
|
||||
lfs3_data_t file_data;
|
||||
lfs3_shrubcommit_t shrub_commit;
|
||||
|
||||
// uncreated files must be unsync
|
||||
@@ -13547,7 +13548,7 @@ static int lfs3_file_sync_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
#ifndef LFS3_KVONLY
|
||||
LFS3_ASSERT(file->cache.pos == 0);
|
||||
#endif
|
||||
LFS3_ASSERT(file->cache.d.size == lfs3_file_size_(file));
|
||||
LFS3_ASSERT(file->cache.size == lfs3_file_size_(file));
|
||||
|
||||
// discard any lingering bshrub state
|
||||
#ifndef LFS3_KVONLY
|
||||
@@ -13556,10 +13557,13 @@ static int lfs3_file_sync_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
lfs3_file_discardbshrub(file);
|
||||
|
||||
// build a small shrub commit
|
||||
if (file->cache.d.size > 0) {
|
||||
if (file->cache.size > 0) {
|
||||
file_data = LFS3_DATA_BUF(
|
||||
file->cache.buffer,
|
||||
file->cache.size);
|
||||
shrub_rattrs[shrub_rattr_count++] = LFS3_RATTR_DATA(
|
||||
LFS3_TAG_DATA, +file->cache.d.size,
|
||||
&file->cache.d);
|
||||
LFS3_TAG_DATA, +file->cache.size,
|
||||
&file_data);
|
||||
|
||||
LFS3_ASSERT(shrub_rattr_count
|
||||
<= sizeof(shrub_rattrs)/sizeof(lfs3_rattr_t));
|
||||
@@ -13695,14 +13699,14 @@ static int lfs3_file_sync_(lfs3_t *lfs3, lfs3_file_t *file,
|
||||
//
|
||||
// note we need to be careful if caches have different
|
||||
// sizes, prefer the most recent data in this case
|
||||
lfs3_size_t d = file->cache.d.size - lfs3_min(
|
||||
lfs3_size_t d = file->cache.size - lfs3_min(
|
||||
lfs3_file_cachesize(lfs3, file_),
|
||||
file->cache.d.size);
|
||||
file->cache.size);
|
||||
file_->cache.pos = file->cache.pos + d;
|
||||
lfs3_memcpy(file_->cache.d.u.buffer_,
|
||||
file->cache.d.u.buffer_ + d,
|
||||
file->cache.d.size - d);
|
||||
file_->cache.d.size = file->cache.d.size - d;
|
||||
lfs3_memcpy(file_->cache.buffer,
|
||||
file->cache.buffer + d,
|
||||
file->cache.size - d);
|
||||
file_->cache.size = file->cache.size - d;
|
||||
|
||||
// update any custom attrs
|
||||
for (lfs3_size_t i = 0; i < file->cfg->attr_count; i++) {
|
||||
@@ -13778,10 +13782,10 @@ int lfs3_file_sync(lfs3_t *lfs3, lfs3_file_t *file) {
|
||||
// though don't flush quite yet if our file is small and can be
|
||||
// combined with sync in a single commit
|
||||
int err;
|
||||
if (!(file->cache.d.size == lfs3_file_size_(file)
|
||||
&& file->cache.d.size <= lfs3->cfg->inline_size
|
||||
&& file->cache.d.size <= lfs3->cfg->fragment_size
|
||||
&& file->cache.d.size < lfs3->cfg->crystal_thresh)) {
|
||||
if (!(file->cache.size == lfs3_file_size_(file)
|
||||
&& file->cache.size <= lfs3->cfg->inline_size
|
||||
&& file->cache.size <= lfs3->cfg->fragment_size
|
||||
&& file->cache.size < lfs3->cfg->crystal_thresh)) {
|
||||
err = lfs3_file_flush(lfs3, file);
|
||||
if (err) {
|
||||
goto failed;
|
||||
@@ -13991,12 +13995,12 @@ int lfs3_file_truncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
||||
}
|
||||
|
||||
// truncate our cache
|
||||
file->cache.d.size = lfs3_min(
|
||||
file->cache.d.size,
|
||||
file->cache.size = lfs3_min(
|
||||
file->cache.size,
|
||||
size_ - lfs3_min(file->cache.pos, size_));
|
||||
file->cache.pos = lfs3_min(file->cache.pos, size_);
|
||||
// mark as flushed if this completely truncates our cache
|
||||
if (file->cache.d.size == 0) {
|
||||
if (file->cache.size == 0) {
|
||||
lfs3_file_discardcache(file);
|
||||
}
|
||||
|
||||
@@ -14073,27 +14077,27 @@ int lfs3_file_fruncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size_) {
|
||||
}
|
||||
|
||||
// fruncate our cache
|
||||
lfs3_memmove(file->cache.d.u.buffer_,
|
||||
&file->cache.d.u.buffer_[lfs3_min(
|
||||
lfs3_memmove(file->cache.buffer,
|
||||
&file->cache.buffer[lfs3_min(
|
||||
lfs3_smax(
|
||||
size - size_ - file->cache.pos,
|
||||
0),
|
||||
file->cache.d.size)],
|
||||
file->cache.d.size - lfs3_min(
|
||||
file->cache.size)],
|
||||
file->cache.size - lfs3_min(
|
||||
lfs3_smax(
|
||||
size - size_ - file->cache.pos,
|
||||
0),
|
||||
file->cache.d.size));
|
||||
file->cache.d.size -= lfs3_min(
|
||||
file->cache.size));
|
||||
file->cache.size -= lfs3_min(
|
||||
lfs3_smax(
|
||||
size - size_ - file->cache.pos,
|
||||
0),
|
||||
file->cache.d.size);
|
||||
file->cache.size);
|
||||
file->cache.pos -= lfs3_smin(
|
||||
size - size_,
|
||||
file->cache.pos);
|
||||
// mark as flushed if this completely truncates our cache
|
||||
if (file->cache.d.size == 0) {
|
||||
if (file->cache.size == 0) {
|
||||
lfs3_file_discardcache(file);
|
||||
}
|
||||
|
||||
|
||||
@@ -636,7 +636,6 @@ typedef struct lfs3_data {
|
||||
lfs3_size_t size;
|
||||
union {
|
||||
const uint8_t *buffer;
|
||||
uint8_t *buffer_;
|
||||
struct {
|
||||
lfs3_block_t block;
|
||||
lfs3_size_t off;
|
||||
@@ -742,11 +741,14 @@ typedef struct lfs3_file {
|
||||
#endif
|
||||
|
||||
// in-RAM cache
|
||||
//
|
||||
// note this lines up with lfs3_data_t's buffer representation
|
||||
struct {
|
||||
#ifndef LFS3_KVONLY
|
||||
lfs3_off_t pos;
|
||||
#endif
|
||||
lfs3_data_t d;
|
||||
lfs3_off_t size;
|
||||
uint8_t *buffer;
|
||||
} cache;
|
||||
|
||||
// on-disk leaf bptr
|
||||
|
||||
Reference in New Issue
Block a user