From 587812ab73fd3d028891298907ddaab00021f407 Mon Sep 17 00:00:00 2001 From: mandreyel Date: Fri, 27 Jul 2018 14:26:17 +0200 Subject: [PATCH] Add experimental cache-hint support --- include/mio/detail/basic_mmap.hpp | 16 ++++++----- include/mio/detail/basic_mmap.ipp | 44 ++++++++++++++++++++++++------- include/mio/mmap.hpp | 38 ++++++++++++++------------ include/mio/page.hpp | 12 +++++++++ include/mio/shared_mmap.hpp | 25 +++++++++--------- test/example.cpp | 2 +- 6 files changed, 91 insertions(+), 46 deletions(-) diff --git a/include/mio/detail/basic_mmap.hpp b/include/mio/detail/basic_mmap.hpp index 512d537..844baf3 100644 --- a/include/mio/detail/basic_mmap.hpp +++ b/include/mio/detail/basic_mmap.hpp @@ -25,6 +25,8 @@ #include #include +#include "../page.hpp" + #ifdef _WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN @@ -73,6 +75,11 @@ private: // Points to the first requested byte, and not to the actual start of the mapping. pointer data_ = nullptr; + // Length, in bytes, requested by user, which may not be the length of the full + // mapping, and the entire length of the full mapping. + size_type length_ = 0; + size_type mapped_length_ = 0; + // On POSIX, we only need a file handle to create a mapping, while on Windows // systems the file handle is necessary to retrieve a file mapping handle, but any // subsequent operations on the mapped region must be done through the latter. @@ -81,11 +88,6 @@ private: handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; #endif - // Length, in bytes, requested by user, which may not be the length of the full - // mapping, and the entire length of the full mapping. - size_type length_ = 0; - size_type mapped_length_ = 0; - // Letting user map a file using both an existing file handle and a path introcudes // some complexity in that we must not close the file handle if user provided it, // but we must close it if we obtained it using the provided path. For this reason, @@ -135,9 +137,9 @@ public: template void map(String& path, size_type offset, size_type length, - access_mode mode, std::error_code& error); + access_mode mode, std::error_code& error, cache_hint hint); void map(handle_type handle, size_type offset, size_type length, - access_mode mode, std::error_code& error); + access_mode mode, std::error_code& error, cache_hint hint); void unmap(); void sync(std::error_code& error); diff --git a/include/mio/detail/basic_mmap.ipp b/include/mio/detail/basic_mmap.ipp index 1fd4e70..3506c17 100644 --- a/include/mio/detail/basic_mmap.ipp +++ b/include/mio/detail/basic_mmap.ipp @@ -61,10 +61,14 @@ inline std::error_code last_error() noexcept return error; } +// Windows API hints: +// https://docs.microsoft.com/en-us/windows/desktop/memory/creating-a-view-within-a-file + template -file_handle_type open_file(const String& path, - const access_mode mode, std::error_code& error) +file_handle_type open_file(const String& path, const access_mode mode, + std::error_code& error, const cache_hint hint) { + // TODO implement cache_hint hint error.clear(); if(detail::empty(path)) { @@ -72,12 +76,23 @@ file_handle_type open_file(const String& path, return INVALID_HANDLE_VALUE; } #ifdef _WIN32 + const auto file_attribute = [hint] + { + switch(hint) { + case cache_hint::normal: + return FILE_ATTRIBUTE_NORMAL; + case cache_hint::random: + return FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; + case cache_hint::sequential: + return FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN; + } + }(); const auto handle = ::CreateFile(c_str(path), mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, + file_attribute, 0); #else const auto handle = ::open(c_str(path), @@ -244,8 +259,8 @@ typename basic_mmap::handle_type basic_mmap::mapping_handle() cons template template -void basic_mmap::map(String& path, size_type offset, - size_type length, access_mode mode, std::error_code& error) +void basic_mmap::map(String& path, size_type offset, size_type length, + access_mode mode, std::error_code& error, cache_hint hint) { error.clear(); if(detail::empty(path)) @@ -253,9 +268,9 @@ void basic_mmap::map(String& path, size_type offset, error = std::make_error_code(std::errc::invalid_argument); return; } - const auto handle = open_file(path, mode, error); + const auto handle = open_file(path, mode, error, hint); if(error) { return; } - map(handle, offset, length, mode, error); + map(handle, offset, length, mode, error, hint); // This MUST be after the call to map, as that sets this to true. if(!error) { @@ -264,8 +279,8 @@ void basic_mmap::map(String& path, size_type offset, } template -void basic_mmap::map(handle_type handle, size_type offset, - size_type length, access_mode mode, std::error_code& error) +void basic_mmap::map(handle_type handle, size_type offset, size_type length, + access_mode mode, std::error_code& error, cache_hint hint) { error.clear(); if(handle == INVALID_HANDLE_VALUE) @@ -305,6 +320,17 @@ void basic_mmap::map(handle_type handle, size_type offset, file_mapping_handle_ = ctx.file_mapping_handle; #endif } + +#ifndef _WIN32 + if(hint != cache_hint::normal) + { + if(madvise(data_, mapped_length_, + hint == cache_hint::random ? MADV_RANDOM : MADV_SEQUENTIAL) == -1) + { + error = last_error(); + } + } +#endif } template diff --git a/include/mio/mmap.hpp b/include/mio/mmap.hpp index 7c1b0be..f4d6ff3 100644 --- a/include/mio/mmap.hpp +++ b/include/mio/mmap.hpp @@ -22,6 +22,7 @@ #define MIO_MMAP_HEADER #include "detail/basic_mmap.hpp" +#include "page.hpp" #include @@ -70,10 +71,11 @@ public: * establishing the mapping is thrown. */ template - basic_mmap(const String& path, const size_type offset, const size_type length) + basic_mmap(const String& path, const size_type offset, const size_type length, + const cache_hint hint = cache_hint::normal) { std::error_code error; - map(path, offset, length, error); + map(path, offset, length, error, hint); if(error) { throw error; } } @@ -84,7 +86,7 @@ public: basic_mmap(const handle_type handle, const size_type offset, const size_type length) { std::error_code error; - map(handle, offset, length, error); + map(handle, offset, length, error, cache_hint::normal); if(error) { throw error; } } @@ -216,10 +218,10 @@ public: * case a mapping of the entire file is created. */ template - void map(const String& path, const size_type offset, - const size_type length, std::error_code& error) + void map(const String& path, const size_type offset, const size_type length, + std::error_code& error, const cache_hint hint = cache_hint::normal) { - impl_.map(path, offset, length, AccessMode, error); + impl_.map(path, offset, length, AccessMode, error, hint); } /** @@ -242,9 +244,9 @@ public: * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, - const size_type length, std::error_code& error) + const size_type length, std::error_code& error) { - impl_.map(handle, offset, length, AccessMode, error); + impl_.map(handle, offset, length, AccessMode, error, cache_hint::normal); } /** @@ -330,11 +332,11 @@ using ummap_sink = basic_mmap_sink; template< typename MMap, typename MappingToken -> MMap make_mmap(const MappingToken& token, - int64_t offset, int64_t length, std::error_code& error) +> MMap make_mmap(const MappingToken& token, const int64_t offset, const int64_t length, + std::error_code& error, const cache_hint hint = cache_hint::normal) { MMap mmap; - mmap.map(token, offset, length, error); + mmap.map(token, offset, length, error, hint); return mmap; } @@ -346,10 +348,11 @@ template< * mmap_source::file_handle. */ template -mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, - mmap_source::size_type length, std::error_code& error) +mmap_source make_mmap_source(const MappingToken& token, + const mmap_source::size_type offset, const mmap_source::size_type length, + std::error_code& error, const cache_hint hint = cache_hint::normal) { - return make_mmap(token, offset, length, error); + return make_mmap(token, offset, length, error, hint); } /** @@ -360,10 +363,11 @@ mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type o * mmap_sink::file_handle. */ template -mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, - mmap_sink::size_type length, std::error_code& error) +mmap_sink make_mmap_sink(const MappingToken& token, const mmap_sink::size_type offset, + const mmap_sink::size_type length, std::error_code& error, + const cache_hint hint = cache_hint::normal) { - return make_mmap(token, offset, length, error); + return make_mmap(token, offset, length, error, hint); } } // namespace mio diff --git a/include/mio/page.hpp b/include/mio/page.hpp index c5a73b4..edbfdc2 100644 --- a/include/mio/page.hpp +++ b/include/mio/page.hpp @@ -66,6 +66,18 @@ inline size_t make_offset_page_aligned(size_t offset) noexcept return offset / page_size_ * page_size_; } +enum class cache_hint +{ + // No special treatment (this is the default). + normal, + // Expect page references in random order, so decrease read-ahead. + random, + // Expect page references in sequential order, which instructs the kernel to + // aggressively read ahead the mapped pages and free them soon after + // accessing them. + sequential, +}; + } // namespace mio #endif // MIO_PAGE_HEADER diff --git a/include/mio/shared_mmap.hpp b/include/mio/shared_mmap.hpp index d1a4a2e..fe54255 100644 --- a/include/mio/shared_mmap.hpp +++ b/include/mio/shared_mmap.hpp @@ -92,10 +92,10 @@ public: * establishing the mapping is thrown. */ template - basic_shared_mmap(const String& path, const size_type offset, const size_type length) + basic_shared_mmap(const String& path, const size_type offset, const size_type length, const cache_hint hint = cache_hint::normal) { std::error_code error; - map(path, offset, length, error); + map(path, offset, length, error, hint); if(error) { throw error; } } @@ -103,10 +103,11 @@ public: * The same as invoking the `map` function, except any error that may occur while * establishing the mapping is thrown. */ - basic_shared_mmap(const handle_type handle, const size_type offset, const size_type length) + basic_shared_mmap(const handle_type handle, const size_type offset, + const size_type length) { std::error_code error; - map(handle, offset, length, error); + map(handle, offset, length, error, cache_hint::normal); if(error) { throw error; } } @@ -231,10 +232,10 @@ public: * case a mapping of the entire file is created. */ template - void map(const String& path, const size_type offset, - const size_type length, std::error_code& error) + void map(const String& path, const size_type offset, const size_type length, + std::error_code& error, const cache_hint hint = cache_hint::normal) { - map_impl(path, offset, length, error); + map_impl(path, offset, length, error, hint); } /** @@ -257,9 +258,9 @@ public: * case a mapping of the entire file is created. */ void map(const handle_type handle, const size_type offset, - const size_type length, std::error_code& error) + const size_type length, std::error_code& error) { - map_impl(handle, offset, length, error); + map_impl(handle, offset, length, error, cache_hint::normal); } /** @@ -316,17 +317,17 @@ public: private: template void map_impl(const MappingToken& token, const size_type offset, - const size_type length, std::error_code& error) + const size_type length, std::error_code& error, const cache_hint hint) { if(!pimpl_) { - mmap_type mmap = make_mmap(token, offset, length, error); + mmap_type mmap = make_mmap(token, offset, length, error, hint); if(error) { return; } pimpl_ = std::make_shared(std::move(mmap)); } else { - pimpl_->map(token, offset, length, AccessMode, error); + pimpl_->map(token, offset, length, AccessMode, error, hint); } } }; diff --git a/test/example.cpp b/test/example.cpp index 304e3e6..5dd3044 100644 --- a/test/example.cpp +++ b/test/example.cpp @@ -17,7 +17,7 @@ int main() // length of the mapping is otherwise expected, with the factory method. std::error_code error; mio::mmap_sink rw_mmap = mio::make_mmap_sink( - "file.txt", 0, mio::map_entire_file, error); + "file.txt", 0, mio::map_entire_file, error, mio::cache_hint::random); if (error) { return handle_error(error); } // You can use any iterator based function.