-
Notifications
You must be signed in to change notification settings - Fork 81
feat(io): add Resize API for efficient storage allocation #1571
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -218,6 +218,24 @@ class BasicIO { | |
| } | ||
| } | ||
|
|
||
| inline void | ||
| Resize(uint64_t size) { | ||
| if constexpr (has_ResizeImpl<IOTmpl>::value) { | ||
| return cast().ResizeImpl(size); | ||
| } else { | ||
| if (size <= this->size_) { | ||
| return; | ||
| } | ||
| ByteBuffer buffer(SERIALIZE_BUFFER_SIZE, this->allocator_); | ||
| uint64_t offset = this->size_; | ||
| while (offset < size) { | ||
| auto cur_size = std::min(SERIALIZE_BUFFER_SIZE, size - offset); | ||
| this->Write(buffer.data, cur_size, offset); | ||
| offset += cur_size; | ||
| } | ||
| } | ||
|
Comment on lines
+225
to
+236
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fallback implementation for } else {
if (size > this->size_) {
// A single write of 0 bytes at the target size is enough to
// trigger the size update in WriteImpl, which is more efficient
// than writing data in a loop.
this->Write(nullptr, 0, size);
}
} |
||
| } | ||
|
|
||
| inline int64_t | ||
| GetMemoryUsage() const { | ||
| return this->size_; | ||
|
|
@@ -314,5 +332,6 @@ class BasicIO { | |
| std::declval<uint64_t>()) | ||
| GENERATE_HAS_MEMBER_FUNCTION(ReleaseImpl, void, std::declval<const uint8_t*>()) | ||
| GENERATE_HAS_MEMBER_FUNCTION(InitIOImpl, void, std::declval<const IOParamPtr&>()) | ||
| GENERATE_HAS_MEMBER_FUNCTION(ResizeImpl, void, std::declval<uint64_t>()) | ||
| }; | ||
| } // namespace vsag | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -58,6 +58,19 @@ BufferIO::WriteImpl(const uint8_t* data, uint64_t size, uint64_t offset) { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| void | ||||||
| BufferIO::ResizeImpl(uint64_t size) { | ||||||
| #ifdef __APPLE__ | ||||||
| auto ret = ftruncate(this->fd_, static_cast<off_t>(size)); | ||||||
| #else | ||||||
| auto ret = ftruncate64(this->fd_, static_cast<int64_t>(size)); | ||||||
| #endif | ||||||
| if (ret == -1) { | ||||||
| throw VsagException(ErrorType::INTERNAL_ERROR, "ftruncate failed"); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to
Suggested change
|
||||||
| } | ||||||
| this->size_ = size; | ||||||
| } | ||||||
|
|
||||||
| bool | ||||||
| BufferIO::ReadImpl(uint64_t size, uint64_t offset, uint8_t* data) const { | ||||||
| if (size == 0) { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,6 +105,60 @@ MMapIO::WriteImpl(const uint8_t* data, uint64_t size, uint64_t offset) { | |
| memcpy(this->start_ + offset, data, size); | ||
| } | ||
|
|
||
| void | ||
| MMapIO::ResizeImpl(uint64_t size) { | ||
| auto new_size = size; | ||
| auto old_size = this->size_; | ||
| if (old_size == 0) { | ||
| old_size = DEFAULT_INIT_MMAP_SIZE; | ||
| } | ||
| if (new_size > old_size) { | ||
| auto ret = | ||
| #ifdef __APPLE__ | ||
| ftruncate(this->fd_, static_cast<off_t>(new_size)); | ||
| #else | ||
| ftruncate64(this->fd_, static_cast<int64_t>(new_size)); | ||
| #endif | ||
| if (ret == -1) { | ||
| throw VsagException(ErrorType::INTERNAL_ERROR, "ftruncate failed"); | ||
| } | ||
|
|
||
| #ifdef __APPLE__ | ||
| munmap(this->start_, old_size); | ||
| void* addr = mmap(nullptr, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd_, 0); | ||
| if (addr == MAP_FAILED) { | ||
| throw VsagException(ErrorType::INTERNAL_ERROR, "mmap remap failed"); | ||
| } | ||
| this->start_ = static_cast<uint8_t*>(addr); | ||
| #else | ||
| this->start_ = | ||
| static_cast<uint8_t*>(mremap(this->start_, old_size, new_size, MREMAP_MAYMOVE)); | ||
| #endif | ||
| } else if (new_size < old_size) { | ||
| #ifdef __APPLE__ | ||
| munmap(this->start_, old_size); | ||
| void* addr = mmap(nullptr, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd_, 0); | ||
| if (addr == MAP_FAILED) { | ||
| throw VsagException(ErrorType::INTERNAL_ERROR, "mmap remap failed"); | ||
| } | ||
| this->start_ = static_cast<uint8_t*>(addr); | ||
| #else | ||
| this->start_ = | ||
| static_cast<uint8_t*>(mremap(this->start_, old_size, new_size, MREMAP_MAYMOVE)); | ||
| #endif | ||
| auto ret = | ||
| #ifdef __APPLE__ | ||
| ftruncate(this->fd_, static_cast<off_t>(new_size)); | ||
| #else | ||
| ftruncate64(this->fd_, static_cast<int64_t>(new_size)); | ||
| #endif | ||
| if (ret == -1) { | ||
| throw VsagException(ErrorType::INTERNAL_ERROR, "ftruncate failed"); | ||
| } | ||
| } | ||
| this->size_ = new_size; | ||
| } | ||
|
Comment on lines
+109
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This implementation of
I've provided a suggested refactoring that addresses all these points by extracting the remap and truncate logic into lambdas, adding error checking, and improving error messages. void
MMapIO::ResizeImpl(uint64_t size) {
auto new_size = size;
if (new_size == this->size_) {
return;
}
auto old_size = this->size_;
if (old_size == 0) {
old_size = DEFAULT_INIT_MMAP_SIZE;
}
auto remap_memory = [&]() {
#ifdef __APPLE__
munmap(this->start_, old_size);
void* addr = mmap(nullptr, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd_, 0);
if (addr == MAP_FAILED) {
throw VsagException(ErrorType::INTERNAL_ERROR, fmt::format("mmap remap failed: {}", strerror(errno)));
}
this->start_ = static_cast<uint8_t*>(addr);
#else
void* new_addr = mremap(this->start_, old_size, new_size, MREMAP_MAYMOVE);
if (new_addr == MAP_FAILED) {
throw VsagException(ErrorType::INTERNAL_ERROR, fmt::format("mremap failed: {}", strerror(errno)));
}
this->start_ = static_cast<uint8_t*>(new_addr);
#endif
};
auto truncate_file = [&]() {
auto ret =
#ifdef __APPLE__
ftruncate(this->fd_, static_cast<off_t>(new_size));
#else
ftruncate64(this->fd_, static_cast<int64_t>(new_size));
#endif
if (ret == -1) {
throw VsagException(ErrorType::INTERNAL_ERROR, fmt::format("ftruncate failed: {}", strerror(errno)));
}
};
if (new_size > old_size) {
truncate_file();
remap_memory();
} else if (new_size < old_size) {
remap_memory();
truncate_file();
}
this->size_ = new_size;
} |
||
|
|
||
| bool | ||
| MMapIO::ReadImpl(uint64_t size, uint64_t offset, uint8_t* data) const { | ||
| if (offset + size > this->size_) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message for
ftruncateis a bit generic. Including the system error message usingstrerror(errno)would provide more context for debugging, which is a common practice elsewhere in this file.