@cryptotaxi247 / netdata-1 / commits / 47fa3d708

Speed up BUFFER increases (minimize reallocs) (#12792)

* speedup BUFFER increases by forward looking reallocs * implemented buffer_vsprintf() and optimized buffer_sprintf() to minimize calls to vsnprintfz() * optimize json generation for well known strings

Costa Tsaousis committed May 3, 2022 at 00:30 UTC 47fa3d708902fb001b2e88e4145d2a451549cd8e
3 files changed +114 -56
libnetdata/buffer/buffer.c
+48 -19
@@ -136,6 +136,24 @@ void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
136 wb->len += wstr - str;
137 }
138
139 +void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) {
140 + if(unlikely(!txt || !*txt)) return;
141 +
142 + buffer_need_bytes(wb, len + 1);
143 +
144 + char *s = &wb->buffer[wb->len];
145 + const char *end = &txt[len + 1];
146 +
147 + while(txt != end)
148 + *s++ = *txt++;
149 +
150 + wb->len += len;
151 +
152 + // keep it NULL terminating
153 + // not counting it at wb->len
154 + wb->buffer[wb->len] = '\0';
155 +}
156 +
157 void buffer_strcat(BUFFER *wb, const char *txt)
158 {
159 // buffer_sprintf(wb, "%s", txt);
@@ -159,8 +177,7 @@ void buffer_strcat(BUFFER *wb, const char *txt)
177 if(*txt) {
178 debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
179 len = strlen(txt);
162 - buffer_increase(wb, len);
163 - buffer_strcat(wb, txt);
180 + buffer_fast_strcat(wb, txt, len);
181 }
182 else {
183 // terminate the string
@@ -236,15 +253,23 @@ void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
253 {
254 if(unlikely(!fmt || !*fmt)) return;
255
239 - buffer_need_bytes(wb, 2);
256 + size_t wrote = 0, need = 2, space_remaining = 0;
257
241 - size_t len = wb->size - wb->len - 1;
258 + do {
259 + need += space_remaining * 2;
260
243 - wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
261 + debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu, by %zu bytes (wrote = %zu)\n", wb->len, wb->size, need, wrote);
262 + buffer_need_bytes(wb, need);
263
245 - buffer_overflow_check(wb);
264 + space_remaining = wb->size - wb->len - 1;
265
247 - // the buffer is \0 terminated by vsnprintfz
266 + wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], space_remaining, fmt, args);
267 +
268 + } while(wrote >= space_remaining);
269 +
270 + wb->len += wrote;
271 +
272 + // the buffer is \0 terminated by vsnprintf
273 }
274
275 void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
@@ -252,22 +277,21 @@ void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
277 if(unlikely(!fmt || !*fmt)) return;
278
279 va_list args;
255 - size_t wrote = 0, need = 2, multiplier = 0, len;
280 + size_t wrote = 0, need = 2, space_remaining = 0;
281
282 do {
258 - need += wrote + multiplier * WEB_DATA_LENGTH_INCREASE_STEP;
259 - multiplier++;
283 + need += space_remaining * 2;
284
285 debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu, by %zu bytes (wrote = %zu)\n", wb->len, wb->size, need, wrote);
286 buffer_need_bytes(wb, need);
287
264 - len = wb->size - wb->len - 1;
288 + space_remaining = wb->size - wb->len - 1;
289
290 va_start(args, fmt);
267 - wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
291 + wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], space_remaining, fmt, args);
292 va_end(args);
293
270 - } while(wrote >= len);
294 + } while(wrote >= space_remaining);
295
296 wb->len += wrote;
297
@@ -420,16 +444,21 @@ void buffer_increase(BUFFER *b, size_t free_size_required) {
444 buffer_overflow_check(b);
445
446 size_t left = b->size - b->len;
423 -
447 if(left >= free_size_required) return;
448
426 - size_t increase = free_size_required - left;
427 - if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
449 + size_t wanted = free_size_required - left;
450 + size_t minimum = WEB_DATA_LENGTH_INCREASE_STEP;
451 + if(minimum > wanted) wanted = minimum;
452 +
453 + size_t optimal = b->size;
454 + if(b->size > 5*1024*1024) optimal = b->size / 2;
455 +
456 + if(optimal > wanted) wanted = optimal;
457
429 - debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
458 + debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + wanted);
459
431 - b->buffer = reallocz(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
432 - b->size += increase;
460 + b->buffer = reallocz(b->buffer, b->size + wanted + sizeof(BUFFER_OVERFLOW_EOF) + 2);
461 + b->size += wanted;
462
463 buffer_overflow_init(b);
464 buffer_overflow_check(b);
libnetdata/buffer/buffer.h
+1
@@ -55,6 +55,7 @@ extern const char *buffer_tostring(BUFFER *wb);
55 extern void buffer_reset(BUFFER *wb);
56
57 extern void buffer_strcat(BUFFER *wb, const char *txt);
58 +extern void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len);
59 extern void buffer_rrd_value(BUFFER *wb, calculated_number value);
60
61 extern void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds);
web/api/formatters/json/json.c
+65 -37
@@ -16,7 +16,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
16
17 //info("RRD2JSON(): %s: BEGIN", r->st->id);
18 int row_annotations = 0, dates, dates_with_new = 0;
19 - char kq[2] = "", // key quote
19 + char kq[2] = "", // key quote
20 sq[2] = "", // string quote
21 pre_label[101] = "", // before each label
22 post_label[101] = "", // after each label
@@ -28,7 +28,8 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
28 normal_annotation[201] = "", // default row annotation
29 overflow_annotation[201] = "", // overflow row annotation
30 data_begin[101] = "", // between labels and values
31 - finish[101] = ""; // at the end of everything
31 + finish[101] = "", // at the end of everything
32 + object_rows_time[101] = "";
33
34 if(datatable) {
35 dates = JSON_DATES_JS;
@@ -49,7 +50,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
50 strcpy(post_value, "}");
51 strcpy(post_line, "]}");
52 snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
52 - strcpy(finish, "\n ]\n}");
53 + strcpy(finish, "\n]\n}");
54
55 snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
56 snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
@@ -76,23 +77,38 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
77 dates_with_new = 0;
78 }
79 if( options & RRDR_OPTION_OBJECTSROWS )
79 - strcpy(pre_date, " { ");
80 + strcpy(pre_date, " { ");
81 else
81 - strcpy(pre_date, " [ ");
82 - strcpy(pre_label, ", \"");
82 + strcpy(pre_date, " [ ");
83 + strcpy(pre_label, ",\"");
84 strcpy(post_label, "\"");
84 - strcpy(pre_value, ", ");
85 + strcpy(pre_value, ",");
86 if( options & RRDR_OPTION_OBJECTSROWS )
87 strcpy(post_line, "}");
88 else
89 strcpy(post_line, "]");
90 snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
90 - strcpy(finish, "\n ]\n}");
91 + strcpy(finish, "\n]\n}");
92
93 buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
94 buffer_sprintf(wb, "%stime%s", sq, sq);
95 +
96 + if( options & RRDR_OPTION_OBJECTSROWS )
97 + snprintfz(object_rows_time, 100, "%stime%s: ", kq, kq);
98 +
99 }
100
101 + size_t pre_value_len = strlen(pre_value);
102 + size_t post_value_len = strlen(post_value);
103 + size_t pre_label_len = strlen(pre_label);
104 + size_t post_label_len = strlen(post_label);
105 + size_t pre_date_len = strlen(pre_date);
106 + size_t post_date_len = strlen(post_date);
107 + size_t post_line_len = strlen(post_line);
108 + size_t normal_annotation_len = strlen(normal_annotation);
109 + size_t overflow_annotation_len = strlen(overflow_annotation);
110 + size_t object_rows_time_len = strlen(object_rows_time);
111 +
112 // -------------------------------------------------------------------------
113 // print the JSON header
114
@@ -104,18 +120,19 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
120 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
121 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
122
107 - buffer_strcat(wb, pre_label);
123 + buffer_fast_strcat(wb, pre_label, pre_label_len);
124 buffer_strcat(wb, rd->name);
125 // buffer_strcat(wb, ".");
126 // buffer_strcat(wb, rd->rrdset->name);
111 - buffer_strcat(wb, post_label);
127 + buffer_fast_strcat(wb, post_label, post_label_len);
128 i++;
129 }
130 if(!i) {
115 - buffer_strcat(wb, pre_label);
116 - buffer_strcat(wb, "no data");
117 - buffer_strcat(wb, post_label);
131 + buffer_fast_strcat(wb, pre_label, pre_label_len);
132 + buffer_fast_strcat(wb, "no data", 7);
133 + buffer_fast_strcat(wb, post_label, post_label_len);
134 }
135 + size_t total_number_of_dimensions = i;
136
137 // print the begin of row data
138 buffer_strcat(wb, data_begin);
@@ -133,6 +150,13 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
150 step = -1;
151 }
152
153 + // pre-allocate a large enough buffer for us
154 + // this does not need to be accurate - it is just a hint to avoid multiple realloc().
155 + buffer_need_bytes(wb,
156 + ( 20 * rrdr_rows(r)) // timestamp + json overhead
157 + + ( (pre_value_len + post_value_len + 4) * total_number_of_dimensions * rrdr_rows(r) ) // number
158 + );
159 +
160 // for each line in the array
161 calculated_number total = 1;
162 for(i = start; i != end ;i += step) {
@@ -146,48 +170,52 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
170 struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
171 if(!tm) { error("localtime_r() failed."); continue; }
172
149 - if(likely(i != start)) buffer_strcat(wb, ",\n");
150 - buffer_strcat(wb, pre_date);
173 + if(likely(i != start)) buffer_fast_strcat(wb, ",\n", 2);
174 + buffer_fast_strcat(wb, pre_date, pre_date_len);
175
176 if( options & RRDR_OPTION_OBJECTSROWS )
153 - buffer_sprintf(wb, "%stime%s: ", kq, kq);
177 + buffer_fast_strcat(wb, object_rows_time, object_rows_time_len);
178
155 - if(dates_with_new)
156 - buffer_strcat(wb, "new ");
179 + if(unlikely(dates_with_new))
180 + buffer_fast_strcat(wb, "new ", 4);
181
182 buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
183
160 - buffer_strcat(wb, post_date);
184 + buffer_fast_strcat(wb, post_date, post_date_len);
185
162 - if(row_annotations) {
186 + if(unlikely(row_annotations)) {
187 // google supports one annotation per row
188 int annotation_found = 0;
189 for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd ;c++, rd = rd->next) {
190 if(unlikely(!(r->od[c] & RRDR_DIMENSION_SELECTED))) continue;
191
168 - if(co[c] & RRDR_VALUE_RESET) {
169 - buffer_strcat(wb, overflow_annotation);
192 + if(unlikely(co[c] & RRDR_VALUE_RESET)) {
193 + buffer_fast_strcat(wb, overflow_annotation, overflow_annotation_len);
194 annotation_found = 1;
195 break;
196 }
197 }
174 - if(!annotation_found)
175 - buffer_strcat(wb, normal_annotation);
198 + if(likely(!annotation_found))
199 + buffer_fast_strcat(wb, normal_annotation, normal_annotation_len);
200 }
201 }
202 else {
203 // print the timestamp of the line
180 - if(likely(i != start)) buffer_strcat(wb, ",\n");
181 - buffer_strcat(wb, pre_date);
204 + if(likely(i != start))
205 + buffer_fast_strcat(wb, ",\n", 2);
206
183 - if( options & RRDR_OPTION_OBJECTSROWS )
184 - buffer_sprintf(wb, "%stime%s: ", kq, kq);
207 + buffer_fast_strcat(wb, pre_date, pre_date_len);
208 +
209 + if(unlikely( options & RRDR_OPTION_OBJECTSROWS ))
210 + buffer_fast_strcat(wb, object_rows_time, object_rows_time_len);
211
212 buffer_rrd_value(wb, (calculated_number)r->t[i]);
213 +
214 // in ms
188 - if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
215 + if(unlikely(options & RRDR_OPTION_MILLISECONDS))
216 + buffer_fast_strcat(wb, "000", 3);
217
190 - buffer_strcat(wb, post_date);
218 + buffer_fast_strcat(wb, post_date, post_date_len);
219 }
220
221 int set_min_max = 0;
@@ -213,16 +241,16 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
241
242 calculated_number n = cn[c];
243
216 - buffer_strcat(wb, pre_value);
244 + buffer_fast_strcat(wb, pre_value, pre_value_len);
245
218 - if( options & RRDR_OPTION_OBJECTSROWS )
246 + if(unlikely( options & RRDR_OPTION_OBJECTSROWS ))
247 buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
248
249 if(co[c] & RRDR_VALUE_EMPTY) {
222 - if(options & RRDR_OPTION_NULL2ZERO)
223 - buffer_strcat(wb, "0");
250 + if(unlikely(options & RRDR_OPTION_NULL2ZERO))
251 + buffer_fast_strcat(wb, "0", 1);
252 else
225 - buffer_strcat(wb, "null");
253 + buffer_fast_strcat(wb, "null", 4);
254 }
255 else {
256 if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
@@ -243,10 +271,10 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, struct
271 buffer_rrd_value(wb, n);
272 }
273
246 - buffer_strcat(wb, post_value);
274 + buffer_fast_strcat(wb, post_value, post_value_len);
275 }
276
249 - buffer_strcat(wb, post_line);
277 + buffer_fast_strcat(wb, post_line, post_line_len);
278 }
279
280 buffer_strcat(wb, finish);