On 25 Apr 2025, at 22:45, Dmitry Porokh via dev wrote:
> According to profiling data, converting UUIDs to strings is a frequent > operation in some workloads. This typically results in a call to > xasprintf(), which internally calls vsnprintf() twice, first to > calculate the required buffer size, and then to format the string. > > This patch introduces specialized functions for printing UUIDs, which > both reduces code duplication and improves performance. > > For example, on my laptop, 10,000,000 calls to the new uuid_to_string() > function takes 1296 ms, while the same number of xasprintf() calls using > UUID_FMT take 2498 ms. > > Signed-off-by: Dmitry Porokh <dpor...@nvidia.com> > --- Thanks, Dmitry, for your patch! With the exception of one small comment below, this patch looks good. Assuming we can fix this on commit, I’ll add my ACK. I believe all of Ilya’s comments are addressed, but I’ll leave some time before committing. Cheers, Eelco Acked-by: Eelco Chaudron echau...@redhat.com > v4: > - sad mistake of snprintf usage in ds_put_uuid fixed > v3: > - More clear commit message & summary > - Zero-copy version of ds_put_uuid > - snprintf instead of sprintf > - Comments and code cosmetic fixes > > include/openvswitch/dynamic-string.h | 3 +++ > include/openvswitch/json.h | 3 +++ > lib/db-ctl-base.c | 6 +++--- > lib/dynamic-string.c | 14 ++++++++++++++ > lib/json.c | 11 +++++++++++ > lib/ovsdb-cs.c | 4 +--- > lib/ovsdb-data.c | 5 ++--- > lib/ovsdb-idl.c | 6 ++---- > lib/uuid.h | 15 +++++++++++++++ > ovsdb/jsonrpc-server.c | 21 +++++++-------------- > ovsdb/ovsdb-client.c | 3 +-- > ovsdb/raft-private.c | 17 +++++++---------- > ovsdb/raft-rpc.c | 23 ++++++++++------------- > ovsdb/raft.c | 6 +++--- > 14 files changed, 82 insertions(+), 55 deletions(-) > > diff --git a/include/openvswitch/dynamic-string.h > b/include/openvswitch/dynamic-string.h > index 1c262b049..a1d341d48 100644 > --- a/include/openvswitch/dynamic-string.h > +++ b/include/openvswitch/dynamic-string.h > @@ -28,6 +28,8 @@ > extern "C" { > #endif > > +struct uuid; > + > /* A "dynamic string", that is, a buffer that can be used to construct a > * string across a series of operations that extend or modify it. > * > @@ -58,6 +60,7 @@ void ds_put_format(struct ds *, const char *, ...) > OVS_PRINTF_FORMAT(2, 3); > void ds_put_format_valist(struct ds *, const char *, va_list) > OVS_PRINTF_FORMAT(2, 0); > void ds_put_printable(struct ds *, const char *, size_t); > +void ds_put_uuid(struct ds *, const struct uuid *); > void ds_put_hex(struct ds *ds, const void *buf, size_t size); > void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, > uintptr_t ofs, bool ascii); > diff --git a/include/openvswitch/json.h b/include/openvswitch/json.h > index 555440760..097bd057d 100644 > --- a/include/openvswitch/json.h > +++ b/include/openvswitch/json.h > @@ -80,6 +80,7 @@ struct json *json_null_create(void); > struct json *json_boolean_create(bool); > struct json *json_string_create(const char *); > struct json *json_string_create_nocopy(char *); > +struct json *json_string_create_uuid(const struct uuid *); > struct json *json_serialized_object_create(const struct json *); > struct json *json_integer_create(long long int); > struct json *json_real_create(double); > @@ -101,6 +102,8 @@ void json_object_put_string(struct json *, > void json_object_put_format(struct json *, > const char *name, const char *format, ...) > OVS_PRINTF_FORMAT(3, 4); > +void json_object_put_uuid(struct json *, const char *name, > + const struct uuid *); > > const char *json_string(const struct json *); > const char *json_serialized_object(const struct json *); > diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c > index de046a4ed..1f157e46c 100644 > --- a/lib/db-ctl-base.c > +++ b/lib/db-ctl-base.c > @@ -1785,7 +1785,7 @@ cmd_create(struct ctl_context *ctx) > return; > } > } > - ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid)); > + ds_put_uuid(&ctx->output, &row->uuid); > } > > /* This function may be used as the 'postprocess' function for commands that > @@ -1809,7 +1809,7 @@ post_create(struct ctl_context *ctx) > real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy); > if (real) { > ds_clear(&ctx->output); > - ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(real)); > + ds_put_uuid(&ctx->output, real); > } > ds_put_char(&ctx->output, '\n'); > } > @@ -2153,7 +2153,7 @@ cmd_show_row(struct ctl_context *ctx, const struct > ovsdb_idl_row *row, > datum = ovsdb_idl_read(row, show->name_column); > ovsdb_datum_to_string(datum, &show->name_column->type, &ctx->output); > } else { > - ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid)); > + ds_put_uuid(&ctx->output, &row->uuid); > } > ds_put_char(&ctx->output, '\n'); > > diff --git a/lib/dynamic-string.c b/lib/dynamic-string.c > index 8e9555a63..37b513966 100644 > --- a/lib/dynamic-string.c > +++ b/lib/dynamic-string.c > @@ -21,6 +21,7 @@ > #include <string.h> > #include <time.h> > #include "timeval.h" > +#include "uuid.h" > #include "util.h" > > /* Initializes 'ds' as an empty string buffer. */ > @@ -188,6 +189,19 @@ ds_put_printable(struct ds *ds, const char *s, size_t n) > } > } > > +void > +ds_put_uuid(struct ds *ds, const struct uuid *uuid) > +{ > + /* We do not use dp_put_format() here to avoid two calls to > + * vsnprintf(). > + * > + * We CAN use UUID_LEN + 1 in snprintf because ds->string always > + * contains one more byte for '\0' Need to add a dot at the end of the sentence. > + */ > + snprintf(ds_put_uninit(ds, UUID_LEN), UUID_LEN + 1, > + UUID_FMT, UUID_ARGS(uuid)); > +} > + > /* Writes the current time with optional millisecond resolution to 'string' > * based on 'template'. > * The current time is either localtime or UTC based on 'utc'. */ > diff --git a/lib/json.c b/lib/json.c > index 001f6e6ab..2649e8e12 100644 > --- a/lib/json.c > +++ b/lib/json.c > @@ -189,6 +189,11 @@ json_string_create(const char *s) > return json_string_create_nocopy(xstrdup(s)); > } > > +struct json *json_string_create_uuid(const struct uuid *uuid) > +{ > + return json_string_create_nocopy(uuid_to_string(uuid)); > +} > + > struct json * > json_serialized_object_create(const struct json *src) > { > @@ -342,6 +347,12 @@ json_object_put_format(struct json *json, > va_end(args); > } > > +void json_object_put_uuid(struct json *json, > + const char *name, const struct uuid *uuid) > +{ > + json_object_put(json, name, json_string_create_uuid(uuid)); > +} > + > const char * > json_string(const struct json *json) > { > diff --git a/lib/ovsdb-cs.c b/lib/ovsdb-cs.c > index b5eda88ad..87c6211bf 100644 > --- a/lib/ovsdb-cs.c > +++ b/lib/ovsdb-cs.c > @@ -1546,9 +1546,7 @@ ovsdb_cs_send_monitor_request(struct ovsdb_cs *cs, > struct ovsdb_cs_db *db, > json_clone(db->monitor_id), > mrs); > if (version == 3) { > - struct json *json_last_id = json_string_create_nocopy( > - xasprintf(UUID_FMT, UUID_ARGS(&db->last_id))); > - json_array_add(params, json_last_id); > + json_array_add(params, json_string_create_uuid(&db->last_id)); > } > ovsdb_cs_send_request(cs, jsonrpc_create_request(method, params, NULL)); > } > diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c > index aadac6bb2..e4149401f 100644 > --- a/lib/ovsdb-data.c > +++ b/lib/ovsdb-data.c > @@ -483,8 +483,7 @@ ovsdb_atom_to_json__(const union ovsdb_atom *atom, enum > ovsdb_atomic_type type, > : json_deep_clone(atom->s); > > case OVSDB_TYPE_UUID: > - return wrap_json("uuid", json_string_create_nocopy( > - xasprintf(UUID_FMT, UUID_ARGS(&atom->uuid)))); > + return wrap_json("uuid", json_string_create_uuid(&atom->uuid)); > > case OVSDB_N_TYPES: > default: > @@ -753,7 +752,7 @@ ovsdb_atom_to_string(const union ovsdb_atom *atom, enum > ovsdb_atomic_type type, > break; > > case OVSDB_TYPE_UUID: > - ds_put_format(out, UUID_FMT, UUID_ARGS(&atom->uuid)); > + ds_put_uuid(out, &atom->uuid); > break; > > case OVSDB_N_TYPES: > diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c > index 4c2a3e3aa..226a4b475 100644 > --- a/lib/ovsdb-idl.c > +++ b/lib/ovsdb-idl.c > @@ -2838,8 +2838,7 @@ where_uuid_equals(const struct uuid *uuid) > json_string_create("=="), > json_array_create_2( > json_string_create("uuid"), > - json_string_create_nocopy( > - xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); > + json_string_create_uuid(uuid)))); > } > > static const struct ovsdb_idl_row * > @@ -3308,8 +3307,7 @@ ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) > struct json *value; > if (row->persist_uuid) { > uuid_json = "uuid"; > - value = json_string_create_nocopy( > - xasprintf(UUID_FMT, UUID_ARGS(&row->uuid))); > + value = json_string_create_uuid(&row->uuid); > } else { > uuid_json = "uuid-name"; > value = json_string_create_nocopy( > diff --git a/lib/uuid.h b/lib/uuid.h > index fa49354f6..0115241d1 100644 > --- a/lib/uuid.h > +++ b/lib/uuid.h > @@ -16,6 +16,7 @@ > #ifndef UUID_H > #define UUID_H 1 > > +#include "util.h" > #include "openvswitch/uuid.h" > > #ifdef __cplusplus > @@ -73,6 +74,20 @@ uuid_prefix(const struct uuid *uuid, int digits) > return (uuid->parts[0] >> (32 - 4 * digits)); > } > > +/* Returns a string representation of a UUID. > + * > + * The string is allocated on the heap. Ownership of the string is > + * transferred to the caller. > + */ > +static inline char * > +uuid_to_string(const struct uuid *uuid) > +{ > + char *data = xmalloc(UUID_LEN + 1); > + > + snprintf(data, UUID_LEN + 1, UUID_FMT, UUID_ARGS(uuid)); > + return data; > +} > + > void uuid_init(void); > void uuid_generate(struct uuid *); > struct uuid uuid_random(void); > diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c > index 26a53898f..ba5a4211b 100644 > --- a/ovsdb/jsonrpc-server.c > +++ b/ovsdb/jsonrpc-server.c > @@ -1157,8 +1157,7 @@ ovsdb_jsonrpc_session_got_request(struct > ovsdb_jsonrpc_session *s, > const struct uuid *uuid = &s->up.server->uuid; > struct json *result; > > - result = json_string_create_nocopy(xasprintf(UUID_FMT, > - UUID_ARGS(uuid))); > + result = json_string_create_uuid(uuid); > reply = jsonrpc_create_reply(result, request->id); > } else if (!strcmp(request->method, "lock")) { > reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT); > @@ -1609,10 +1608,8 @@ ovsdb_jsonrpc_monitor_create(struct > ovsdb_jsonrpc_session *s, struct ovsdb *db, > json = json ? json : json_object_create(); > > if (m->version == OVSDB_MONITOR_V3) { > - struct json *json_last_id = json_string_create_nocopy( > - xasprintf(UUID_FMT, > - UUID_ARGS(ovsdb_monitor_get_last_txnid( > - m->dbmon)))); > + struct json *json_last_id = json_string_create_uuid( > + ovsdb_monitor_get_last_txnid(m->dbmon)); > > struct json *json_found = json_boolean_create(!initial); > json = json_array_create_3(json_found, json_last_id, json); > @@ -1752,10 +1749,8 @@ ovsdb_jsonrpc_monitor_cond_change(struct > ovsdb_jsonrpc_session *s, > struct jsonrpc_msg *msg; > struct json *p; > if (m->version == OVSDB_MONITOR_V3) { > - struct json *json_last_id = json_string_create_nocopy( > - xasprintf(UUID_FMT, > - UUID_ARGS(ovsdb_monitor_get_last_txnid( > - m->dbmon)))); > + struct json *json_last_id = json_string_create_uuid( > + ovsdb_monitor_get_last_txnid(m->dbmon)); > > p = json_array_create_3(json_clone(m->monitor_id), json_last_id, > update_json); > @@ -1912,10 +1907,8 @@ ovsdb_jsonrpc_monitor_flush_all(struct > ovsdb_jsonrpc_session *s) > struct jsonrpc_msg *msg; > struct json *params; > if (m->version == OVSDB_MONITOR_V3) { > - struct json *json_last_id = json_string_create_nocopy( > - xasprintf(UUID_FMT, > - UUID_ARGS(ovsdb_monitor_get_last_txnid( > - m->dbmon)))); > + struct json *json_last_id = json_string_create_uuid( > + ovsdb_monitor_get_last_txnid(m->dbmon)); > params = json_array_create_3(json_clone(m->monitor_id), > json_last_id, json); > } else { > diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c > index 3fa1d9afc..5258eccaa 100644 > --- a/ovsdb/ovsdb-client.c > +++ b/ovsdb/ovsdb-client.c > @@ -1469,8 +1469,7 @@ do_monitor__(struct jsonrpc *rpc, const char *database, > break; > case OVSDB_MONITOR_V3: > method = "monitor_cond_since"; > - struct json *json_last_id = json_string_create_nocopy( > - xasprintf(UUID_FMT, UUID_ARGS(last_id))); > + struct json *json_last_id = json_string_create_uuid(last_id); > json_array_add(monitor, json_last_id); > break; > case OVSDB_MONITOR_VERSION_MAX: > diff --git a/ovsdb/raft-private.c b/ovsdb/raft-private.c > index e685c8103..16ae6d572 100644 > --- a/ovsdb/raft-private.c > +++ b/ovsdb/raft-private.c > @@ -312,7 +312,7 @@ raft_entry_to_json(const struct raft_entry *e) > if (raft_entry_has_data(e)) { > json_object_put(json, "data", > json_clone(raft_entry_get_serialized_data(e))); > - json_object_put_format(json, "eid", UUID_FMT, UUID_ARGS(&e->eid)); > + json_object_put_uuid(json, "eid", &e->eid); > } > if (e->servers) { > json_object_put(json, "servers", json_clone(e->servers)); > @@ -509,10 +509,9 @@ raft_header_to_json(const struct raft_header *h) > { > struct json *json = json_object_create(); > > - json_object_put_format(json, "server_id", UUID_FMT, UUID_ARGS(&h->sid)); > + json_object_put_uuid(json, "server_id", &h->sid); > if (!uuid_is_zero(&h->cid)) { > - json_object_put_format(json, "cluster_id", > - UUID_FMT, UUID_ARGS(&h->cid)); > + json_object_put_uuid(json, "cluster_id", &h->cid); > } > json_object_put_string(json, "local_address", h->local_address); > json_object_put_string(json, "name", h->name); > @@ -532,8 +531,7 @@ raft_header_to_json(const struct raft_header *h) > json_object_put(json, "prev_data", > json_clone(raft_entry_get_serialized_data(&h->snap))); > } > - json_object_put_format(json, "prev_eid", > - UUID_FMT, UUID_ARGS(&h->snap.eid)); > + json_object_put_uuid(json, "prev_eid", &h->snap.eid); > if (h->snap.election_timer) { > raft_put_uint64(json, "prev_election_timer", > h->snap.election_timer); > @@ -689,8 +687,7 @@ raft_record_to_json(const struct raft_record *r) > raft_put_uint64(json, "election_timer", r->entry.election_timer); > } > if (!uuid_is_zero(&r->entry.eid)) { > - json_object_put_format(json, "eid", > - UUID_FMT, UUID_ARGS(&r->entry.eid)); > + json_object_put_uuid(json, "eid", &r->entry.eid); > } > break; > > @@ -700,7 +697,7 @@ raft_record_to_json(const struct raft_record *r) > > case RAFT_REC_VOTE: > raft_put_uint64(json, "term", r->term); > - json_object_put_format(json, "vote", UUID_FMT, UUID_ARGS(&r->sid)); > + json_object_put_uuid(json, "vote", &r->sid); > break; > > case RAFT_REC_NOTE: > @@ -713,7 +710,7 @@ raft_record_to_json(const struct raft_record *r) > > case RAFT_REC_LEADER: > raft_put_uint64(json, "term", r->term); > - json_object_put_format(json, "leader", UUID_FMT, UUID_ARGS(&r->sid)); > + json_object_put_uuid(json, "leader", &r->sid); > break; > > default: > diff --git a/ovsdb/raft-rpc.c b/ovsdb/raft-rpc.c > index 27c3aad99..846727c0a 100644 > --- a/ovsdb/raft-rpc.c > +++ b/ovsdb/raft-rpc.c > @@ -333,7 +333,7 @@ raft_vote_reply_to_jsonrpc(const struct raft_vote_reply > *rpy, > struct json *args) > { > raft_put_uint64(args, "term", rpy->term); > - json_object_put_format(args, "vote", UUID_FMT, UUID_ARGS(&rpy->vote)); > + json_object_put_uuid(args, "vote", &rpy->vote); > if (rpy->is_prevote) { > json_object_put(args, "is_prevote", json_boolean_create(true)); > } > @@ -476,8 +476,7 @@ raft_remove_server_reply_to_jsonrpc(const struct > raft_remove_server_reply *rpy, > struct json *args) > { > if (!uuid_is_zero(&rpy->target_sid)) { > - json_object_put_format(args, "target_server", > - UUID_FMT, UUID_ARGS(&rpy->target_sid)); > + json_object_put_uuid(args, "target_server", &rpy->target_sid); > } > json_object_put(args, "success", json_boolean_create(rpy->success)); > } > @@ -524,8 +523,7 @@ raft_install_snapshot_request_to_jsonrpc( > raft_put_uint64(args, "last_index", rq->last_index); > raft_put_uint64(args, "last_term", rq->last_term); > json_object_put(args, "last_servers", json_clone(rq->last_servers)); > - json_object_put_format(args, "last_eid", > - UUID_FMT, UUID_ARGS(&rq->last_eid)); > + json_object_put_uuid(args, "last_eid", &rq->last_eid); > raft_put_uint64(args, "election_timer", rq->election_timer); > > json_object_put(args, "data", json_clone(rq->data)); > @@ -636,7 +634,7 @@ static void > raft_remove_server_request_to_jsonrpc( > const struct raft_remove_server_request *rq, struct json *args) > { > - json_object_put_format(args, "server_id", UUID_FMT, UUID_ARGS(&rq->sid)); > + json_object_put_uuid(args, "server_id", &rq->sid); > } > > static void > @@ -708,8 +706,8 @@ raft_execute_command_request_to_jsonrpc( > const struct raft_execute_command_request *rq, struct json *args) > { > json_object_put(args, "data", json_clone(rq->data)); > - json_object_put_format(args, "prereq", UUID_FMT, UUID_ARGS(&rq->prereq)); > - json_object_put_format(args, "result", UUID_FMT, UUID_ARGS(&rq->result)); > + json_object_put_uuid(args, "prereq", &rq->prereq); > + json_object_put_uuid(args, "result", &rq->result); > } > > static void > @@ -751,7 +749,7 @@ static void > raft_execute_command_reply_to_jsonrpc( > const struct raft_execute_command_reply *rpy, struct json *args) > { > - json_object_put_format(args, "result", UUID_FMT, > UUID_ARGS(&rpy->result)); > + json_object_put_uuid(args, "result", &rpy->result); > json_object_put_string(args, "status", > raft_command_status_to_string(rpy->status)); > if (rpy->commit_index) { > @@ -833,13 +831,12 @@ raft_rpc_to_jsonrpc(const struct uuid *cid, > { > struct json *args = json_object_create(); > if (!uuid_is_zero(cid)) { > - json_object_put_format(args, "cluster", UUID_FMT, UUID_ARGS(cid)); > + json_object_put_uuid(args, "cluster", cid); > } > if (!uuid_is_zero(&rpc->common.sid)) { > - json_object_put_format(args, "to", UUID_FMT, > - UUID_ARGS(&rpc->common.sid)); > + json_object_put_uuid(args, "to", &rpc->common.sid); > } > - json_object_put_format(args, "from", UUID_FMT, UUID_ARGS(sid)); > + json_object_put_uuid(args, "from", sid); > if (rpc->common.comment) { > json_object_put_string(args, "comment", rpc->common.comment); > } > diff --git a/ovsdb/raft.c b/ovsdb/raft.c > index 7d78f710e..78ae39e84 100644 > --- a/ovsdb/raft.c > +++ b/ovsdb/raft.c > @@ -527,7 +527,7 @@ raft_create_cluster(const char *file_name, const char > *name, > }; > raft_entry_set_parsed_data(&h.snap, data); > shash_add_nocopy(json_object(h.snap.servers), > - xasprintf(UUID_FMT, UUID_ARGS(&h.sid)), > + uuid_to_string(&h.sid), > json_string_create(local_address)); > error = ovsdb_log_write_and_free(log, raft_header_to_json(&h)); > raft_header_uninit(&h); > @@ -4846,7 +4846,7 @@ raft_unixctl_cid(struct unixctl_conn *conn, > } else if (uuid_is_zero(&raft->cid)) { > unixctl_command_reply_error(conn, "cluster id not yet known"); > } else { > - char *uuid = xasprintf(UUID_FMT, UUID_ARGS(&raft->cid)); > + char *uuid = uuid_to_string(&raft->cid); > unixctl_command_reply(conn, uuid); > free(uuid); > } > @@ -4861,7 +4861,7 @@ raft_unixctl_sid(struct unixctl_conn *conn, > if (!raft) { > unixctl_command_reply_error(conn, "unknown cluster"); > } else { > - char *uuid = xasprintf(UUID_FMT, UUID_ARGS(&raft->sid)); > + char *uuid = uuid_to_string(&raft->sid); > unixctl_command_reply(conn, uuid); > free(uuid); > } > -- > 2.43.0 > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev