Skip to content

Commit

Permalink
json: Store short arrays in-place.
Browse files Browse the repository at this point in the history
Similarly to strings, 24 bytes that we have in 'struct json' can fit
up to 3 JSON_ARRAY elements.  And we can use separate storage types
to count them.

There are many small arrays in typical databases, for example, every
UUID is a 2-element array.  So, the change does have a noticeable
performance impact.

With 350MB OVN Northbound database with 12M atoms:

                         Before        After       Improvement
 ovsdb-client dump      16.6 sec      14.9 sec       10.2 %
 Compaction             13.4 sec      11.0 sec       17.9 %
 Memory usage (RSS)     2.05 GB       1.90 GB         7.3 %

With 615MB OVN Southbound database with 23M atoms:

                         Before        After       Improvement
 ovsdb-client dump      43.7 sec      40.5 sec        7.3 %
 Compaction             32.5 sec      29.4 sec        9.5 %
 Memory usage (RSS)     4.80 GB       4.46 GB         7.1 %

In the results above, 'ovsdb-client dump' is measuring how log it
takes for the server to prepare and send a reply, 'Memory usage (RSS)'
reflects the RSS of the ovsdb-server after loading the full database.
ovn-heater tests report similar reduction in CPU and memory usage.

Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
igsilya committed Dec 17, 2024
1 parent 1665537 commit ff9c8f4
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 20 deletions.
20 changes: 17 additions & 3 deletions include/openvswitch/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include <stdio.h>
#include "openvswitch/shash.h"
#include "openvswitch/util.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -66,9 +67,19 @@ struct json_array {
/* Maximum string length that can be stored inline ('\0' is not included). */
#define JSON_STRING_INLINE_LEN (sizeof(struct json_array) - 1)

#define JSON_ARRAY_INLINE_LEN 3
BUILD_ASSERT_DECL(sizeof(struct json_array) / sizeof(struct json *)
>= JSON_ARRAY_INLINE_LEN);

enum json_storage_type {
JSON_STRING_DYNAMIC = 0, /* JSON_STRING is stored via 'str_ptr'. */
JSON_STRING_INLINE, /* JSON_STRING is stored in 'str' array. */
/* JSON_STRING storage types. */
JSON_STRING_DYNAMIC = 0, /* Stored via 'str_ptr'. */
JSON_STRING_INLINE, /* Stored in 'str' array. */
/* JSON_ARRAY storage types.*/
JSON_ARRAY_DYNAMIC, /* 'elems' is a dynamically allocated array. */
JSON_ARRAY_INLINE_1, /* Static 'elems' with exactly 1 element. */
JSON_ARRAY_INLINE_2, /* Static 'elems' with exactly 2 elements. */
JSON_ARRAY_INLINE_3, /* Static 'elems' with exactly 3 elements. */
};

/* A JSON value. */
Expand All @@ -78,7 +89,10 @@ struct json {
size_t count;
union {
struct shash *object; /* Contains "struct json *"s. */
struct json_array array;
union {
struct json *elements[JSON_ARRAY_INLINE_LEN];
struct json_array array;
}; /* JSON_ARRAY. */
long long int integer;
double real;
union {
Expand Down
119 changes: 102 additions & 17 deletions lib/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ struct json *
json_array_create_empty(void)
{
struct json *json = json_create(JSON_ARRAY);
json->storage_type = JSON_ARRAY_DYNAMIC;
json->array.elements = NULL;
json->array.size = 0;
json->array.allocated = 0;
Expand All @@ -230,6 +231,39 @@ json_array_create_empty(void)
void
json_array_add(struct json *array_, struct json *element)
{
switch (array_->storage_type) {
case JSON_ARRAY_DYNAMIC:
if (!array_->array.size) {
array_->storage_type = JSON_ARRAY_INLINE_1;
array_->elements[0] = element;
return;
}
break;

case JSON_ARRAY_INLINE_1:
case JSON_ARRAY_INLINE_2:
array_->elements[array_->storage_type - JSON_ARRAY_DYNAMIC] = element;
array_->storage_type++;
return;

case JSON_ARRAY_INLINE_3: {
struct json **elements = xmalloc(4 * sizeof *elements);

memcpy(elements, array_->elements, 3 * sizeof array_->elements[0]);
array_->array.elements = elements;
array_->array.elements[3] = element;
array_->array.size = 4;
array_->array.allocated = 4;
array_->storage_type = JSON_ARRAY_DYNAMIC;
return;
}

case JSON_STRING_DYNAMIC:
case JSON_STRING_INLINE:
default:
OVS_NOT_REACHED();
}

struct json_array *array = &array_->array;
if (array->size >= array->allocated) {
array->elements = x2nrealloc(array->elements, &array->allocated,
Expand All @@ -242,20 +276,45 @@ void
json_array_set(struct json *json, size_t index, struct json *element)
{
ovs_assert(json_array_size(json) > index);
json->array.elements[index] = element;
if (json->storage_type == JSON_ARRAY_DYNAMIC) {
json->array.elements[index] = element;
} else {
json->elements[index] = element;
}
}

struct json *
json_array_pop(struct json *json)
{
size_t n = json_array_size(json);

return n ? json->array.elements[--n] : NULL;
if (!n) {
return NULL;
}
if (json->storage_type == JSON_ARRAY_DYNAMIC) {
return json->array.elements[--n];
}
if (json->storage_type > JSON_ARRAY_INLINE_1) {
return json->elements[--json->storage_type - JSON_ARRAY_DYNAMIC];
}

/* Need to fall back to an empty array in case it's the last
* inline element. */
struct json *element = json->elements[0];
json->storage_type = JSON_ARRAY_DYNAMIC;
json->array.elements = NULL;
json->array.size = 0;
json->array.allocated = 0;
return element;
}

void
json_array_trim(struct json *array_)
{
if (array_->storage_type != JSON_ARRAY_DYNAMIC) {
return;
}

struct json_array *array = &array_->array;
if (array->size < array->allocated) {
array->allocated = array->size;
Expand All @@ -268,6 +327,7 @@ struct json *
json_array_create(struct json **elements, size_t n)
{
struct json *json = json_create(JSON_ARRAY);
json->storage_type = JSON_ARRAY_DYNAMIC;
json->array.elements = elements;
json->array.size = n;
json->array.allocated = n;
Expand All @@ -277,28 +337,37 @@ json_array_create(struct json **elements, size_t n)
struct json *
json_array_create_1(struct json *elem0)
{
struct json **elements = xmalloc(sizeof *elements);
elements[0] = elem0;
return json_array_create(elements, 1);
struct json *json = json_create(JSON_ARRAY);

json->storage_type = JSON_ARRAY_INLINE_1;
json->elements[0] = elem0;

return json;
}

struct json *
json_array_create_2(struct json *elem0, struct json *elem1)
{
struct json **elements = xmalloc(2 * sizeof *elements);
elements[0] = elem0;
elements[1] = elem1;
return json_array_create(elements, 2);
struct json *json = json_create(JSON_ARRAY);

json->storage_type = JSON_ARRAY_INLINE_2;
json->elements[0] = elem0;
json->elements[1] = elem1;

return json;
}

struct json *
json_array_create_3(struct json *elem0, struct json *elem1, struct json *elem2)
{
struct json **elements = xmalloc(3 * sizeof *elements);
elements[0] = elem0;
elements[1] = elem1;
elements[2] = elem2;
return json_array_create(elements, 3);
struct json *json = json_create(JSON_ARRAY);

json->storage_type = JSON_ARRAY_INLINE_3;
json->elements[0] = elem0;
json->elements[1] = elem1;
json->elements[2] = elem2;

return json;
}

bool
Expand Down Expand Up @@ -390,14 +459,28 @@ size_t
json_array_size(const struct json *json)
{
ovs_assert(json->type == JSON_ARRAY);
return json->array.size;
if (json->storage_type == JSON_ARRAY_DYNAMIC) {
return json->array.size;
}
return json->storage_type - JSON_ARRAY_DYNAMIC;
}

const struct json *
json_array_at(const struct json *json, size_t index)
{
ovs_assert(json->type == JSON_ARRAY);
return (json->array.size > index) ? json->array.elements[index] : NULL;

if (json->storage_type == JSON_ARRAY_DYNAMIC) {
if (json->array.size <= index) {
return NULL;
}
return json->array.elements[index];
}

if (json->storage_type - JSON_ARRAY_DYNAMIC <= index) {
return NULL;
}
return json->elements[index];
}

struct shash *
Expand Down Expand Up @@ -507,7 +590,9 @@ json_destroy_array(struct json *json, bool yield)
json_destroy(CONST_CAST(struct json *, json_array_at(json, i)));
}
}
free(json->array.elements);
if (json->storage_type == JSON_ARRAY_DYNAMIC) {
free(json->array.elements);
}
}

static struct json *json_deep_clone_object(const struct shash *object);
Expand Down

0 comments on commit ff9c8f4

Please sign in to comment.