I also ran a small unit test through perf where there were many writes and found that std::ftell dominated with std::fwrite close behind
For me fwrite dominated over ftell, but the most time was spent on XOR:
ROUTINE ====================== AutoFile::write in src/streams.cpp
49 100 Total samples (flat / cumulative)
. . 40: nSize -= nNow;
. . 41: }
. . 42: }
. . 43:
. . 44: void AutoFile::write(Span<const std::byte> src)
---
. . 45: {
. . 46: if (!m_file) throw std::ios_base::failure("AutoFile::write: file handle is nullptr");
. . 47: if (m_xor.empty()) {
. . 48: if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) {
. . 49: throw std::ios_base::failure("AutoFile::write: write failed");
. . 50: }
. . 51: } else {
. . 52: std::array<std::byte, 4096> buf;
. . 53: while (src.size() > 0) {
. . 54: auto buf_now{Span{buf}.first(std::min<size_t>(src.size(), buf.size()))};
. 2 55: std::copy(src.begin(), src.begin() + buf_now.size(), buf_now.begin());
. 16 56: const auto current_pos{std::ftell(m_file)};
49 49 57: util::Xor(buf_now, m_xor, current_pos);
. 33 58: if (current_pos < 0 || std::fwrite(buf_now.data(), 1, buf_now.size(), m_file) != buf_now.size()) {
. . 59: throw std::ios_base::failure{"XorFile::write: failed"};
. . 60: }
. . 61: src = src.subspan(buf_now.size());
. . 62: }
. . 63: }
. . 64: }
---