Skip to content

Commit 1d92ad9

Browse files
author
Rao Meng
committed
optimize latency and code size; upgrade fmtlib to 8.1.0
1 parent e2b4546 commit 1d92ad9

File tree

4 files changed

+93
-100
lines changed

4 files changed

+93
-100
lines changed

bench/spdlog

Submodule spdlog updated 113 files

fmt

Submodule fmt updated 68 files

fmtlog-inl.h

+26-3
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class fmtlogDetailT
227227
: tb(buffer) {}
228228

229229
fmtlog::ThreadBuffer* tb;
230-
const fmtlog::SPSCVarQueueOPT<>::MsgHeader* header = nullptr;
230+
const fmtlog::SPSCVarQueueOPT::MsgHeader* header = nullptr;
231231
};
232232
std::vector<HeapNode> bgThreadBuffers;
233233
std::mutex logInfoMutex;
@@ -343,7 +343,7 @@ class fmtlogDetailT
343343
if (thr.joinable()) thr.join();
344344
}
345345

346-
void handleLog(fmt::string_view threadName, const fmtlog::SPSCVarQueueOPT<>::MsgHeader* header) {
346+
void handleLog(fmt::string_view threadName, const fmtlog::SPSCVarQueueOPT::MsgHeader* header) {
347347
setArgVal<6>(threadName);
348348
StaticLogInfo& info = bgLogInfos[header->logId];
349349
const char* data = (const char*)(header + 1);
@@ -375,7 +375,7 @@ class fmtlogDetailT
375375
logLevel = (const char*)"DBG INF WRN ERR OFF" + (info.logLevel << 2);
376376

377377
size_t headerPos = membuf.size();
378-
fmt::detail::vformat_to(membuf, headerPattern, fmt::basic_format_args(args.data(), parttenArgSize));
378+
fmtlog::vformat_to(membuf, headerPattern, fmt::basic_format_args(args.data(), parttenArgSize));
379379
size_t bodyPos = membuf.size();
380380

381381
if (info.formatToFn) {
@@ -492,6 +492,29 @@ void fmtlogT<_>::registerLogInfo(uint32_t& logId, FormatToFn fn, const char* loc
492492
d.logInfos.emplace_back(fn, location, level, fmtString);
493493
}
494494

495+
template<int _>
496+
void fmtlogT<_>::vformat_to(fmtlog::MemoryBuffer& out, fmt::string_view fmt,
497+
fmt::format_args args) {
498+
fmt::detail::vformat_to(out, fmt, args);
499+
}
500+
501+
template<int _>
502+
size_t fmtlogT<_>::formatted_size(fmt::string_view fmt, fmt::format_args args) {
503+
auto buf = fmt::detail::counting_buffer<>();
504+
fmt::detail::vformat_to(buf, fmt, args);
505+
return buf.count();
506+
}
507+
508+
template<int _>
509+
void fmtlogT<_>::vformat_to(char* out, fmt::string_view fmt, fmt::format_args args) {
510+
fmt::vformat_to(out, fmt, args);
511+
}
512+
513+
template<int _>
514+
fmtlogT<_>::SPSCVarQueueOPT::MsgHeader* fmtlogT<_>::SPSCVarQueueOPT::allocMsg(uint32_t size) {
515+
return alloc(size);
516+
}
517+
495518
template<int _>
496519
void fmtlogT<_>::preallocate() {
497520
fmtlogDetailWrapper<>::impl.preallocate();

fmtlog.h

+65-95
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct UnrefPtr<std::unique_ptr<Arg, D>> : std::true_type
7575
template<typename Arg>
7676
struct UnrefPtr<Arg*> : std::true_type
7777
{ using type = Arg; };
78+
7879
}; // namespace fmtlogdetail
7980

8081
template<int __ = 0>
@@ -101,8 +102,8 @@ class fmtlogT
101102
// Set the file for logging
102103
static void setLogFile(const char* filename, bool truncate = false);
103104

104-
// Set an existing FILE* for logging, if manageFp is false fmtlog will not buffer log internally and will not close
105-
// the FILE*
105+
// Set an existing FILE* for logging, if manageFp is false fmtlog will not buffer log internally
106+
// and will not close the FILE*
106107
static void setLogFile(FILE* fp, bool manageFp = false);
107108

108109
// Collect log msgs from all threads and write to log file
@@ -130,7 +131,8 @@ class fmtlogT
130131
// bodyPos: log body index in the msg
131132
// logFilePos: log file position of this msg
132133
typedef void (*LogCBFn)(int64_t ns, LogLevel level, fmt::string_view location, size_t basePos,
133-
fmt::string_view threadName, fmt::string_view msg, size_t bodyPos, size_t logFilePos);
134+
fmt::string_view threadName, fmt::string_view msg, size_t bodyPos,
135+
size_t logFilePos);
134136

135137
// Set a callback function for all log msgs with a mininum log level
136138
static void setLogCB(LogCBFn cb, LogLevel minCBLogLevel);
@@ -158,54 +160,30 @@ class fmtlogT
158160
// Stop the polling thread
159161
static void stopPollingThread();
160162

161-
private:
162-
fmtlogT() { init(); }
163-
164-
void init() {
165-
if (!inited) {
166-
inited = true;
167-
tscns.init();
168-
currentLogLevel = INF;
169-
}
170-
}
171-
172-
template<int>
173-
friend class fmtlogDetailT;
174-
template<int>
175-
friend struct fmtlogWrapper;
176-
template<typename S, typename... Args>
177-
friend void test(const S& format, Args&&...);
178-
179-
using Context = fmt::format_context;
180-
using MemoryBuffer = fmt::basic_memory_buffer<char, 10000>;
181-
typedef const char* (*FormatToFn)(fmt::string_view format, const char* data, MemoryBuffer& out, int& argIdx,
182-
std::vector<fmt::basic_format_arg<Context>>& args);
183-
184-
static void registerLogInfo(uint32_t& logId, FormatToFn fn, const char* location, LogLevel level,
185-
fmt::string_view fmtString);
186-
187163
// https://github.com/MengRao/SPSC_Queue
188-
template<uint32_t Bytes = 1 << 20>
189164
class SPSCVarQueueOPT
190165
{
191166
public:
192167
struct MsgHeader
193168
{
169+
inline void push(uint32_t sz) { *(volatile uint32_t*)&size = sz + sizeof(MsgHeader); }
170+
194171
uint32_t size;
195172
uint32_t logId;
196173
};
197-
static constexpr uint32_t BLK_CNT = Bytes / sizeof(MsgHeader);
174+
static constexpr uint32_t BLK_CNT = (1 << 20) / sizeof(MsgHeader);
175+
176+
MsgHeader* allocMsg(uint32_t size);
198177

199-
MsgHeader* alloc(uint32_t size_) {
200-
size = size_ + sizeof(MsgHeader);
178+
MsgHeader* alloc(uint32_t size) {
179+
size += sizeof(MsgHeader);
201180
uint32_t blk_sz = (size + sizeof(MsgHeader) - 1) / sizeof(MsgHeader);
202181
if (blk_sz >= free_write_cnt) {
203182
uint32_t read_idx_cache = *(volatile uint32_t*)&read_idx;
204183
if (read_idx_cache <= write_idx) {
205184
free_write_cnt = BLK_CNT - write_idx;
206185
if (blk_sz >= free_write_cnt && read_idx_cache != 0) { // wrap around
207186
blk[0].size = 0;
208-
std::atomic_thread_fence(std::memory_order_release);
209187
blk[write_idx].size = 1;
210188
write_idx = 0;
211189
free_write_cnt = read_idx_cache;
@@ -218,28 +196,14 @@ class fmtlogT
218196
return nullptr;
219197
}
220198
}
221-
return &blk[write_idx];
222-
}
223-
224-
void push() {
225-
uint32_t blk_sz = (size + sizeof(MsgHeader) - 1) / sizeof(MsgHeader);
226-
blk[write_idx + blk_sz].size = 0;
227-
std::atomic_thread_fence(std::memory_order_release);
228-
blk[write_idx].size = size;
199+
MsgHeader* ret = &blk[write_idx];
229200
write_idx += blk_sz;
230201
free_write_cnt -= blk_sz;
202+
blk[write_idx].size = 0;
203+
return ret;
231204
}
232205

233-
template<typename Writer>
234-
bool tryPush(uint32_t size, Writer writer) {
235-
MsgHeader* header = alloc(size);
236-
if (!header) return false;
237-
writer(header);
238-
push();
239-
return true;
240-
}
241-
242-
const MsgHeader* front() {
206+
inline const MsgHeader* front() {
243207
uint32_t size = blk[read_idx].size;
244208
if (size == 1) { // wrap around
245209
read_idx = 0;
@@ -249,33 +213,22 @@ class fmtlogT
249213
return &blk[read_idx];
250214
}
251215

252-
void pop() {
216+
inline void pop() {
253217
uint32_t blk_sz = (blk[read_idx].size + sizeof(MsgHeader) - 1) / sizeof(MsgHeader);
254218
*(volatile uint32_t*)&read_idx = read_idx + blk_sz;
255219
}
256220

257-
template<typename Reader>
258-
bool tryPop(Reader reader) {
259-
MsgHeader* header = front();
260-
if (!header) return false;
261-
reader(header);
262-
pop();
263-
return true;
264-
}
265-
266221
private:
267222
alignas(64) MsgHeader blk[BLK_CNT] = {};
268-
269-
alignas(128) uint32_t write_idx = 0;
223+
uint32_t write_idx = 0;
270224
uint32_t free_write_cnt = BLK_CNT;
271-
uint32_t size;
272225

273226
alignas(128) uint32_t read_idx = 0;
274227
};
275228

276229
struct ThreadBuffer
277230
{
278-
SPSCVarQueueOPT<> varq;
231+
SPSCVarQueueOPT varq;
279232
bool shouldDeallocate = false;
280233
char name[32];
281234
size_t nameSize;
@@ -294,7 +247,8 @@ class fmtlogT
294247
}
295248
else {
296249
#ifdef _WIN32
297-
return calibrate(1000000 * 100); // wait more time as Windows' system time is in 100ns precision
250+
return calibrate(1000000 *
251+
100); // wait more time as Windows' system time is in 100ns precision
298252
#else
299253
return calibrate(1000000 * 10); //
300254
#endif
@@ -355,18 +309,33 @@ class fmtlogT
355309

356310
void adjustOffset() { ns_offset = base_ns - (int64_t)(base_tsc * tsc_ghz_inv); }
357311

358-
alignas(64) double tsc_ghz_inv; // make sure tsc_ghz_inv and ns_offset are on the same cache line
312+
alignas(64) double tsc_ghz_inv;
359313
int64_t ns_offset;
360314
int64_t base_tsc;
361315
int64_t base_ns;
362316
};
363317

364-
bool inited = false;
318+
void init() {
319+
tscns.init();
320+
currentLogLevel = INF;
321+
}
322+
323+
using Context = fmt::format_context;
324+
using MemoryBuffer = fmt::basic_memory_buffer<char, 10000>;
325+
typedef const char* (*FormatToFn)(fmt::string_view format, const char* data, MemoryBuffer& out,
326+
int& argIdx, std::vector<fmt::basic_format_arg<Context>>& args);
327+
328+
static void registerLogInfo(uint32_t& logId, FormatToFn fn, const char* location, LogLevel level,
329+
fmt::string_view fmtString);
330+
331+
static void vformat_to(MemoryBuffer& out, fmt::string_view fmt, fmt::format_args args);
332+
333+
static size_t formatted_size(fmt::string_view fmt, fmt::format_args args);
334+
335+
static void vformat_to(char* out, fmt::string_view fmt, fmt::format_args args);
365336

366-
public:
367337
TSCNS tscns;
368338

369-
private:
370339
volatile LogLevel currentLogLevel;
371340
static FAST_THREAD_LOCAL ThreadBuffer* threadBuffer;
372341

@@ -550,8 +519,8 @@ class fmtlogT
550519
}
551520

552521
template<typename... Args>
553-
static const char* formatTo(fmt::string_view format, const char* data, MemoryBuffer& out, int& argIdx,
554-
std::vector<fmt::basic_format_arg<Context>>& args) {
522+
static const char* formatTo(fmt::string_view format, const char* data, MemoryBuffer& out,
523+
int& argIdx, std::vector<fmt::basic_format_arg<Context>>& args) {
555524
constexpr size_t num_args = sizeof...(Args);
556525
constexpr size_t num_dtors = fmt::detail::count<needCallDtor<Args>()...>();
557526
const char* dtor_args[std::max(num_dtors, (size_t)1)];
@@ -564,7 +533,7 @@ class fmtlogT
564533
else {
565534
ret = decodeArgs<true, 0, 0, Args...>(data, args.data() + argIdx, dtor_args);
566535
}
567-
fmt::detail::vformat_to(out, format, fmt::basic_format_args(args.data() + argIdx, num_args));
536+
vformat_to(out, format, fmt::basic_format_args(args.data() + argIdx, num_args));
568537
destructArgs<0, Args...>(dtor_args);
569538

570539
return ret;
@@ -642,17 +611,17 @@ class fmtlogT
642611
}
643612
constexpr size_t num_cstring = fmt::detail::count<isCstring<Args>()...>();
644613
size_t cstringSizes[std::max(num_cstring, (size_t)1)];
645-
size_t allocSize = getArgSizes<0>(cstringSizes, args...) + 8;
614+
size_t alloc_size = 8 + getArgSizes<0>(cstringSizes, args...);
646615
if (threadBuffer == nullptr) preallocate();
647616
do {
648-
if (threadBuffer->varq.tryPush(allocSize, [&](typename SPSCVarQueueOPT<>::MsgHeader* header) {
649-
header->logId = logId;
650-
char* writePos = (char*)(header + 1);
651-
*(int64_t*)writePos = tsc;
652-
writePos += 8;
653-
encodeArgs<0>(cstringSizes, writePos, std::forward<Args>(args)...);
654-
}))
655-
return;
617+
auto header = threadBuffer->varq.allocMsg(alloc_size);
618+
if (!header) continue;
619+
header->logId = logId;
620+
char* out = (char*)(header + 1);
621+
*(int64_t*)out = tsc;
622+
out += 8;
623+
encodeArgs<0>(cstringSizes, out, std::forward<Args>(args)...);
624+
header->push(alloc_size);
656625
} while (FMTLOG_BLOCK);
657626
}
658627

@@ -663,20 +632,21 @@ class fmtlogT
663632
fmt::detail::check_format_string<Args...>(format);
664633
}
665634
fmt::string_view sv(format);
666-
size_t formatted_size = fmt::formatted_size(fmt::runtime(sv), args...);
667-
size_t allocSize = formatted_size + 8 + 8;
635+
auto&& fmt_args = fmt::make_format_args(args...);
636+
size_t fmt_size = formatted_size(sv, fmt_args);
637+
size_t alloc_size = 8 + 8 + fmt_size;
668638
if (threadBuffer == nullptr) preallocate();
669639
do {
670-
if (threadBuffer->varq.tryPush(allocSize, [&](typename SPSCVarQueueOPT<>::MsgHeader* header) {
671-
header->logId = (uint32_t)level;
672-
char* writePos = (char*)(header + 1);
673-
*(int64_t*)writePos = tscns.rdtsc();
674-
writePos += 8;
675-
*(const char**)writePos = location;
676-
writePos += 8;
677-
fmt::format_to(writePos, fmt::runtime(sv), args...);
678-
}))
679-
return;
640+
auto header = threadBuffer->varq.allocMsg(alloc_size);
641+
if (!header) continue;
642+
header->logId = (uint32_t)level;
643+
char* out = (char*)(header + 1);
644+
*(int64_t*)out = tscns.rdtsc();
645+
out += 8;
646+
*(const char**)out = location;
647+
out += 8;
648+
vformat_to(out, sv, fmt_args);
649+
header->push(alloc_size);
680650
} while (FMTLOG_BLOCK);
681651
}
682652
};

0 commit comments

Comments
 (0)