aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-11-27 14:32:19 +0100
committerGitHub <[email protected]>2023-11-27 14:32:19 +0100
commit4d95b578350ebfbbf6d54407c9403547b01cac4c (patch)
tree9f8df5d934a6a62fdcebeac94dffe52139d3ea6b /src/zenutil
parentgc stop command (#569) (diff)
downloadzen-4d95b578350ebfbbf6d54407c9403547b01cac4c.tar.xz
zen-4d95b578350ebfbbf6d54407c9403547b01cac4c.zip
optimized index snapshot reading/writing (#561)
the previous implementation of in-memory index snapshots serialise data to memory before writing to disk and vice versa when reading. This leads to some memory spikes which end up pushing useful data out of system cache and also cause stalls on I/O operations. this change moves more code to a streaming serialisation approach which scales better from a memory usage perspective and also performs much better
Diffstat (limited to 'src/zenutil')
-rw-r--r--src/zenutil/basicfile.cpp94
-rw-r--r--src/zenutil/include/zenutil/basicfile.h31
-rw-r--r--src/zenutil/include/zenutil/zenutil.h6
-rw-r--r--src/zenutil/zenutil.cpp19
4 files changed, 139 insertions, 11 deletions
diff --git a/src/zenutil/basicfile.cpp b/src/zenutil/basicfile.cpp
index 1dce71e60..7d0edaa5d 100644
--- a/src/zenutil/basicfile.cpp
+++ b/src/zenutil/basicfile.cpp
@@ -76,16 +76,15 @@ BasicFile::Open(const std::filesystem::path& FileName, Mode InMode, std::error_c
const DWORD dwShareMode = FILE_SHARE_READ | (EnumHasAllFlags(InMode, Mode::kPreventWrite) ? 0 : FILE_SHARE_WRITE) |
(EnumHasAllFlags(InMode, Mode::kPreventDelete) ? 0 : FILE_SHARE_DELETE);
- const DWORD dwFlagsAndAttributes =
- FILE_ATTRIBUTE_NORMAL | (EnumHasAllFlags(InMode, Mode::kDeleteOnClose) ? FILE_FLAG_DELETE_ON_CLOSE : 0);
- const HANDLE hTemplateFile = nullptr;
- const HANDLE FileHandle = CreateFile(FileName.c_str(),
- dwDesiredAccess,
- dwShareMode,
- /* lpSecurityAttributes */ nullptr,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- hTemplateFile);
+ const DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ const HANDLE hTemplateFile = nullptr;
+ const HANDLE FileHandle = CreateFile(FileName.c_str(),
+ dwDesiredAccess,
+ dwShareMode,
+ /* lpSecurityAttributes */ nullptr,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ hTemplateFile);
if (FileHandle == INVALID_HANDLE_VALUE)
{
@@ -588,6 +587,8 @@ LockFile::Update(CbObject Payload, std::error_code& Ec)
BasicFile::Write(Payload.GetBuffer(), 0, Ec);
}
+//////////////////////////////////////////////////////////////////////////
+
BasicFileBuffer::BasicFileBuffer(BasicFile& Base, uint64_t BufferSize)
: m_Base(Base)
, m_Buffer(nullptr)
@@ -662,6 +663,79 @@ BasicFileBuffer::MakeView(uint64_t Size, uint64_t FileOffset)
return MemoryView(m_Buffer + (FileOffset - m_BufferStart), Size);
}
+//////////////////////////////////////////////////////////////////////////
+
+BasicFileWriter::BasicFileWriter(BasicFile& Base, uint64_t BufferSize)
+: m_Base(Base)
+, m_Buffer(nullptr)
+, m_BufferSize(BufferSize)
+, m_BufferStart(0)
+, m_BufferEnd(0)
+{
+ m_Buffer = (uint8_t*)Memory::Alloc(m_BufferSize);
+}
+
+BasicFileWriter::~BasicFileWriter()
+{
+ Flush();
+ Memory::Free(m_Buffer);
+}
+
+void
+BasicFileWriter::Write(void* Data, uint64_t Size, uint64_t FileOffset)
+{
+ if (m_Buffer == nullptr || (Size >= m_BufferSize))
+ {
+ m_Base.Write(Data, Size, FileOffset);
+ return;
+ }
+
+ // Note that this only supports buffering of sequential writes!
+
+ if (FileOffset != m_BufferEnd)
+ {
+ Flush();
+ m_BufferStart = m_BufferEnd = FileOffset;
+ }
+
+ while (Size)
+ {
+ const uint64_t RemainingBufferCapacity = m_BufferStart + m_BufferSize - m_BufferEnd;
+ const uint64_t BlockWriteBytes = Min(RemainingBufferCapacity, Size);
+ const uint64_t BufferWriteOffset = FileOffset - m_BufferStart;
+
+ ZEN_ASSERT_SLOW(BufferWriteOffset < m_BufferSize);
+ ZEN_ASSERT_SLOW((BufferWriteOffset + BlockWriteBytes) <= m_BufferSize);
+
+ memcpy(m_Buffer + BufferWriteOffset, Data, BlockWriteBytes);
+
+ Size -= BlockWriteBytes;
+ m_BufferEnd += BlockWriteBytes;
+ FileOffset += BlockWriteBytes;
+
+ if ((m_BufferEnd - m_BufferStart) == m_BufferSize)
+ {
+ Flush();
+ }
+ }
+}
+
+void
+BasicFileWriter::Flush()
+{
+ const uint64_t BufferedBytes = m_BufferEnd - m_BufferStart;
+
+ if (BufferedBytes == 0)
+ return;
+
+ const uint64_t WriteOffset = m_BufferStart;
+ m_BufferStart = m_BufferEnd;
+
+ m_Base.Write(m_Buffer, BufferedBytes, WriteOffset);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
/*
___________ __
\__ ___/___ _______/ |_ ______
diff --git a/src/zenutil/include/zenutil/basicfile.h b/src/zenutil/include/zenutil/basicfile.h
index 7797258e8..f25d9f23c 100644
--- a/src/zenutil/include/zenutil/basicfile.h
+++ b/src/zenutil/include/zenutil/basicfile.h
@@ -44,7 +44,6 @@ public:
kModeMask = 0x0007,
kPreventDelete = 0x1000'0000, // Do not open with delete sharing mode (prevent other processes from deleting file while open)
kPreventWrite = 0x2000'0000, // Do not open with write sharing mode (prevent other processes from writing to file while open)
- kDeleteOnClose = 0x4000'0000, // File should be deleted when the last handle is closed
};
void Open(const std::filesystem::path& FileName, Mode Mode);
@@ -138,6 +137,13 @@ public:
void Read(void* Data, uint64_t Size, uint64_t FileOffset);
MemoryView MakeView(uint64_t Size, uint64_t FileOffset);
+ template<typename T>
+ const T* MakeView(uint64_t FileOffset)
+ {
+ MemoryView View = MakeView(sizeof(T), FileOffset);
+ return reinterpret_cast<const T*>(View.GetData());
+ }
+
private:
BasicFile& m_Base;
uint8_t* m_Buffer;
@@ -147,6 +153,29 @@ private:
uint64_t m_BufferEnd;
};
+/** Adds a layer of buffered writing to a BasicFile
+
+This class is not intended for concurrent access, it is not thread safe.
+
+*/
+
+class BasicFileWriter
+{
+public:
+ BasicFileWriter(BasicFile& Base, uint64_t BufferSize);
+ ~BasicFileWriter();
+
+ void Write(void* Data, uint64_t Size, uint64_t FileOffset);
+ void Flush();
+
+private:
+ BasicFile& m_Base;
+ uint8_t* m_Buffer;
+ const uint64_t m_BufferSize;
+ uint64_t m_BufferStart;
+ uint64_t m_BufferEnd;
+};
+
ZENCORE_API void basicfile_forcelink();
} // namespace zen
diff --git a/src/zenutil/include/zenutil/zenutil.h b/src/zenutil/include/zenutil/zenutil.h
index 14d21ea0d..662743de8 100644
--- a/src/zenutil/include/zenutil/zenutil.h
+++ b/src/zenutil/include/zenutil/zenutil.h
@@ -1,3 +1,9 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
+
+namespace zen {
+
+void zenutil_forcelinktests();
+
+}
diff --git a/src/zenutil/zenutil.cpp b/src/zenutil/zenutil.cpp
new file mode 100644
index 000000000..df075ea3f
--- /dev/null
+++ b/src/zenutil/zenutil.cpp
@@ -0,0 +1,19 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "zenutil/zenutil.h"
+
+#if ZEN_WITH_TESTS
+
+# include <zenutil/basicfile.h>
+
+namespace zen {
+
+void
+zenutil_forcelinktests()
+{
+ basicfile_forcelink();
+}
+
+} // namespace zen
+
+#endif