You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++生成JSON字符串性能疑问:前置snprintf为何降延迟?求高效方案

问题:重复snprintf语句为何降低延迟?更高效的JSON字符串生成方法?

我需要向仅接受JSON字符串的服务器发送请求,因此需生成仅部分关键字不同的字符串,且对延迟极为敏感。在搭载i9 10900k的机器上,使用编译命令g++ -O3 main.cpp -o main编译测试代码后,若保留第一条长snprintf语句,目标snprintf的延迟低于1us;移除该语句后,延迟升至5us。测试代码如下:

#include <stdio.h>
#include <sys/time.h>

int main() {
  char body[256];
  double qty = 1;
  // the following line make a big difference, 5us if remove it, else < 1us  !!!
  snprintf(body, sizeof(body), "{\"productType\": \"usdt-futures\", \"marginMode\": \"crossed\", \"orderType\": \"limit\", \"marginCoin\": \"USDT\", \"size\":\"%s\", \"price\":\"%s\", \"force\": \"%s\", \"clientOid\": \"%s\", \"side\": \"%s\", \"symbol\": \"%s\"}", "huangxinyu", "0.1", "0.11112", "asdasdas", "", (qty > 0 ? "sskiii" : "sell"));  // !!!!!!!!! this line make a huge difference in latency 
  snprintf(body, sizeof(body), "asdasdasdasd");
  timeval t;
  gettimeofday(&t, NULL);
  int usec = t.tv_usec;
  snprintf(body, sizeof(body), "{\"productType\": \"usdt-futures\", \"marginMode\": \"crossed\", \"orderType\": \"limit\", \"marginCoin\": \"USDT\", \"size\":\"%s\", \"price\":\"%s\", \"force\": \"%s\", \"clientOid\": \"%s\", \"side\": \"%s\", \"symbol\": \"%s\"}", "asd", "0.1111", "0.11112", "ioc", "", (qty > 0 ? "buy" : "sell"));
  gettimeofday(&t, NULL);
  printf("cost %ld\n", t.tv_usec - usec);
}

一、为何添加第一条snprintf会降低延迟?

  • CPU缓存预热:第一条snprintf执行时,会把JSON格式的常量字符串、snprintf函数的代码逻辑、以及body数组所在的内存页加载到CPU的L1/L2高速缓存中。当目标snprintf执行时,这些资源已经在缓存里,无需从主内存甚至磁盘加载,因此延迟大幅降低。如果移除第一条语句,目标snprintf是首次执行相关逻辑,需要冷加载资源,导致延迟升高。
  • 分支预测与编译器优化:-O3优化下,第一条snprintf会让CPU提前学习到(qty > 0 ? ... : ...)这个分支的走向,减少分支预测失败的开销;同时编译器可能会对重复的格式字符串解析做优化,生成更高效的机器码,让后续相同格式的snprintf执行更快。

二、更高效的JSON字符串生成方法

1. 预模板+内存拷贝替换

把JSON的固定部分提前存为模板,可变字段直接通过内存拷贝替换到对应位置,完全跳过格式解析的开销:

#include <string.h>
#include <stdio.h>

int main() {
  char body[256];
  // 预定义模板,可变位置预留足够空间
  const char template[] = "{\"productType\": \"usdt-futures\", \"marginMode\": \"crossed\", \"orderType\": \"limit\", \"marginCoin\": \"USDT\", \"size\":\"\", \"price\":\"\", \"force\":\"\", \"clientOid\":\"\", \"side\":\"\", \"symbol\":\"\"}";
  
  memcpy(body, template, sizeof(template)-1);
  
  // 替换size字段
  char* pos = strstr(body, "\"size\":\"") + 7;
  const char* size_val = "asd";
  memcpy(pos, size_val, strlen(size_val));
  pos[strlen(size_val)] = '"';
  
  // 替换price字段
  pos = strstr(body, "\"price\":\"") + 8;
  const char* price_val = "0.1111";
  memcpy(pos, price_val, strlen(price_val));
  pos[strlen(price_val)] = '"';
  
  // 依次替换其他字段...
}

2. 手动栈上拼接

如果字段长度可提前预估,直接手动拼接字符串,避免格式化函数的额外开销:

#include <stdio.h>

int main() {
  char body[256];
  int offset = 0;
  
  offset += sprintf(body + offset, "{\"productType\": \"usdt-futures\", \"marginMode\": \"crossed\", \"orderType\": \"limit\", \"marginCoin\": \"USDT\", \"size\":\"");
  offset += sprintf(body + offset, "%s", "asd");
  offset += sprintf(body + offset, "\", \"price\":\"");
  offset += sprintf(body + offset, "%s", "0.1111");
  offset += sprintf(body + offset, "\", \"force\":\"");
  offset += sprintf(body + offset, "%s", "0.11112");
  offset += sprintf(body + offset, "\", \"clientOid\":\"");
  offset += sprintf(body + offset, "%s", "ioc");
  offset += sprintf(body + offset, "\", \"side\":\"");
  offset += sprintf(body + offset, "%s", "");
  offset += sprintf(body + offset, "\", \"symbol\":\"");
  offset += sprintf(body + offset, "%s", "buy");
  offset += sprintf(body + offset, "\"}");
}

3. 编译期生成(C++)

如果部分可变字段在编译期已知,可利用C++的constexpr在编译期生成完整JSON字符串,运行时直接使用:

#include <array>
#include <string_view>

constexpr std::array<char, 256> build_json(std::string_view size, std::string_view price) {
  std::array<char, 256> buf{};
  int idx = 0;
  // 手动拼接固定部分和可变部分
  auto append = [&](std::string_view s) {
    for (char c : s) buf[idx++] = c;
  };
  append("{\"productType\": \"usdt-futures\", \"marginMode\": \"crossed\", \"orderType\": \"limit\", \"marginCoin\": \"USDT\", \"size\":\"");
  append(size);
  append("\", \"price\":\"");
  append(price);
  append("\", \"force\":\"0.11112\", \"clientOid\":\"ioc\", \"side\":\"\", \"symbol\":\"buy\"}");
  buf[idx] = '\0';
  return buf;
}

int main() {
  constexpr auto json = build_json("asd", "0.1111");
  // 直接使用json.data()即可
}

4. 预生成常用变体

如果业务中常用的JSON变体有限,提前把所有可能的JSON字符串预先生成好,运行时直接选择对应的字符串,实现零开销。

内容的提问来源于stack exchange,提问作者nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:19:59