@@ -32,6 +32,7 @@
#include <stdio.h>
#include "openvswitch/shash.h"
+#include "openvswitch/util.h"
#ifdef __cplusplus
extern "C" {
@@ -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, /* 'elements' is a dynamically allocated array. */
+ JSON_ARRAY_INLINE_1, /* Static 'elements' with exactly 1 element. */
+ JSON_ARRAY_INLINE_2, /* Static 'elements' with exactly 2 elements. */
+ JSON_ARRAY_INLINE_3, /* Static 'elements' with exactly 3 elements. */
};
/* A JSON value. */
@@ -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 {
@@ -226,6 +226,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;
@@ -235,6 +236,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,
@@ -247,18 +281,43 @@ 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)
{
- return json->array.size ? json->array.elements[--json->array.size] : NULL;
+ if (!json_array_size(json)) {
+ return NULL;
+ }
+ if (json->storage_type == JSON_ARRAY_DYNAMIC) {
+ return json->array.elements[--json->array.size];
+ }
+ 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;
@@ -271,6 +330,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;
@@ -280,28 +340,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
@@ -399,14 +468,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 *
@@ -516,7 +599,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);
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 on heavy operations like compaction. Signed-off-by: Ilya Maximets <i.maximets@ovn.org> --- include/openvswitch/json.h | 20 ++++++- lib/json.c | 119 +++++++++++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 20 deletions(-)