mirror of
https://github.com/vimpunk/mio.git
synced 2025-12-06 08:46:51 +08:00
Add experimental cache-hint support
This commit is contained in:
parent
7dfc49b197
commit
587812ab73
@ -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);
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user