From 2873dbe15472faca5e14d83b3b9d03221dcb5e31 Mon Sep 17 00:00:00 2001 From: Mariusz Glebocki Date: Mon, 4 Jul 2022 16:23:31 +0200 Subject: [PATCH] Optimize file writing by using a memory buffer. (#3461) --- docs/CONTRIBUTORS | 1 + src/V3EmitCSyms.cpp | 3 ++- src/V3File.cpp | 5 ++++- src/V3File.h | 43 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index d598cebd5..efd6749cc 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -68,6 +68,7 @@ Lukasz Dalek Maarten De Braekeleer Maciej Sobkowski Marco Widmer +Mariusz Glebocki Markus Krause Marlon James Marshal Qiao diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 28d455fd9..c66f346b0 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -951,7 +951,8 @@ void EmitCSyms::emitSymImp() { } closeSplit(); - VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); + m_ofp = nullptr; + VL_DO_CLEAR(delete m_ofpBase, m_ofpBase = nullptr); } //###################################################################### diff --git a/src/V3File.cpp b/src/V3File.cpp index f6b9cf11d..305d19c08 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -920,13 +920,16 @@ void V3OutFormatter::printf(const char* fmt...) { // V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code. V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang) - : V3OutFormatter{filename, lang} { + : V3OutFormatter{filename, lang} + , m_bufferp{new std::array{}} { if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) { v3fatal("Cannot write " << filename); } } V3OutFile::~V3OutFile() { + writeBlock(); + if (m_fp) fclose(m_fp); m_fp = nullptr; } diff --git a/src/V3File.h b/src/V3File.h index dd337b165..6c45a0456 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -22,6 +22,7 @@ #include "V3Error.h" +#include #include #include #include @@ -183,18 +184,56 @@ public: // V3OutFile: A class for printing to a file, with automatic indentation of C++ code. class V3OutFile VL_NOT_FINAL : public V3OutFormatter { + // Size of m_bufferp. + // 128kB has been experimentally determined to be in the zone of buffer sizes that work best. + // It is also considered to be the smallest I/O buffer size in GNU coreutils (io_blksize) that + // allows to best minimize syscall overhead. + // The hard boundaries are CPU L2/L3 cache size on the top and filesystem block size + // on the bottom. + static constexpr std::size_t WRITE_BUFFER_SIZE_BYTES = 128 * 1024; + // MEMBERS + std::unique_ptr> m_bufferp; // Write buffer + std::size_t m_usedBytes = 0; // Number of bytes stored in m_bufferp FILE* m_fp = nullptr; public: V3OutFile(const string& filename, V3OutFormatter::Language lang); + V3OutFile(const V3OutFile&) = delete; + V3OutFile& operator=(const V3OutFile&) = delete; + V3OutFile(V3OutFile&&) = delete; + V3OutFile& operator=(V3OutFile&&) = delete; + virtual ~V3OutFile() override; void putsForceIncs(); private: + void writeBlock() { + if (VL_LIKELY(m_usedBytes > 0)) fwrite(m_bufferp->data(), m_usedBytes, 1, m_fp); + m_usedBytes = 0; + } + // CALLBACKS - virtual void putcOutput(char chr) override { fputc(chr, m_fp); } - virtual void putsOutput(const char* str) override { fputs(str, m_fp); } + virtual void putcOutput(char chr) override { + m_bufferp->at(m_usedBytes++) = chr; + if (VL_UNLIKELY(m_usedBytes >= WRITE_BUFFER_SIZE_BYTES)) writeBlock(); + } + virtual void putsOutput(const char* str) override { + std::size_t len = strlen(str); + std::size_t availableBytes = WRITE_BUFFER_SIZE_BYTES - m_usedBytes; + while (VL_UNLIKELY(len >= availableBytes)) { + memcpy(m_bufferp->data() + m_usedBytes, str, availableBytes); + m_usedBytes = WRITE_BUFFER_SIZE_BYTES; + writeBlock(); + str += availableBytes; + len -= availableBytes; + availableBytes = WRITE_BUFFER_SIZE_BYTES; + } + if (len > 0) { + memcpy(m_bufferp->data() + m_usedBytes, str, len); + m_usedBytes += len; + } + } }; class V3OutCFile VL_NOT_FINAL : public V3OutFile {