Skip to content

Commit cda6385

Browse files
common: update logging to enforce max_capacity and optimize queue resizing (#24490)
* common: update logging to enforce max_capacity and optimize queue resizing logic * common/log: remove queue expansion logic
1 parent 890f1a2 commit cda6385

1 file changed

Lines changed: 81 additions & 75 deletions

File tree

common/log.cpp

Lines changed: 81 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@
1111
#include <sstream>
1212
#include <thread>
1313
#include <vector>
14+
#include <algorithm>
1415

1516
#if defined(_WIN32)
17+
# define WIN32_LEAN_AND_MEAN
18+
# ifndef NOMINMAX
19+
# define NOMINMAX
20+
# endif
1621
# include <io.h>
1722
# include <windows.h>
1823
# define isatty _isatty
@@ -62,16 +67,15 @@ static const char* g_col[] = {
6267
};
6368

6469
struct common_log_entry {
65-
enum ggml_log_level level;
66-
67-
bool prefix;
68-
69-
int64_t timestamp;
70+
enum ggml_log_level level {GGML_LOG_LEVEL_INFO};
7071

7172
std::vector<char> msg;
7273

73-
// signals the worker thread to stop
74-
bool is_end;
74+
int64_t timestamp { 0 };
75+
bool is_end { false }; // signals the worker thread to stop
76+
bool prefix { false };
77+
78+
common_log_entry(size_t size = 256) : msg(size) { }
7579

7680
void print(FILE * file = nullptr) const {
7781
FILE * fcur = file;
@@ -122,22 +126,15 @@ struct common_log_entry {
122126
};
123127

124128
struct common_log {
125-
// default capacity - will be expanded if needed
126-
common_log() : common_log(256) {}
127-
128-
common_log(size_t capacity) {
129-
file = nullptr;
130-
prefix = false;
129+
// default capacity
130+
common_log(size_t capacity = 512) {
131+
file = nullptr;
132+
prefix = false;
131133
timestamps = false;
132-
running = false;
133-
t_start = t_us();
134-
135-
// initial message size - will be expanded if longer messages arrive
136-
entries.resize(capacity);
137-
for (auto & entry : entries) {
138-
entry.msg.resize(256);
139-
}
134+
running = false;
135+
t_start = t_us();
140136

137+
queue.resize(capacity, common_log_entry(256));
141138
head = 0;
142139
tail = 0;
143140

@@ -152,9 +149,10 @@ struct common_log {
152149
}
153150

154151
private:
155-
std::mutex mtx;
156-
std::thread thrd;
157-
std::condition_variable cv;
152+
std::mutex mtx;
153+
std::thread thrd;
154+
std::condition_variable cv_new; // new entry
155+
std::condition_variable cv_full; // wait on full
158156

159157
FILE * file;
160158

@@ -164,24 +162,53 @@ struct common_log {
164162

165163
int64_t t_start;
166164

167-
// ring buffer of entries
168-
std::vector<common_log_entry> entries;
165+
// queue of entries
166+
std::vector<common_log_entry> queue;
169167
size_t head;
170168
size_t tail;
171169

172-
// worker thread copies into this
173-
common_log_entry cur;
170+
bool print_entry(const common_log_entry & e) const {
171+
if (e.is_end) return true;
172+
173+
e.print();
174+
if (file) {
175+
e.print(file);
176+
}
177+
return false;
178+
}
179+
180+
bool flush_queue(size_t start_head, size_t end_tail, size_t & out_head) const {
181+
bool stop = false;
182+
size_t h = start_head;
183+
while (h != end_tail && !stop) {
184+
stop = print_entry(queue[h]);
185+
h = (h + 1) % queue.size();
186+
}
187+
out_head = h;
188+
return stop;
189+
}
174190

175191
public:
192+
bool is_full() const {
193+
return ((tail + 1) % queue.size()) == head;
194+
}
195+
196+
bool is_empty() const {
197+
return head == tail;
198+
}
199+
176200
void add(enum ggml_log_level level, const char * fmt, va_list args) {
177-
std::lock_guard<std::mutex> lock(mtx);
201+
std::unique_lock<std::mutex> lock(mtx);
202+
203+
// block if the queue is full
204+
cv_full.wait(lock, [this]() { return !running || !is_full(); });
178205

179206
if (!running) {
180207
// discard messages while the worker thread is paused
181208
return;
182209
}
183210

184-
auto & entry = entries[tail];
211+
auto & entry = queue[tail];
185212

186213
{
187214
// cannot use args twice, so make a copy in case we need to expand the buffer
@@ -216,38 +243,16 @@ struct common_log {
216243
va_end(args_copy);
217244
}
218245

219-
entry.level = level;
220-
entry.prefix = prefix;
246+
entry.is_end = false;
247+
entry.level = level;
248+
entry.prefix = prefix;
221249
entry.timestamp = 0;
222250
if (timestamps) {
223251
entry.timestamp = t_us() - t_start;
224252
}
225-
entry.is_end = false;
226-
227-
tail = (tail + 1) % entries.size();
228-
if (tail == head) {
229-
// expand the buffer
230-
std::vector<common_log_entry> new_entries(2*entries.size());
231-
232-
size_t new_tail = 0;
233253

234-
do {
235-
new_entries[new_tail] = std::move(entries[head]);
236-
237-
head = (head + 1) % entries.size();
238-
new_tail = (new_tail + 1);
239-
} while (head != tail);
240-
241-
head = 0;
242-
tail = new_tail;
243-
244-
for (size_t i = tail; i < new_entries.size(); i++) {
245-
new_entries[i].msg.resize(256);
246-
}
247-
248-
entries = std::move(new_entries);
249-
}
250-
cv.notify_one();
254+
tail = (tail + 1) % queue.size();
255+
cv_new.notify_one();
251256
}
252257

253258
void resume() {
@@ -261,22 +266,23 @@ struct common_log {
261266

262267
thrd = std::thread([this]() {
263268
while (true) {
264-
{
265-
std::unique_lock<std::mutex> lock(mtx);
266-
cv.wait(lock, [this]() { return head != tail; });
267-
cur = entries[head];
269+
std::unique_lock<std::mutex> lock(mtx);
270+
cv_new.wait(lock, [this]() { return !is_empty(); });
268271

269-
head = (head + 1) % entries.size();
270-
}
272+
size_t cached_head = head;
273+
size_t cached_tail = tail;
271274

272-
if (cur.is_end) {
273-
break;
274-
}
275+
lock.unlock(); // drop the lock during flush
276+
277+
size_t next_head;
278+
bool stop = flush_queue(cached_head, cached_tail, next_head);
275279

276-
cur.print(); // stdout and stderr
280+
lock.lock();
281+
head = next_head;
282+
cv_full.notify_all();
277283

278-
if (file) {
279-
cur.print(file);
284+
if (stop) {
285+
break;
280286
}
281287
}
282288
});
@@ -293,13 +299,13 @@ struct common_log {
293299
running = false;
294300

295301
// push an entry to signal the worker thread to stop
296-
{
297-
auto & entry = entries[tail];
298-
entry.is_end = true;
302+
auto & entry = queue[tail];
303+
entry.is_end = true;
304+
tail = (tail + 1) % queue.size();
299305

300-
tail = (tail + 1) % entries.size();
301-
}
302-
cv.notify_one();
306+
// wakeup everyone
307+
cv_new.notify_one();
308+
cv_full.notify_all();
303309
}
304310

305311
thrd.join();

0 commit comments

Comments
 (0)