diff options
author | Tor Egge <Tor.Egge@broadpark.no> | 2021-02-10 14:48:36 +0100 |
---|---|---|
committer | Tor Egge <Tor.Egge@broadpark.no> | 2021-02-10 16:04:30 +0100 |
commit | 3f49fca252a0a7ddc5524c0daba04c0ddb86bda2 (patch) | |
tree | 5e192374f183c9c6273b0985250d66549b26c849 /vespalib | |
parent | 96faf76fdfd71551f52e9d038bad24e8f98bc301 (diff) |
Add memory allocator backed by a file.
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/src/tests/alloc/alloc_test.cpp | 28 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/util/CMakeLists.txt | 1 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp | 87 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/util/mmap_file_allocator.h | 33 |
4 files changed, 149 insertions, 0 deletions
diff --git a/vespalib/src/tests/alloc/alloc_test.cpp b/vespalib/src/tests/alloc/alloc_test.cpp index 4dbeba62ee1..056bac58d28 100644 --- a/vespalib/src/tests/alloc/alloc_test.cpp +++ b/vespalib/src/tests/alloc/alloc_test.cpp @@ -2,8 +2,10 @@ #include <vespa/vespalib/testkit/testapp.h> #include <vespa/vespalib/util/alloc.h> +#include <vespa/vespalib/util/mmap_file_allocator.h> #include <vespa/vespalib/util/exceptions.h> #include <cstddef> +#include <sys/mman.h> using namespace vespalib; using namespace vespalib::alloc; @@ -279,4 +281,30 @@ TEST("auto alloced mmap alloc can not be shrinked below HUGEPAGE_SIZE/2 + 1 ") { EXPECT_EQUAL(SZ, buf.size()); } +TEST("mmap file allocator works") +{ + MmapFileAllocator allocator("mmap-file-allocator-dir"); + auto alloc = Alloc::alloc_with_allocator(&allocator); + auto buf = alloc.create(0); + EXPECT_EQUAL(0u, allocator.get_end_offset()); + EXPECT_EQUAL(0u, buf.size()); + EXPECT_TRUE(buf.get() == nullptr); + buf = alloc.create(4); + EXPECT_LESS_EQUAL(4u, buf.size()); + EXPECT_TRUE(buf.get() != nullptr); + memcpy(buf.get(), "1st", 4); + auto buf2 = alloc.create(5); + EXPECT_LESS_EQUAL(5u, buf2.size()); + EXPECT_TRUE(buf2.get() != nullptr); + EXPECT_TRUE(buf.get() != buf2.get()); + memcpy(buf2.get(), "fine", 5); + EXPECT_FALSE(buf.resize_inplace(5)); + EXPECT_FALSE(buf.resize_inplace(3)); + EXPECT_NOT_EQUAL(0u, allocator.get_end_offset()); + int result = msync(buf.get(), buf.size(), MS_SYNC); + EXPECT_EQUAL(0, result); + result = msync(buf2.get(), buf2.size(), MS_SYNC); + EXPECT_EQUAL(0, result); +} + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/vespa/vespalib/util/CMakeLists.txt b/vespalib/src/vespa/vespalib/util/CMakeLists.txt index 1e3f7dc44e7..bee95ad22dc 100644 --- a/vespalib/src/vespa/vespalib/util/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/util/CMakeLists.txt @@ -31,6 +31,7 @@ vespa_add_library(vespalib_vespalib_util OBJECT left_right_heap.cpp lz4compressor.cpp md5.c + mmap_file_allocator.cpp printable.cpp priority_queue.cpp random.cpp diff --git a/vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp new file mode 100644 index 00000000000..821e3c86a67 --- /dev/null +++ b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.cpp @@ -0,0 +1,87 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "mmap_file_allocator.h" +#include <fcntl.h> +#include <sys/mman.h> +#include <cassert> + +namespace vespalib::alloc { + +namespace { + +const size_t page_size = std::max(getpagesize(), 4096); + +size_t round_to_page_size(size_t size) { + return (size + ((-size) & (page_size - 1))); +} + +} + +MmapFileAllocator::MmapFileAllocator(const vespalib::string& dir_name) + : _dir_name(dir_name), + _file(_dir_name + "/swapfile"), + _end_offset(0) +{ + mkdir(_dir_name, true); + _file.open(O_RDWR | O_CREAT | O_TRUNC, false); +} + +MmapFileAllocator::~MmapFileAllocator() +{ + assert(_allocations.empty()); + _file.close(); + _file.unlink(); + rmdir(_dir_name, true); +} + +MmapFileAllocator::PtrAndSize +MmapFileAllocator::alloc(size_t sz) const +{ + if (sz == 0) { + return PtrAndSize(nullptr, 0); // empty allocation + } + uint64_t offset = _end_offset; + sz = round_to_page_size(sz); + _end_offset += sz; + _file.resize(_end_offset); + void *buf = mmap(nullptr, sz, + PROT_READ | PROT_WRITE, + MAP_SHARED, + _file.getFileDescriptor(), + offset); + assert(buf != MAP_FAILED); + assert(buf != nullptr); + // Register allocation + auto ins_res = _allocations.emplace(buf, sz); + assert(ins_res.second); + return PtrAndSize(buf, sz); +} + + +void +MmapFileAllocator::free(PtrAndSize alloc) const +{ + if (alloc.second == 0) { + assert(alloc.first == nullptr); + return; // empty allocation + } + assert(alloc.first != nullptr); + // Check that matching allocation is registered + auto itr = _allocations.find(alloc.first); + assert(itr != _allocations.end()); + assert(itr->first == alloc.first); + assert(itr->second == alloc.second); + _allocations.erase(itr); + int retval = madvise(alloc.first, alloc.second, MADV_DONTNEED); + assert(retval == 0); + retval = munmap(alloc.first, alloc.second); + assert(retval == 0); +} + +size_t +MmapFileAllocator::resize_inplace(PtrAndSize, size_t) const +{ + return 0; +} + +} diff --git a/vespalib/src/vespa/vespalib/util/mmap_file_allocator.h b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.h new file mode 100644 index 00000000000..84f63c8fc61 --- /dev/null +++ b/vespalib/src/vespa/vespalib/util/mmap_file_allocator.h @@ -0,0 +1,33 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include "alloc.h" +#include <vespa/vespalib/io/fileutil.h> +#include <vespa/vespalib/stllike/string.h> +#include <map> + +namespace vespalib::alloc { + +/* + * Class handling memory allocations backed by one or more files. + * Not reentant. Should not be destructed before all allocations + * have been freed. + */ +class MmapFileAllocator : public MemoryAllocator { + vespalib::string _dir_name; + mutable File _file; + mutable uint64_t _end_offset; + mutable std::map<void *, size_t> _allocations; +public: + MmapFileAllocator(const vespalib::string& dir_name); + ~MmapFileAllocator(); + PtrAndSize alloc(size_t sz) const override; + void free(PtrAndSize alloc) const override; + size_t resize_inplace(PtrAndSize, size_t) const override; + + // For unit test + size_t get_end_offset() const noexcept { return _end_offset; } +}; + +} |