json/write: add an output buffer

This improves output speed significantly, especially if the write
callback is expensive (e.g. issues a syscall or takes a lock).

On my test system, jsoncat citylots.json > /dev/null improves from
~2.8s to ~1.7s.

citylots.json: https://github.com/zemirco/sf-city-lots-json (~181 MiB)

Change-Id: I7d411ce92366712ed87ad5fc6e9b64828541db4d
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Daniel Verkamp 2016-12-19 10:42:02 -07:00
parent 2138676573
commit a509ddeb24
2 changed files with 59 additions and 7 deletions

View File

@ -47,6 +47,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include "spdk/likely.h"
#include "spdk/string.h" #include "spdk/string.h"
#define SPDK_JSON_MAX_NESTING_DEPTH 64 #define SPDK_JSON_MAX_NESTING_DEPTH 64

View File

@ -41,8 +41,34 @@ struct spdk_json_write_ctx {
bool new_indent; bool new_indent;
bool first_value; bool first_value;
bool failed; bool failed;
size_t buf_filled;
uint8_t buf[4096];
}; };
static int emit_buf_full(struct spdk_json_write_ctx *w, const void *data, size_t size);
static int
fail(struct spdk_json_write_ctx *w)
{
w->failed = true;
return -1;
}
static int
flush_buf(struct spdk_json_write_ctx *w)
{
int rc;
rc = w->write_cb(w->cb_ctx, w->buf, w->buf_filled);
if (rc != 0) {
return fail(w);
}
w->buf_filled = 0;
return 0;
}
struct spdk_json_write_ctx * struct spdk_json_write_ctx *
spdk_json_write_begin(spdk_json_write_cb write_cb, void *cb_ctx, uint32_t flags) spdk_json_write_begin(spdk_json_write_cb write_cb, void *cb_ctx, uint32_t flags)
{ {
@ -60,6 +86,7 @@ spdk_json_write_begin(spdk_json_write_cb write_cb, void *cb_ctx, uint32_t flags)
w->new_indent = false; w->new_indent = false;
w->first_value = true; w->first_value = true;
w->failed = false; w->failed = false;
w->buf_filled = 0;
return w; return w;
} }
@ -68,6 +95,7 @@ int
spdk_json_write_end(struct spdk_json_write_ctx *w) spdk_json_write_end(struct spdk_json_write_ctx *w)
{ {
bool failed; bool failed;
int rc;
if (w == NULL) { if (w == NULL) {
return 0; return 0;
@ -75,28 +103,51 @@ spdk_json_write_end(struct spdk_json_write_ctx *w)
failed = w->failed; failed = w->failed;
rc = flush_buf(w);
if (rc != 0) {
failed = true;
}
free(w); free(w);
return failed ? -1 : 0; return failed ? -1 : 0;
} }
static int static inline int
fail(struct spdk_json_write_ctx *w) emit(struct spdk_json_write_ctx *w, const void *data, size_t size)
{ {
w->failed = true; size_t buf_remain = sizeof(w->buf) - w->buf_filled;
return -1;
if (spdk_unlikely(size > buf_remain)) {
/* Not enough space in buffer for the new data. */
return emit_buf_full(w, data, size);
}
/* Copy the new data into buf. */
memcpy(w->buf + w->buf_filled, data, size);
w->buf_filled += size;
return 0;
} }
static int static int
emit(struct spdk_json_write_ctx *w, const void *data, size_t size) emit_buf_full(struct spdk_json_write_ctx *w, const void *data, size_t size)
{ {
size_t buf_remain = sizeof(w->buf) - w->buf_filled;
int rc; int rc;
rc = w->write_cb(w->cb_ctx, data, size); assert(size > buf_remain);
/* Copy as much of the new data as possible into the buffer and flush it. */
memcpy(w->buf + w->buf_filled, data, buf_remain);
w->buf_filled += buf_remain;
rc = flush_buf(w);
if (rc != 0) { if (rc != 0) {
return fail(w); return fail(w);
} }
return 0;
/* Recurse to emit the rest of the data. */
return emit(w, data + buf_remain, size - buf_remain);
} }
static int static int