Skip to content

Commit 1d9628f

Browse files
authored
Merge pull request #667 from evoskuil/master
Implement random access header and sequential body file and map.
2 parents eb07c03 + 5558e64 commit 1d9628f

File tree

12 files changed

+165
-110
lines changed

12 files changed

+165
-110
lines changed

include/bitcoin/database/error.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ enum error_t : uint8_t
6868
mremap_failure,
6969
munmap_failure,
7070
madvise_failure,
71+
sysconf_failure,
7172
ftruncate_failure,
7273
fsync_failure,
7374

include/bitcoin/database/file/utilities.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ BCD_API bool copy_directory(const path& from, const path& to) NOEXCEPT;
7272
BCD_API code copy_directory_ex(const path& from, const path& to) NOEXCEPT;
7373

7474
/// File descriptor functions (for memory mapping).
75-
BCD_API int open(const path& filename) NOEXCEPT;
76-
BCD_API code open_ex(int& file_descriptor, const path& filename) NOEXCEPT;
75+
BCD_API int open(const path& filename, bool random=true) NOEXCEPT;
76+
BCD_API code open_ex(int& file_descriptor, const path& filename,
77+
bool random=true) NOEXCEPT;
7778
BCD_API bool close(int file_descriptor) NOEXCEPT;
7879
BCD_API code close_ex(int file_descriptor) NOEXCEPT;
7980
BCD_API bool size(size_t& out, int file_descriptor) NOEXCEPT;

include/bitcoin/database/impl/store.ipp

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
namespace libbitcoin {
3232
namespace database {
3333

34+
constexpr auto random = true;
35+
constexpr auto sequential = false;
36+
3437
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
3538

3639
// public
@@ -134,85 +137,85 @@ CLASS::store(const settings& config) NOEXCEPT
134137
// Archive.
135138
// ------------------------------------------------------------------------
136139

137-
header_head_(head(config.path / schema::dir::heads, schema::archive::header)),
138-
header_body_(body(config.path, schema::archive::header), config.header_size, config.header_rate),
140+
header_head_(head(config.path / schema::dir::heads, schema::archive::header), 1, 0, random),
141+
header_body_(body(config.path, schema::archive::header), config.header_size, config.header_rate, sequential),
139142
header(header_head_, header_body_, config.header_buckets),
140143

141-
input_head_(head(config.path / schema::dir::heads, schema::archive::input)),
142-
input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate),
144+
input_head_(head(config.path / schema::dir::heads, schema::archive::input), 1, 0, random),
145+
input_body_(body(config.path, schema::archive::input), config.input_size, config.input_rate, sequential),
143146
input(input_head_, input_body_),
144147

145-
output_head_(head(config.path / schema::dir::heads, schema::archive::output)),
146-
output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate),
148+
output_head_(head(config.path / schema::dir::heads, schema::archive::output), 1, 0, random),
149+
output_body_(body(config.path, schema::archive::output), config.output_size, config.output_rate, sequential),
147150
output(output_head_, output_body_),
148151

149-
point_head_(head(config.path / schema::dir::heads, schema::archive::point)),
150-
point_body_(body(config.path, schema::archive::point), config.point_size, config.point_rate),
152+
point_head_(head(config.path / schema::dir::heads, schema::archive::point), 1, 0, random),
153+
point_body_(body(config.path, schema::archive::point), config.point_size, config.point_rate, sequential),
151154
point(point_head_, point_body_, config.point_buckets),
152155

153-
ins_head_(head(config.path / schema::dir::heads, schema::archive::ins)),
154-
ins_body_(body(config.path, schema::archive::ins), config.ins_size, config.ins_rate),
156+
ins_head_(head(config.path / schema::dir::heads, schema::archive::ins), 1, 0, random),
157+
ins_body_(body(config.path, schema::archive::ins), config.ins_size, config.ins_rate, sequential),
155158
ins(ins_head_, ins_body_),
156159

157-
outs_head_(head(config.path / schema::dir::heads, schema::archive::outs)),
158-
outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate),
160+
outs_head_(head(config.path / schema::dir::heads, schema::archive::outs), 1, 0, random),
161+
outs_body_(body(config.path, schema::archive::outs), config.outs_size, config.outs_rate, sequential),
159162
outs(outs_head_, outs_body_),
160163

161-
tx_head_(head(config.path / schema::dir::heads, schema::archive::tx)),
162-
tx_body_(body(config.path, schema::archive::tx), config.tx_size, config.tx_rate),
164+
tx_head_(head(config.path / schema::dir::heads, schema::archive::tx), 1, 0, random),
165+
tx_body_(body(config.path, schema::archive::tx), config.tx_size, config.tx_rate, sequential),
163166
tx(tx_head_, tx_body_, config.tx_buckets),
164167

165-
txs_head_(head(config.path / schema::dir::heads, schema::archive::txs)),
166-
txs_body_(body(config.path, schema::archive::txs), config.txs_size, config.txs_rate),
168+
txs_head_(head(config.path / schema::dir::heads, schema::archive::txs), 1, 0, random),
169+
txs_body_(body(config.path, schema::archive::txs), config.txs_size, config.txs_rate, sequential),
167170
txs(txs_head_, txs_body_, config.txs_buckets),
168171

169172
// Indexes.
170173
// ------------------------------------------------------------------------
171174

172-
candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate)),
173-
candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate),
175+
candidate_head_(head(config.path / schema::dir::heads, schema::indexes::candidate), 1, 0, random),
176+
candidate_body_(body(config.path, schema::indexes::candidate), config.candidate_size, config.candidate_rate, sequential),
174177
candidate(candidate_head_, candidate_body_),
175178

176-
confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed)),
177-
confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate),
179+
confirmed_head_(head(config.path / schema::dir::heads, schema::indexes::confirmed), 1, 0, random),
180+
confirmed_body_(body(config.path, schema::indexes::confirmed), config.confirmed_size, config.confirmed_rate, sequential),
178181
confirmed(confirmed_head_, confirmed_body_),
179182

180-
strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx)),
181-
strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx_size, config.strong_tx_rate),
183+
strong_tx_head_(head(config.path / schema::dir::heads, schema::indexes::strong_tx), 1, 0, random),
184+
strong_tx_body_(body(config.path, schema::indexes::strong_tx), config.strong_tx_size, config.strong_tx_rate, sequential),
182185
strong_tx(strong_tx_head_, strong_tx_body_, config.strong_tx_buckets),
183186

184187
// Caches.
185188
// ------------------------------------------------------------------------
186189

187-
duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate)),
188-
duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate_size, config.duplicate_rate),
190+
duplicate_head_(head(config.path / schema::dir::heads, schema::caches::duplicate), 1, 0, random),
191+
duplicate_body_(body(config.path, schema::caches::duplicate), config.duplicate_size, config.duplicate_rate, sequential),
189192
duplicate(duplicate_head_, duplicate_body_, config.duplicate_buckets),
190193

191-
prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout)),
192-
prevout_body_(body(config.path, schema::caches::prevout), config.prevout_size, config.prevout_rate),
194+
prevout_head_(head(config.path / schema::dir::heads, schema::caches::prevout), 1, 0, random),
195+
prevout_body_(body(config.path, schema::caches::prevout), config.prevout_size, config.prevout_rate, sequential),
193196
prevout(prevout_head_, prevout_body_, config.prevout_buckets),
194197

195-
validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk)),
196-
validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk_size, config.validated_bk_rate),
198+
validated_bk_head_(head(config.path / schema::dir::heads, schema::caches::validated_bk), 1, 0, random),
199+
validated_bk_body_(body(config.path, schema::caches::validated_bk), config.validated_bk_size, config.validated_bk_rate, sequential),
197200
validated_bk(validated_bk_head_, validated_bk_body_, config.validated_bk_buckets),
198201

199-
validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx)),
200-
validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx_size, config.validated_tx_rate),
202+
validated_tx_head_(head(config.path / schema::dir::heads, schema::caches::validated_tx), 1, 0, random),
203+
validated_tx_body_(body(config.path, schema::caches::validated_tx), config.validated_tx_size, config.validated_tx_rate, sequential),
201204
validated_tx(validated_tx_head_, validated_tx_body_, config.validated_tx_buckets),
202205

203206
// Optionals.
204207
// ------------------------------------------------------------------------
205208

206-
address_head_(head(config.path / schema::dir::heads, schema::optionals::address)),
207-
address_body_(body(config.path, schema::optionals::address), config.address_size, config.address_rate),
209+
address_head_(head(config.path / schema::dir::heads, schema::optionals::address), 1, 0, random),
210+
address_body_(body(config.path, schema::optionals::address), config.address_size, config.address_rate, sequential),
208211
address(address_head_, address_body_, config.address_buckets),
209212

210-
filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk)),
211-
filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk_size, config.filter_bk_rate),
213+
filter_bk_head_(head(config.path / schema::dir::heads, schema::optionals::filter_bk), 1, 0, random),
214+
filter_bk_body_(body(config.path, schema::optionals::filter_bk), config.filter_bk_size, config.filter_bk_rate, sequential),
212215
filter_bk(filter_bk_head_, filter_bk_body_, config.filter_bk_buckets),
213216

214-
filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx)),
215-
filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx_size, config.filter_tx_rate),
217+
filter_tx_head_(head(config.path / schema::dir::heads, schema::optionals::filter_tx), 1, 0, random),
218+
filter_tx_body_(body(config.path, schema::optionals::filter_tx), config.filter_tx_size, config.filter_tx_rate, sequential),
216219
filter_tx(filter_tx_head_, filter_tx_body_, config.filter_tx_buckets),
217220

218221
// Locks.

include/bitcoin/database/memory/map.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class BCD_API map
4343
DELETE_COPY_MOVE(map);
4444

4545
map(const std::filesystem::path& filename, size_t minimum=1,
46-
size_t expansion=0) NOEXCEPT;
46+
size_t expansion=0, bool random=true) NOEXCEPT;
4747

4848
/// Destruct for debug assertion only.
4949
virtual ~map() NOEXCEPT;
@@ -140,6 +140,7 @@ class BCD_API map
140140
const std::filesystem::path filename_;
141141
const size_t minimum_;
142142
const size_t expansion_;
143+
const bool random_;
143144

144145
// Protected by remap_mutex.
145146
// requires remap_mutex_ exclusive lock for write.

src/error.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error)
6161
{ mremap_failure, "mremap failure" },
6262
{ munmap_failure, "munmap failure" },
6363
{ madvise_failure, "madvise failure" },
64+
{ sysconf_failure, "sysconf failure" },
6465
{ ftruncate_failure, "ftruncate failure" },
6566
{ fsync_failure, "fsync failure" },
6667

src/file/utilities.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -242,29 +242,62 @@ code copy_directory_ex(const path& from, const path& to) NOEXCEPT
242242

243243
// File descriptor functions required for memory mapping.
244244

245-
int open(const path& filename) NOEXCEPT
245+
#if defined(HAVE_MSC) || !defined(HAVE_APPLE)
246+
#define MSC_OR_NOAPPLE(parameter) parameter
247+
#else
248+
#define MSC_OR_NOAPPLE(parameter)
249+
#endif
250+
251+
int open(const path& filename, bool MSC_OR_NOAPPLE(random)) NOEXCEPT
246252
{
247253
const auto path = system::to_extended_path(filename);
248254
int file_descriptor{};
249255

250-
// _wsopen_s and open set errno on failure.
251-
// _wsopen_s and wstring do not throw (but are unannotated).
252256
#if defined(HAVE_MSC)
253-
// sets file_descriptor = -1 on error.
254-
_wsopen_s(&file_descriptor, path.c_str(),
255-
O_RDWR | _O_BINARY | _O_RANDOM, _SH_DENYWR, _S_IREAD | _S_IWRITE);
257+
// _wsopen_s and wstring do not throw (but are unannotated).
258+
// sets file_descriptor = -1 and errno on error.
259+
const auto access = (random ? _O_RANDOM : _O_SEQUENTIAL);
260+
::_wsopen_s(&file_descriptor, path.c_str(),
261+
O_RDWR | _O_BINARY | access, _SH_DENYWR, _S_IREAD | _S_IWRITE);
256262
#else
257-
// returns -1 on failure.
258-
file_descriptor = ::open(path.c_str(),
259-
O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
260-
#endif
263+
// open sets errno on failure.
264+
file_descriptor = ::open(path.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
265+
266+
#if !defined(HAVE_APPLE)
267+
if (file_descriptor != -1)
268+
{
269+
// _O_RANDOM equivalent, posix_fadvise returns error on failure.
270+
const auto advice = random ? POSIX_FADV_RANDOM : POSIX_FADV_SEQUENTIAL;
271+
const auto result = ::posix_fadvise(file_descriptor, 0, 0, advice);
272+
if (!is_zero(result))
273+
{
274+
close(file_descriptor);
275+
file_descriptor = -1;
276+
errno = result;
277+
}
278+
else
279+
{
280+
// _SH_DENYWR equivalent.
281+
const struct flock lock{ F_WRLCK, SEEK_SET, 0, 0 };
282+
if (::fcntl(file_descriptor, F_SETLK, &lock) == -1)
283+
{
284+
const auto last = errno;
285+
close(file_descriptor);
286+
file_descriptor = -1;
287+
errno = last;
288+
}
289+
}
290+
}
291+
#endif // !HAVE_APPLE
292+
#endif // HAVE_MSC
293+
261294
return file_descriptor;
262295
}
263296

264-
code open_ex(int& file_descriptor, const path& filename) NOEXCEPT
297+
code open_ex(int& file_descriptor, const path& filename, bool random) NOEXCEPT
265298
{
266299
system::error::clear_errno();
267-
file_descriptor = open(filename);
300+
file_descriptor = open(filename, random);
268301
return system::error::get_errno();
269302
}
270303

src/memory/map.cpp

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <sys/mman.h>
2525
#include <sys/stat.h>
2626
#include <sys/types.h>
27+
#include <unistd.h>
2728
#endif
2829
#include <algorithm>
2930
#include <chrono>
@@ -43,8 +44,12 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
4344

4445
using namespace system;
4546

46-
map::map(const path& filename, size_t minimum, size_t expansion) NOEXCEPT
47-
: filename_(filename), minimum_(minimum), expansion_(expansion)
47+
map::map(const path& filename, size_t minimum, size_t expansion,
48+
bool random) NOEXCEPT
49+
: filename_(filename),
50+
minimum_(minimum),
51+
expansion_(expansion),
52+
random_(random)
4853
{
4954
}
5055

@@ -69,7 +74,8 @@ code map::open() NOEXCEPT
6974
if (opened_ != file::invalid)
7075
return error::open_open;
7176

72-
if (const auto ec = file::open_ex(opened_, filename_))
77+
// Windows doesn't use madvise, instead infers map access from file open.
78+
if (const auto ec = file::open_ex(opened_, filename_, random_))
7379
return ec;
7480

7581
return file::size_ex(logical_, opened_);
@@ -583,43 +589,41 @@ bool map::finalize_(size_t size) NOEXCEPT
583589
return false;
584590
}
585591

586-
// TODO: also, madvise only the size increase.
587-
// TODO: madvise with large length value fails on linux (and zero is noop).
588-
// TODO: Random may not be best since writes are sequential (for bodies).
589-
// TODO: Heads are truly read/write random so could benefit.
590-
// TODO: this fails with large and/or unaligned size. Align size to page,
591-
// TODO: rounded up. Iterate over calls at 1GB (1_u64 << 30), sample:
592-
////// Get page size (usually 4KB)
593-
////const size_t page_size = sysconf(_SC_PAGESIZE);
594-
////if (page_size == static_cast<size_t>(-1))
595-
////{
596-
//// set_first_code(error::sysconf_failure);
597-
//// unmap_();
598-
//// return false;
599-
////}
600-
////
601-
////// Align size up to page boundary
602-
////const size_t aligned_size = (size + page_size - 1) & ~(page_size - 1);
603-
////
604-
////// Use 1GB chunks to avoid large-length issues
605-
////constexpr size_t chunk_size = 1ULL << 30; // 1GB
606-
////uint8_t* ptr = memory_map_;
607-
////for (size_t offset = 0; offset < aligned_size; offset += chunk_size)
608-
////{
609-
//// size_t len = std::min(chunk_size, aligned_size - offset);
610-
//// if (::madvise(ptr + offset, len, MADV_SEQUENTIAL) == fail)
611-
//// {
612-
//// set_first_code(error::madvise_failure);
613-
//// unmap_();
614-
//// return false;
615-
//// }
616-
////}
617-
////if (::madvise(memory_map_, size, MADV_RANDOM) == fail)
618-
////{
619-
//// set_first_code(error::madvise_failure);
620-
//// unmap_();
621-
//// return false;
622-
////}
592+
#if !defined(HAVE_MSC)
593+
// Get page size (usually 4KB).
594+
const int page_size = ::sysconf(_SC_PAGESIZE);
595+
const auto page = possible_narrow_sign_cast<size_t>(page_size);
596+
597+
// If not one bit then page size is not a power of two as required.
598+
if (page_size == fail || !is_one(ones_count(page)))
599+
{
600+
set_first_code(error::sysconf_failure);
601+
unmap_();
602+
return false;
603+
}
604+
605+
// Align size up to page boundary.
606+
const auto max = sub1(page);
607+
const auto align = bit_and(ceilinged_add(size, max), bit_not(max));
608+
609+
// Use 1GB chunks to avoid large-length issues.
610+
constexpr auto chunk = power2(30u);
611+
const auto advice = random_ ? MADV_RANDOM : MADV_SEQUENTIAL;
612+
613+
for (auto offset = zero; offset < align; offset += chunk)
614+
{
615+
BC_PUSH_WARNING(NO_POINTER_ARITHMETIC)
616+
const auto start = memory_map_ + offset;
617+
BC_POP_WARNING()
618+
619+
if (::madvise(start, std::min(chunk, align - offset), advice) == fail)
620+
{
621+
set_first_code(error::madvise_failure);
622+
unmap_();
623+
return false;
624+
}
625+
}
626+
#endif
623627

624628
loaded_ = true;
625629
capacity_ = size;

0 commit comments

Comments
 (0)