From 2df0ca34aa3000dadf76633ca700abf0bf50756d Mon Sep 17 00:00:00 2001 From: Hans-Andreas Engel Date: Fri, 8 Apr 2016 10:51:40 +0200 Subject: reduce memory allocations to zero --- src/config.h.cmake.in | 3 +++ src/glog/logging.h.in | 7 +++++++ src/logging.cc | 31 +++++++++++++++++++++++++++++++ src/logging_unittest.cc | 4 +++- src/logging_unittest.err | 2 ++ 5 files changed, 46 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/config.h.cmake.in b/src/config.h.cmake.in index 6635df1..a946451 100644 --- a/src/config.h.cmake.in +++ b/src/config.h.cmake.in @@ -174,6 +174,9 @@ /* location of source code */ #cmakedefine TEST_SRC_DIR ${TEST_SRC_DIR} +/* Define to necessary thread-local storage attribute. */ +#cmakedefine GLOG_THREAD_LOCAL_STORAGE ${GLOG_THREAD_LOCAL_STORAGE} + /* Version number of package */ #cmakedefine VERSION diff --git a/src/glog/logging.h.in b/src/glog/logging.h.in index b1de2c9..04cce1d 100644 --- a/src/glog/logging.h.in +++ b/src/glog/logging.h.in @@ -1107,6 +1107,12 @@ class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf { LogStreamBuf(char *buf, int len) { setp(buf, buf + len - 2); } + + // Resets the buffer. Useful if we reuse it by means of TLS. + void reset() { + setp(pbase(), epptr()); + } + // This effectively ignores overflow. virtual int_type overflow(int_type ch) { return ch; @@ -1169,6 +1175,7 @@ public: size_t pcount() const { return streambuf_.pcount(); } char* pbase() const { return streambuf_.pbase(); } char* str() const { return pbase(); } + void reset() { streambuf_.reset(); } private: LogStream(const LogStream&); diff --git a/src/logging.cc b/src/logging.cc index ec9eef1..42290f4 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -336,6 +336,7 @@ const size_t LogMessage::kMaxLogMessageLen = 30000; struct LogMessage::LogMessageData { LogMessageData(); + void reset(); int preserved_errno_; // preserved errno // Buffer space; contains complete message text. @@ -1141,10 +1142,22 @@ static bool fatal_msg_exclusive = true; static LogMessage::LogMessageData fatal_msg_data_exclusive; static LogMessage::LogMessageData fatal_msg_data_shared; +#ifdef GLOG_THREAD_LOCAL_STORAGE +// Static thread-local log data space to use, because typically at most one +// LogMessageData object exists (in this case glog makes zero heap memory +// allocations). +static GLOG_THREAD_LOCAL_STORAGE bool thread_data_available = true; +static GLOG_THREAD_LOCAL_STORAGE LogMessage::LogMessageData thread_msg_data; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) + LogMessage::LogMessageData::LogMessageData() : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) { } +void LogMessage::LogMessageData::reset() { + stream_.reset(); +} + LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int ctr, void (LogMessage::*send_method)()) : allocated_(NULL) { @@ -1197,8 +1210,22 @@ void LogMessage::Init(const char* file, void (LogMessage::*send_method)()) { allocated_ = NULL; if (severity != GLOG_FATAL || !exit_on_dfatal) { +#ifdef GLOG_THREAD_LOCAL_STORAGE + // No need for locking, because this is thread local. + if (thread_data_available) { + thread_data_available = false; + data_ = &thread_msg_data; + // Make sure to clear log data since it may have been used and filled with + // data. We do not want to append the new message to the previous one. + data_->reset(); + } else { + allocated_ = new LogMessageData(); + data_ = allocated_; + } +#else // !defined(GLOG_THREAD_LOCAL_STORAGE) allocated_ = new LogMessageData(); data_ = allocated_; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) data_->first_fatal_ = false; } else { MutexLock l(&fatal_msg_lock); @@ -1267,6 +1294,10 @@ void LogMessage::Init(const char* file, LogMessage::~LogMessage() { Flush(); +#ifdef GLOG_THREAD_LOCAL_STORAGE + if (data_ == &thread_msg_data) + thread_data_available = true; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) delete allocated_; } diff --git a/src/logging_unittest.cc b/src/logging_unittest.cc index b886222..76eb75b 100644 --- a/src/logging_unittest.cc +++ b/src/logging_unittest.cc @@ -272,12 +272,14 @@ void TestLogging(bool check_counts) { LOG(ERROR) << string("foo") << ' '<< j << ' ' << setw(10) << j << " " << setw(1) << hex << j; + LOG(ERROR) << (&LOG(ERROR) && 0) << " nested LOG"; + LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << "no prefix"; if (check_counts) { CHECK_EQ(base_num_infos + 14, LogMessage::num_messages(GLOG_INFO)); CHECK_EQ(base_num_warning + 3, LogMessage::num_messages(GLOG_WARNING)); - CHECK_EQ(base_num_errors + 15, LogMessage::num_messages(GLOG_ERROR)); + CHECK_EQ(base_num_errors + 17, LogMessage::num_messages(GLOG_ERROR)); } } diff --git a/src/logging_unittest.err b/src/logging_unittest.err index 4f80bf5..e08836f 100644 --- a/src/logging_unittest.err +++ b/src/logging_unittest.err @@ -74,6 +74,8 @@ WDATE TIME__ THREADID logging_unittest.cc:LINE] log_if this IDATE TIME__ THREADID logging_unittest.cc:LINE] array IDATE TIME__ THREADID logging_unittest.cc:LINE] const array EDATE TIME__ THREADID logging_unittest.cc:LINE] foo 1000 0000001000 3e8 +EDATE TIME__ THREADID logging_unittest.cc:LINE] 0 nested LOG +EDATE TIME__ THREADID logging_unittest.cc:LINE] no prefix IDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo bar 10 3.400000 WDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: array -- cgit v1.2.3