From c71ed22e7a03a012fbd1e4ec3c9181185ee3f292 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 28 May 2018 13:51:18 +0100 Subject: [PATCH] Save/memory dumper performance improvements --- src/saveload/saveload.cpp | 133 +++++++++++++++++++++++++++++++++----- src/saveload/saveload.h | 20 +----- 2 files changed, 121 insertions(+), 32 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index cfe81e937b..89b343d579 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -444,13 +444,48 @@ struct ReadBuffer { /** Container for dumping the savegame (quickly) to memory. */ struct MemoryDumper { - AutoFreeSmallVector blocks; ///< Buffer with blocks of allocated memory. + struct BufferInfo { + byte *data; + size_t size = 0; + + BufferInfo(byte *d) : data(d) {} + ~BufferInfo() { free(this->data); } + + BufferInfo(const BufferInfo &) = delete; + BufferInfo(BufferInfo &&other) : data(other.data), size(other.size) { other.data = nullptr; }; + }; + + std::vector blocks; ///< Buffer with blocks of allocated memory. byte *buf; ///< Buffer we're going to write to. byte *bufe; ///< End of the buffer we write to. + size_t completed_block_bytes; ///< Total byte count of completed blocks /** Initialise our variables. */ - MemoryDumper() : buf(NULL), bufe(NULL) + MemoryDumper() : buf(NULL), bufe(NULL), completed_block_bytes(0) + { + } + + void FinaliseBlock() + { + if (!this->blocks.empty()) { + size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf); + this->blocks.back().size = s; + this->completed_block_bytes += s; + } + this->buf = this->bufe = nullptr; + } + + void AllocateBuffer() + { + this->FinaliseBlock(); + this->buf = CallocT(MEMORY_CHUNK_SIZE); + this->blocks.emplace_back(this->buf); + this->bufe = this->buf + MEMORY_CHUNK_SIZE; + } + + void CheckBytes(size_t bytes) { + if (unlikely(this->buf + bytes > this->bufe)) this->AllocateBuffer(); } /** @@ -460,29 +495,79 @@ struct MemoryDumper { inline void WriteByte(byte b) { /* Are we at the end of this chunk? */ - if (this->buf == this->bufe) { - this->buf = CallocT(MEMORY_CHUNK_SIZE); - *this->blocks.Append() = this->buf; - this->bufe = this->buf + MEMORY_CHUNK_SIZE; + if (unlikely(this->buf == this->bufe)) { + this->AllocateBuffer(); } *this->buf++ = b; } + inline void CopyBytes(byte *ptr, size_t length) + { + while (length) { + if (unlikely(this->buf == this->bufe)) { + this->AllocateBuffer(); + } + size_t to_copy = min(this->bufe - this->buf, length); + memcpy(this->buf, ptr, to_copy); + this->buf += to_copy; + ptr += to_copy; + length -= to_copy; + } + } + + inline void RawWriteUint16(uint16 v) + { +#if OTTD_ALIGNMENT == 0 + *((uint16 *) this->buf) = TO_BE16(v); +#else + this->buf[0] = GB(v, 8, 8)); + this->buf[1] = GB(v, 0, 8)); +#endif + this->buf += 2; + } + + inline void RawWriteUint32(uint32 v) + { +#if OTTD_ALIGNMENT == 0 + *((uint32 *) this->buf) = TO_BE32(v); +#else + this->buf[0] = GB(v, 24, 8)); + this->buf[1] = GB(v, 16, 8)); + this->buf[2] = GB(v, 8, 8)); + this->buf[3] = GB(v, 0, 8)); +#endif + this->buf += 4; + } + + inline void RawWriteUint64(uint64 v) + { +#if OTTD_ALIGNMENT == 0 + *((uint64 *) this->buf) = TO_BE64(v); +#else + this->buf[0] = GB(v, 56, 8)); + this->buf[1] = GB(v, 48, 8)); + this->buf[2] = GB(v, 40, 8)); + this->buf[3] = GB(v, 32, 8)); + this->buf[4] = GB(v, 24, 8)); + this->buf[5] = GB(v, 16, 8)); + this->buf[6] = GB(v, 8, 8)); + this->buf[7] = GB(v, 0, 8)); +#endif + this->buf += 8; + } + /** * Flush this dumper into a writer. * @param writer The filter we want to use. */ void Flush(SaveFilter *writer) { - uint i = 0; - size_t t = this->GetSize(); - - while (t > 0) { - size_t to_write = min(MEMORY_CHUNK_SIZE, t); + this->FinaliseBlock(); - writer->Write(this->blocks[i++], to_write); - t -= to_write; + uint block_count = this->blocks.size(); + for (uint i = 0; i < block_count; i++) { + writer->Write(this->blocks[i].data, this->blocks[i].size); } writer->Finish(); @@ -494,7 +579,7 @@ struct MemoryDumper { */ size_t GetSize() const { - return this->blocks.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf); + return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0); } }; @@ -782,6 +867,24 @@ void SlWriteByte(byte b) _sl.dumper->WriteByte(b); } +void SlWriteUint16(uint16 v) +{ + _sl.dumper->CheckBytes(2); + _sl.dumper->RawWriteUint16(v); +} + +void SlWriteUint32(uint32 v) +{ + _sl.dumper->CheckBytes(4); + _sl.dumper->RawWriteUint32(v); +} + +void SlWriteUint64(uint64 v) +{ + _sl.dumper->CheckBytes(8); + _sl.dumper->RawWriteUint64(v); +} + /** * Returns number of bytes read so far * May only be called during a load/load check action @@ -1074,7 +1177,7 @@ static void SlCopyBytes(void *ptr, size_t length) _sl.reader->CopyBytes(p, length); break; case SLA_SAVE: - for (; length != 0; length--) SlWriteByte(*p++); + _sl.dumper->CopyBytes(p, length); break; default: NOT_REACHED(); } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index e4a26dcd71..5bc710d7a8 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -683,23 +683,9 @@ int SlReadUint16(); uint32 SlReadUint32(); uint64 SlReadUint64(); -static inline void SlWriteUint16(uint16 v) -{ - SlWriteByte(GB(v, 8, 8)); - SlWriteByte(GB(v, 0, 8)); -} - -static inline void SlWriteUint32(uint32 v) -{ - SlWriteUint16(GB(v, 16, 16)); - SlWriteUint16(GB(v, 0, 16)); -} - -static inline void SlWriteUint64(uint64 x) -{ - SlWriteUint32((uint32)(x >> 32)); - SlWriteUint32((uint32)x); -} +void SlWriteUint16(uint16 v); +void SlWriteUint32(uint32 v); +void SlWriteUint64(uint64 v); void SlSkipBytes(size_t length);