[PATCH grout] api: optimize send small payloads
Morten Brørup
mb at smartsharesystems.com
Sat Jun 13 11:52:23 CEST 2026
Copy small payloads into buffer holding both header and payload,
and send as one, instead of sending header and payload separately.
Signed-off-by: Morten Brørup <mb at smartsharesystems.com>
---
api/gr_api_client_impl.h | 29 ++++++++++++++++-----
main/api.c | 55 ++++++++++++++++++++++++++++++----------
2 files changed, 63 insertions(+), 21 deletions(-)
diff --git a/api/gr_api_client_impl.h b/api/gr_api_client_impl.h
index 242b286d..3af31e9b 100644
--- a/api/gr_api_client_impl.h
+++ b/api/gr_api_client_impl.h
@@ -198,6 +198,10 @@ static ssize_t recv_all(const struct gr_api_client *c, void *buf, size_t len) {
return len;
}
+// Optimization: Threshold for appending small payload to request buffer,
+// and send in one go, instead of sending request header and payload separately.
+#define GR_API_REQUEST_MERGE_PAYLOAD (4096 - sizeof(struct gr_api_request))
+
long int gr_api_client_send(
struct gr_api_client *client,
uint32_t req_type,
@@ -209,19 +213,30 @@ long int gr_api_client_send(
if (client == NULL || (tx_len == 0 && tx_data != NULL) || (tx_len > 0 && tx_data == NULL))
return errno_set(EINVAL);
- struct gr_api_request req = {
+ struct merged_req {
+ struct gr_api_request req;
+ char payload[GR_API_REQUEST_MERGE_PAYLOAD];
+ } mreq;
+
+ mreq.req = (struct gr_api_request){
.id = ++message_id,
.payload_len = tx_len,
.type = req_type,
};
- if (send_all(client, &req, sizeof(req)) < 0)
- return -errno;
-
- if (tx_len > 0 && send_all(client, tx_data, tx_len) < 0)
- return -errno;
+ if (tx_len <= GR_API_REQUEST_MERGE_PAYLOAD) {
+ if (tx_len > 0)
+ memcpy(mreq.payload, tx_data, tx_len);
+ if (send_all(client, &mreq, sizeof(mreq.req) + tx_len) < 0)
+ return -errno;
+ } else {
+ if (send_all(client, &mreq, sizeof(mreq.req)) < 0)
+ return -errno;
+ if (send_all(client, tx_data, tx_len) < 0)
+ return -errno;
+ }
- return req.id;
+ return mreq.req.id;
}
int gr_api_client_recv(
diff --git a/main/api.c b/main/api.c
index 023c9002..8afcf4b6 100644
--- a/main/api.c
+++ b/main/api.c
@@ -231,22 +231,38 @@ static void disconnect_client(struct api_ctx *ctx) {
free(ctx);
}
+// Optimization: Threshold for appending small payload to response buffer,
+// and send in one go, instead of sending response header and payload separately.
+#define API_RESPONSE_MERGE_PAYLOAD (256 - sizeof(struct gr_api_response))
+
void api_send(struct api_ctx *ctx, uint32_t len, const void *payload) {
assert(ctx != NULL);
- assert(len != 0);
- assert(payload != NULL);
+ assert(payload != NULL || len == 0);
LOG(DEBUG, "pid=%d for_id=%u len=%u", ctx->pid, ctx->header.id, len);
- struct gr_api_response resp = {
+ struct merged_resp {
+ struct gr_api_response resp;
+ char payload[API_RESPONSE_MERGE_PAYLOAD];
+ } mresp;
+
+ mresp.resp = (struct gr_api_response){
.for_id = ctx->header.id,
.payload_len = len,
.status = 0,
};
- if (bufferevent_write(ctx->bev, &resp, sizeof(resp)) < 0)
- LOG(ERR, "pid=%d cannot write header", ctx->pid);
- if (bufferevent_write(ctx->bev, payload, len) < 0)
- LOG(ERR, "pid=%d cannot write payload", ctx->pid);
+
+ if (len <= API_RESPONSE_MERGE_PAYLOAD) {
+ if (len > 0)
+ memcpy(mresp.payload, payload, len);
+ if (bufferevent_write(ctx->bev, &mresp, sizeof(mresp.resp) + len) < 0)
+ LOG(ERR, "pid=%d cannot write header and payload", ctx->pid);
+ } else {
+ if (bufferevent_write(ctx->bev, &mresp, sizeof(mresp.resp)) < 0)
+ LOG(ERR, "pid=%d cannot write header", ctx->pid);
+ else if (bufferevent_write(ctx->bev, payload, len) < 0)
+ LOG(ERR, "pid=%d cannot write payload", ctx->pid);
+ }
}
static void read_cb(struct bufferevent *bev, void *priv) {
@@ -327,18 +343,29 @@ send:
strerror(out.status),
out.len);
- struct gr_api_response resp = {
+ assert(out.payload != NULL || out.len == 0);
+
+ struct merged_resp {
+ struct gr_api_response resp;
+ char payload[API_RESPONSE_MERGE_PAYLOAD];
+ } mresp;
+
+ mresp.resp = (struct gr_api_response){
.for_id = ctx->header.id,
.status = out.status,
.payload_len = out.len,
};
- if (bufferevent_write(bev, &resp, sizeof(resp)) < 0)
- LOG(ERR, "failed to write header");
- if (out.len > 0) {
- assert(out.payload != NULL);
- if (bufferevent_write(bev, out.payload, out.len) < 0)
- LOG(ERR, "failed to write payload");
+ if (out.len <= API_RESPONSE_MERGE_PAYLOAD) {
+ if (out.len > 0)
+ memcpy(mresp.payload, out.payload, out.len);
+ if (bufferevent_write(bev, &mresp, sizeof(mresp.resp) + out.len) < 0)
+ LOG(ERR, "pid=%d cannot write header and payload", ctx->pid);
+ } else {
+ if (bufferevent_write(bev, &mresp, sizeof(mresp.resp)) < 0)
+ LOG(ERR, "pid=%d cannot write header", ctx->pid);
+ else if (bufferevent_write(bev, out.payload, out.len) < 0)
+ LOG(ERR, "pid=%d cannot write payload", ctx->pid);
}
bufferevent_flush(bev, EV_WRITE, BEV_FLUSH);
--
2.43.0
More information about the grout
mailing list