rocksdb/port/win/win_logger.cc
mrambacher 3dff28cf9b Use SystemClock* instead of std::shared_ptr<SystemClock> in lower level routines (#8033)
Summary:
For performance purposes, the lower level routines were changed to use a SystemClock* instead of a std::shared_ptr<SystemClock>.  The shared ptr has some performance degradation on certain hardware classes.

For most of the system, there is no risk of the pointer being deleted/invalid because the shared_ptr will be stored elsewhere.  For example, the ImmutableDBOptions stores the Env which has a std::shared_ptr<SystemClock> in it.  The SystemClock* within the ImmutableDBOptions is essentially a "short cut" to gain access to this constant resource.

There were a few classes (PeriodicWorkScheduler?) where the "short cut" property did not hold.  In those cases, the shared pointer was preserved.

Using db_bench readrandom perf_level=3 on my EC2 box, this change performed as well or better than 6.17:

6.17: readrandom   :      28.046 micros/op 854902 ops/sec;   61.3 MB/s (355999 of 355999 found)
6.18: readrandom   :      32.615 micros/op 735306 ops/sec;   52.7 MB/s (290999 of 290999 found)
PR: readrandom   :      27.500 micros/op 871909 ops/sec;   62.5 MB/s (367999 of 367999 found)

(Note that the times for 6.18 are prior to revert of the SystemClock).

Pull Request resolved: https://github.com/facebook/rocksdb/pull/8033

Reviewed By: pdillinger

Differential Revision: D27014563

Pulled By: mrambacher

fbshipit-source-id: ad0459eba03182e454391b5926bf5cdd45657b67
2021-03-15 04:34:11 -07:00

195 lines
5.3 KiB
C++

// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// Logger implementation that can be shared by all environments
// where enough posix functionality is available.
#if defined(OS_WIN)
#include "port/win/win_logger.h"
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <algorithm>
#include <atomic>
#include "monitoring/iostats_context_imp.h"
#include "port/sys_time.h"
#include "port/win/env_win.h"
#include "port/win/io_win.h"
#include "rocksdb/env.h"
#include "rocksdb/system_clock.h"
namespace ROCKSDB_NAMESPACE {
namespace port {
WinLogger::WinLogger(uint64_t (*gettid)(), SystemClock* clock, HANDLE file,
const InfoLogLevel log_level)
: Logger(log_level),
file_(file),
gettid_(gettid),
log_size_(0),
last_flush_micros_(0),
clock_(clock),
flush_pending_(false) {
assert(file_ != NULL);
assert(file_ != INVALID_HANDLE_VALUE);
}
void WinLogger::DebugWriter(const char* str, int len) {
assert(file_ != INVALID_HANDLE_VALUE);
DWORD bytesWritten = 0;
BOOL ret = WriteFile(file_, str, len, &bytesWritten, NULL);
if (ret == FALSE) {
std::string errSz = GetWindowsErrSz(GetLastError());
fprintf(stderr, "%s", errSz.c_str());
}
}
WinLogger::~WinLogger() { CloseInternal().PermitUncheckedError(); }
Status WinLogger::CloseImpl() {
return CloseInternal();
}
Status WinLogger::CloseInternal() {
Status s;
if (INVALID_HANDLE_VALUE != file_) {
BOOL ret = FlushFileBuffers(file_);
if (ret == 0) {
auto lastError = GetLastError();
s = IOErrorFromWindowsError("Failed to flush LOG on Close() ", lastError);
}
ret = CloseHandle(file_);
// On error the return value is zero
if (ret == 0 && s.ok()) {
auto lastError = GetLastError();
s = IOErrorFromWindowsError("Failed to flush LOG on Close() ", lastError);
}
file_ = INVALID_HANDLE_VALUE;
closed_ = true;
}
return s;
}
void WinLogger::Flush() {
assert(file_ != INVALID_HANDLE_VALUE);
if (flush_pending_) {
flush_pending_ = false;
// With Windows API writes go to OS buffers directly so no fflush needed
// unlike with C runtime API. We don't flush all the way to disk
// for perf reasons.
}
last_flush_micros_ = clock_->NowMicros();
}
void WinLogger::Logv(const char* format, va_list ap) {
IOSTATS_TIMER_GUARD(logger_nanos);
assert(file_ != INVALID_HANDLE_VALUE);
const uint64_t thread_id = (*gettid_)();
// We try twice: the first time with a fixed-size stack allocated buffer,
// and the second time with a much larger dynamically allocated buffer.
char buffer[500];
std::unique_ptr<char[]> largeBuffer;
for (int iter = 0; iter < 2; ++iter) {
char* base;
int bufsize;
if (iter == 0) {
bufsize = sizeof(buffer);
base = buffer;
} else {
bufsize = 30000;
largeBuffer.reset(new char[bufsize]);
base = largeBuffer.get();
}
char* p = base;
char* limit = base + bufsize;
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
const time_t seconds = now_tv.tv_sec;
struct tm t;
localtime_s(&t, &seconds);
p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec),
static_cast<long long unsigned int>(thread_id));
// Print the message
if (p < limit) {
va_list backup_ap;
va_copy(backup_ap, ap);
int done = vsnprintf(p, limit - p, format, backup_ap);
if (done > 0) {
p += done;
} else {
continue;
}
va_end(backup_ap);
}
// Truncate to available space if necessary
if (p >= limit) {
if (iter == 0) {
continue; // Try again with larger buffer
} else {
p = limit - 1;
}
}
// Add newline if necessary
if (p == base || p[-1] != '\n') {
*p++ = '\n';
}
assert(p <= limit);
const size_t write_size = p - base;
DWORD bytesWritten = 0;
BOOL ret = WriteFile(file_, base, static_cast<DWORD>(write_size),
&bytesWritten, NULL);
if (ret == FALSE) {
std::string errSz = GetWindowsErrSz(GetLastError());
fprintf(stderr, "%s", errSz.c_str());
}
flush_pending_ = true;
assert((bytesWritten == write_size) || (ret == FALSE));
if (bytesWritten > 0) {
log_size_ += write_size;
}
uint64_t now_micros =
static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
flush_pending_ = false;
// With Windows API writes go to OS buffers directly so no fflush needed
// unlike with C runtime API. We don't flush all the way to disk
// for perf reasons.
last_flush_micros_ = now_micros;
}
break;
}
}
size_t WinLogger::GetLogFileSize() const { return log_size_; }
}
} // namespace ROCKSDB_NAMESPACE
#endif