Skip to content

Commit 9f1aa88

Browse files
committed
Merge bitcoin/bitcoin#30884: streams: cache file position within AutoFile
a240e15 streams: remove AutoFile::Get() entirely (Pieter Wuille) e624a9b streams: cache file position within AutoFile (Pieter Wuille) Pull request description: Fixes #30833. Instead of relying on frequent `ftell` calls (which appear to cause a significant slowdown on some systems) in XOR-enabled `AutoFile`s, cache the file position within `AutoFile` itself. ACKs for top commit: achow101: ACK a240e15 davidgumberg: untested reACK bitcoin/bitcoin@a240e15 theStack: Code-review ACK a240e15 Tree-SHA512: fd3681edc018afaf955dc7a41a0c953ca80d46c1129e3c5b306c87c95aae93b2fe7b900794eb8b6f10491f9211645e7939918a28838295e6873eb226fca7006f
2 parents 06329eb + a240e15 commit 9f1aa88

File tree

12 files changed

+75
-49
lines changed

12 files changed

+75
-49
lines changed

src/addrdb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
7373
remove(pathTmp);
7474
return false;
7575
}
76-
if (!FileCommit(fileout.Get())) {
76+
if (!fileout.Commit()) {
7777
fileout.fclose();
7878
remove(pathTmp);
7979
LogError("%s: Failed to flush file %s\n", __func__, fs::PathToString(pathTmp));

src/bench/streams_findbyte.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ static void FindByte(benchmark::Bench& bench)
1919
uint8_t data[file_size] = {0};
2020
data[file_size-1] = 1;
2121
file << data;
22-
std::rewind(file.Get());
22+
file.seek(0, SEEK_SET);
2323
BufferedFile bf{file, /*nBufSize=*/file_size + 1, /*nRewindIn=*/file_size};
2424

2525
bench.run([&] {

src/index/blockfilterindex.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ bool BlockFilterIndex::CustomCommit(CDBBatch& batch)
151151
LogError("%s: Failed to open filter file %d\n", __func__, pos.nFile);
152152
return false;
153153
}
154-
if (!FileCommit(file.Get())) {
154+
if (!file.Commit()) {
155155
LogError("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
156156
return false;
157157
}
@@ -201,11 +201,11 @@ size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter&
201201
LogPrintf("%s: Failed to open filter file %d\n", __func__, pos.nFile);
202202
return 0;
203203
}
204-
if (!TruncateFile(last_file.Get(), pos.nPos)) {
204+
if (!last_file.Truncate(pos.nPos)) {
205205
LogPrintf("%s: Failed to truncate filter file %d\n", __func__, pos.nFile);
206206
return 0;
207207
}
208-
if (!FileCommit(last_file.Get())) {
208+
if (!last_file.Commit()) {
209209
LogPrintf("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
210210
return 0;
211211
}

src/index/txindex.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,7 @@ bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRe
8787
CBlockHeader header;
8888
try {
8989
file >> header;
90-
if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) {
91-
LogError("%s: fseek(...) failed\n", __func__);
92-
return false;
93-
}
90+
file.seek(postx.nTxOffset, SEEK_CUR);
9491
file >> TX_WITH_WITNESS(tx);
9592
} catch (const std::exception& e) {
9693
LogError("%s: Deserialize or I/O error - %s\n", __func__, e.what());

src/node/blockstorage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ bool BlockManager::UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos
683683
fileout << GetParams().MessageStart() << nSize;
684684

685685
// Write undo data
686-
long fileOutPos = ftell(fileout.Get());
686+
long fileOutPos = fileout.tell();
687687
if (fileOutPos < 0) {
688688
LogError("%s: ftell failed\n", __func__);
689689
return false;
@@ -981,7 +981,7 @@ bool BlockManager::WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const
981981
fileout << GetParams().MessageStart() << nSize;
982982

983983
// Write block
984-
long fileOutPos = ftell(fileout.Get());
984+
long fileOutPos = fileout.tell();
985985
if (fileOutPos < 0) {
986986
LogError("%s: ftell failed\n", __func__);
987987
return false;

src/node/mempool_persist.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mock
199199
LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
200200
file << unbroadcast_txids;
201201

202-
if (!skip_file_commit && !FileCommit(file.Get()))
203-
throw std::runtime_error("FileCommit failed");
202+
if (!skip_file_commit && !file.Commit())
203+
throw std::runtime_error("Commit failed");
204204
file.fclose();
205205
if (!RenameOver(dump_path + ".new", dump_path)) {
206206
throw std::runtime_error("Rename failed");

src/node/utxo_snapshot.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,11 @@ std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
7373
}
7474
afile >> base_blockhash;
7575

76-
if (std::fgetc(afile.Get()) != EOF) {
76+
int64_t position = afile.tell();
77+
afile.seek(0, SEEK_END);
78+
if (position != afile.tell()) {
7779
LogPrintf("[snapshot] warning: unexpected trailing data in %s\n", read_from_str);
78-
} else if (std::ferror(afile.Get())) {
80+
} else if (afile.IsError()) {
7981
LogPrintf("[snapshot] warning: i/o error reading %s\n", read_from_str);
8082
}
8183
return base_blockhash;

src/streams.cpp

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,29 @@
44

55
#include <span.h>
66
#include <streams.h>
7+
#include <util/fs_helpers.h>
78

89
#include <array>
910

11+
AutoFile::AutoFile(std::FILE* file, std::vector<std::byte> data_xor)
12+
: m_file{file}, m_xor{std::move(data_xor)}
13+
{
14+
if (!IsNull()) {
15+
auto pos{std::ftell(m_file)};
16+
if (pos >= 0) m_position = pos;
17+
}
18+
}
19+
1020
std::size_t AutoFile::detail_fread(Span<std::byte> dst)
1121
{
1222
if (!m_file) throw std::ios_base::failure("AutoFile::read: file handle is nullptr");
13-
if (m_xor.empty()) {
14-
return std::fread(dst.data(), 1, dst.size(), m_file);
15-
} else {
16-
const auto init_pos{std::ftell(m_file)};
17-
if (init_pos < 0) throw std::ios_base::failure("AutoFile::read: ftell failed");
18-
std::size_t ret{std::fread(dst.data(), 1, dst.size(), m_file)};
19-
util::Xor(dst.subspan(0, ret), m_xor, init_pos);
20-
return ret;
23+
size_t ret = std::fread(dst.data(), 1, dst.size(), m_file);
24+
if (!m_xor.empty()) {
25+
if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::read: position unknown");
26+
util::Xor(dst.subspan(0, ret), m_xor, *m_position);
2127
}
28+
if (m_position.has_value()) *m_position += ret;
29+
return ret;
2230
}
2331

2432
void AutoFile::seek(int64_t offset, int origin)
@@ -29,18 +37,23 @@ void AutoFile::seek(int64_t offset, int origin)
2937
if (std::fseek(m_file, offset, origin) != 0) {
3038
throw std::ios_base::failure(feof() ? "AutoFile::seek: end of file" : "AutoFile::seek: fseek failed");
3139
}
40+
if (origin == SEEK_SET) {
41+
m_position = offset;
42+
} else if (origin == SEEK_CUR && m_position.has_value()) {
43+
*m_position += offset;
44+
} else {
45+
int64_t r{std::ftell(m_file)};
46+
if (r < 0) {
47+
throw std::ios_base::failure("AutoFile::seek: ftell failed");
48+
}
49+
m_position = r;
50+
}
3251
}
3352

3453
int64_t AutoFile::tell()
3554
{
36-
if (IsNull()) {
37-
throw std::ios_base::failure("AutoFile::tell: file handle is nullptr");
38-
}
39-
int64_t r{std::ftell(m_file)};
40-
if (r < 0) {
41-
throw std::ios_base::failure("AutoFile::tell: ftell failed");
42-
}
43-
return r;
55+
if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::tell: position unknown");
56+
return *m_position;
4457
}
4558

4659
void AutoFile::read(Span<std::byte> dst)
@@ -60,6 +73,7 @@ void AutoFile::ignore(size_t nSize)
6073
throw std::ios_base::failure(feof() ? "AutoFile::ignore: end of file" : "AutoFile::ignore: fread failed");
6174
}
6275
nSize -= nNow;
76+
if (m_position.has_value()) *m_position += nNow;
6377
}
6478
}
6579

@@ -70,19 +84,34 @@ void AutoFile::write(Span<const std::byte> src)
7084
if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) {
7185
throw std::ios_base::failure("AutoFile::write: write failed");
7286
}
87+
if (m_position.has_value()) *m_position += src.size();
7388
} else {
74-
auto current_pos{std::ftell(m_file)};
75-
if (current_pos < 0) throw std::ios_base::failure("AutoFile::write: ftell failed");
89+
if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::write: position unknown");
7690
std::array<std::byte, 4096> buf;
7791
while (src.size() > 0) {
7892
auto buf_now{Span{buf}.first(std::min<size_t>(src.size(), buf.size()))};
7993
std::copy(src.begin(), src.begin() + buf_now.size(), buf_now.begin());
80-
util::Xor(buf_now, m_xor, current_pos);
94+
util::Xor(buf_now, m_xor, *m_position);
8195
if (std::fwrite(buf_now.data(), 1, buf_now.size(), m_file) != buf_now.size()) {
8296
throw std::ios_base::failure{"XorFile::write: failed"};
8397
}
8498
src = src.subspan(buf_now.size());
85-
current_pos += buf_now.size();
99+
*m_position += buf_now.size();
86100
}
87101
}
88102
}
103+
104+
bool AutoFile::Commit()
105+
{
106+
return ::FileCommit(m_file);
107+
}
108+
109+
bool AutoFile::IsError()
110+
{
111+
return ferror(m_file);
112+
}
113+
114+
bool AutoFile::Truncate(unsigned size)
115+
{
116+
return ::TruncateFile(m_file, size);
117+
}

src/streams.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,10 @@ class AutoFile
390390
protected:
391391
std::FILE* m_file;
392392
std::vector<std::byte> m_xor;
393+
std::optional<int64_t> m_position;
393394

394395
public:
395-
explicit AutoFile(std::FILE* file, std::vector<std::byte> data_xor={}) : m_file{file}, m_xor{std::move(data_xor)} {}
396+
explicit AutoFile(std::FILE* file, std::vector<std::byte> data_xor={});
396397

397398
~AutoFile() { fclose(); }
398399

@@ -419,12 +420,6 @@ class AutoFile
419420
return ret;
420421
}
421422

422-
/** Get wrapped FILE* without transfer of ownership.
423-
* @note Ownership of the FILE* will remain with this class. Use this only if the scope of the
424-
* AutoFile outlives use of the passed pointer.
425-
*/
426-
std::FILE* Get() const { return m_file; }
427-
428423
/** Return true if the wrapped FILE* is nullptr, false otherwise.
429424
*/
430425
bool IsNull() const { return m_file == nullptr; }
@@ -458,6 +453,10 @@ class AutoFile
458453
::Unserialize(*this, obj);
459454
return *this;
460455
}
456+
457+
bool Commit();
458+
bool IsError();
459+
bool Truncate(unsigned size);
461460
};
462461

463462
/** Wrapper around an AutoFile& that implements a ring buffer to

src/test/fuzz/autofile.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ FUZZ_TARGET(autofile)
5656
WriteToStream(fuzzed_data_provider, auto_file);
5757
});
5858
}
59-
(void)auto_file.Get();
6059
(void)auto_file.IsNull();
6160
if (fuzzed_data_provider.ConsumeBool()) {
6261
FILE* f = auto_file.release();

0 commit comments

Comments
 (0)