Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/bitcoin/database/impl/memory/mmap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace database {
// Unstaged aggregates transfer in full, and shared heads map their files.
TEMPLATE
CLASS::mmap(const path& filename, const storage_settings& settings,
bool random, bool staged) NOEXCEPT
bool random, bool staged, bool shared) NOEXCEPT
requires (is_one(columns))
: filenames_{ filename },
minimum_(std::max(one, to_rows(settings.size))),
Expand All @@ -46,7 +46,8 @@ CLASS::mmap(const path& filename, const storage_settings& settings,
zeroed_(settings.zeroed),
random_(random),
staged_(staged),
managed_(!staged && !head_shared),
head_shared_(!staged && shared),
managed_(!staged && !shared),
opened_{ file::invalid }
{
}
Expand All @@ -63,6 +64,7 @@ CLASS::mmap(const paths& filenames, const storage_settings& settings,
zeroed_(settings.zeroed),
random_(random),
staged_(staged),
head_shared_(false),
managed_(false),
opened_{}
{
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/database/impl/memory/mmap_private.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ void CLASS::worker_start_() NOEXCEPT
{
#if defined(MANAGE_STAGING)
// A shared head has no lazy writer (the kernel writes its mapping back).
if (!staged_ && head_shared)
if (head_shared_)
return;

limit_ = system_memory() / throttle_factor;
Expand Down
11 changes: 6 additions & 5 deletions include/bitcoin/database/impl/memory/mmap_staging.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ bool CLASS::persist_(size_t to) NOEXCEPT
const auto from = to_width<Column>(settled_.load());
return staged_ ? ((from >= to) || pwrite_all(opened_[Column],
std::next(memory_map_[Column], from), to - from, from)) :
(head_shared || shared_.load()) ?
(::msync(memory_map_[Column], to, MS_SYNC) != fail) :
(head_shared_ || shared_.load()) ?
(is_zero(to) ||
(::msync(memory_map_[Column], to, MS_SYNC) != fail)) :
transfer_<Column>(to);
}

Expand Down Expand Up @@ -194,7 +195,7 @@ bool CLASS::stage_() NOEXCEPT

// A shared head maps its file in place of commitment (content is the
// mapping, so there is nothing to populate).
if (!staged_ && head_shared)
if (head_shared_)
{
if (mmap_share(memory_map_[Column], target, opened_[Column],
zero) == fail)
Expand Down Expand Up @@ -281,7 +282,7 @@ bool CLASS::commit_(size_t size, bool final) NOEXCEPT
{
// A shared head extends its file mapping over the growth (the file is
// provisioned to capacity, so the extension is already allocated).
if (!staged_ && (head_shared || shared_.load()))
if (head_shared_ || (!staged_ && shared_.load()))
{
const auto grown = page_floor(to_width<Column>(capacity_.load()));

Expand Down Expand Up @@ -340,7 +341,7 @@ bool CLASS::commit_(size_t size, bool final) NOEXCEPT

// A shared head remaps its file onto the replacement (the file is the
// content, so migration copies nothing).
if (!staged_ && (head_shared || shared_.load()))
if (head_shared_ || (!staged_ && shared_.load()))
{
if (mmap_share(replace, target, opened_[Column], zero) == fail)
{
Expand Down
4 changes: 0 additions & 4 deletions include/bitcoin/database/impl/primitives/hashhead.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,7 @@ uint32_t CLASS::derive_buckets(uint64_t expected, uint32_t low,
constexpr auto gigabyte = power2<uint64_t>(30u);
constexpr auto megabyte = power2<uint64_t>(20u);
constexpr auto uncontested = power2<uint64_t>(35u);
#if defined(HAVE_APPLE)
constexpr auto contested = 10u * gigabyte;
#else
constexpr auto contested = 8u * gigabyte;
#endif

if (is_zero(expected) || is_zero(low) || is_zero(high))
return {};
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/database/impl/store/store.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ CLASS::store(const settings& config) NOEXCEPT
candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), head_settings<schema::height::cell>(config.candidate), sequential),
confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), head_settings<schema::height::cell>(config.confirmed), sequential),

strong_head_(head(config.path / schema::dir::heads, schema::indexes::strong), head_settings<schema::strong::cell>(config.strong), random),
strong_head_(head(config.path / schema::dir::heads, schema::indexes::strong), head_settings<schema::strong::cell>(config.strong, advice::sequential), sequential, !staged, shared),
scan_head_(head(config.path / schema::dir::heads, schema::indexes::scan), head_settings(config.scan), random),
scan_body_(body(config.path, schema::indexes::scan), config.scan, sequential, staged),
scan_prefix_head_(head(config.path / schema::dir::heads, schema::indexes::scan_prefix), head_settings(config.scan), sequential),
Expand Down
12 changes: 2 additions & 10 deletions include/bitcoin/database/memory/mmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class mmap

/// Scalar construction (columns == 1).
mmap(const path& filename, const storage_settings& settings={},
bool random=true, bool staged=false) NOEXCEPT
bool random=true, bool staged=false, bool shared=false) NOEXCEPT
requires (is_one(columns));

/// Aggregate construction (columns > 1): one file per column, shared guards.
Expand Down Expand Up @@ -294,15 +294,6 @@ class mmap
static constexpr bool head_release = false;
#endif

// Map heads writable-shared from their files (the native windows model)
// instead of anonymously with a dirty-page writer. Head reclaim is then
// kernel writeback of a bounded rewrite-in-place mapping (clean pages
// drop) rather than swap, which anonymous pages alone require. Bodies
// remain staged, so the unbounded append writeback that motivates dirty
// ratio tuning does not return with it. Excludes head_release (nothing
// to release) and the dirty bitmap (nothing to transfer).
static constexpr bool head_shared = false;

// column dispatch, not thread safe.
template <size_t... Index>
bool flush_all_(size_t rows, std::index_sequence<Index...>) NOEXCEPT;
Expand Down Expand Up @@ -433,6 +424,7 @@ class mmap
const bool zeroed_;
const bool random_;
const bool staged_;
const bool head_shared_;
const bool managed_;

// These are thread safe (atomic).
Expand Down
14 changes: 10 additions & 4 deletions include/bitcoin/database/store.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,29 +314,35 @@ class store

// Heads share the table growth rate.
static constexpr storage_settings head_settings(
const settings::simple_table& table, uint64_t minimum=one) NOEXCEPT
const settings::simple_table& table, uint64_t minimum=one,
advice access=advice::random) NOEXCEPT
{
return
{
.size = std::max<uint64_t>(one, minimum),
.rate = table.rate,
.access = advice::random,
.access = access,
.zeroed = false
};
}

template <size_t Cell>
static constexpr storage_settings head_settings(
const settings::bucket_table& table) NOEXCEPT
const settings::bucket_table& table,
advice access=advice::random) NOEXCEPT
{
return head_settings(table,
system::ceilinged_multiply<uint64_t>(table.buckets, Cell));
system::ceilinged_multiply<uint64_t>(table.buckets, Cell), access);
}

// Bodies are append-only, so stage writes in anonymous memory where the
// staging backend is built (heads update in place and remain resident).
static constexpr bool staged = true;

// A head appended in link order goes cold behind its frontier, so it
// maps its file (droppable pages) rather than holding its content in RAM.
static constexpr bool shared = true;

static inline path head(const path& folder, const std::string& name) NOEXCEPT
{
return folder / (name + schema::ext::head);
Expand Down
51 changes: 51 additions & 0 deletions test/memory/mmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,57 @@ BOOST_AUTO_TEST_CASE(mmap__unstaged__rewrite_below_flush__expected)
BOOST_REQUIRE(!reopened.get_fault());
}

BOOST_AUTO_TEST_CASE(mmap__shared__rewrite_below_flush__expected)
{
constexpr size_t size = 10'000;
constexpr size_t rewrite = 100;
const std::string file = TEST_PATH;
BOOST_REQUIRE(test::create(file));

// Shared head writes through its file mapping (no mark or transfer).
map instance(file, { 1, 50 }, true, false, true);
BOOST_REQUIRE(!instance.open());
BOOST_REQUIRE(!instance.load());

const auto original = stage_vector(stage_pattern, size);
const auto replaced = stage_vector(stage_repattern, rewrite);

auto expected = stage_vector(stage_repattern, rewrite);
const auto retained = stage_vector(stage_pattern, size - rewrite, rewrite);
expected.insert(expected.end(), retained.begin(), retained.end());

auto memory = instance.get(instance.allocate(size));
BOOST_REQUIRE(memory);
std::copy_n(original.begin(), original.size(), memory.begin());
memory.reset();
BOOST_REQUIRE(!instance.flush());

memory = instance.get();
BOOST_REQUIRE(memory);
std::copy_n(replaced.begin(), replaced.size(), memory.begin());
memory.reset();
BOOST_REQUIRE(!instance.flush());
BOOST_REQUIRE(!instance.unload());
BOOST_REQUIRE(!instance.close());
BOOST_REQUIRE(!instance.get_fault());

map reopened(file, { 1, 50 }, true, false, true);
BOOST_REQUIRE(!reopened.open());
BOOST_REQUIRE(!reopened.load());
BOOST_REQUIRE_EQUAL(reopened.size(), size);

memory = reopened.get();
BOOST_REQUIRE(memory);

const auto end = std::next(memory.begin(), expected.size());
BOOST_REQUIRE_EQUAL_COLLECTIONS(memory.begin(), end, expected.begin(), expected.end());

memory.reset();
BOOST_REQUIRE(!reopened.unload());
BOOST_REQUIRE(!reopened.close());
BOOST_REQUIRE(!reopened.get_fault());
}

BOOST_AUTO_TEST_CASE(mmap__unload__shared__unload_locked)
{
const std::string file = TEST_PATH;
Expand Down
2 changes: 1 addition & 1 deletion test/mocks/chunk_storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class chunk_storages

chunk_storages(const path& filename,
const database::storage_settings& ={}, bool=true,
bool=false) NOEXCEPT requires (is_one(columns))
bool=false, bool=false) NOEXCEPT requires (is_one(columns))
: alias_{}, paths_{ filename }, logical_{}
{
}
Expand Down
Loading