Add experimental cache-hint support

This commit is contained in:
mandreyel 2018-07-27 14:26:17 +02:00
parent 7dfc49b197
commit 587812ab73
6 changed files with 91 additions and 46 deletions

View File

@ -25,6 +25,8 @@
#include <string>
#include <system_error>
#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<typename String>
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);

View File

@ -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<typename String>
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<ByteT>::handle_type basic_mmap<ByteT>::mapping_handle() cons
template<typename ByteT>
template<typename String>
void basic_mmap<ByteT>::map(String& path, size_type offset,
size_type length, access_mode mode, std::error_code& error)
void basic_mmap<ByteT>::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<ByteT>::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<ByteT>::map(String& path, size_type offset,
}
template<typename ByteT>
void basic_mmap<ByteT>::map(handle_type handle, size_type offset,
size_type length, access_mode mode, std::error_code& error)
void basic_mmap<ByteT>::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<ByteT>::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<typename ByteT>

View File

@ -22,6 +22,7 @@
#define MIO_MMAP_HEADER
#include "detail/basic_mmap.hpp"
#include "page.hpp"
#include <system_error>
@ -70,10 +71,11 @@ public:
* establishing the mapping is thrown.
*/
template<typename String>
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<typename String>
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<unsigned char>;
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<typename MappingToken>
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<mmap_source>(token, offset, length, error);
return make_mmap<mmap_source>(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<typename MappingToken>
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<mmap_sink>(token, offset, length, error);
return make_mmap<mmap_sink>(token, offset, length, error, hint);
}
} // namespace mio

View File

@ -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

View File

@ -92,10 +92,10 @@ public:
* establishing the mapping is thrown.
*/
template<typename String>
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<typename String>
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<typename MappingToken>
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<mmap_type>(token, offset, length, error);
mmap_type mmap = make_mmap<mmap_type>(token, offset, length, error, hint);
if(error) { return; }
pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
}
else
{
pimpl_->map(token, offset, length, AccessMode, error);
pimpl_->map(token, offset, length, AccessMode, error, hint);
}
}
};

View File

@ -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.