优化cJSON_AddItemToArray函数,提高插入速率

最近在我的写了一个API程序,要求返回响应的数据是JSON,格式类似下面这种:

[{
  "columns":[
    {"text":"Time","type":"time"},
    {"text":"Country","type":"string"},
    {"text":"Number","type":"number"}
  ],
  "rows":[
    [1234567,"SE",123],
    [1234567,"DE",231],
    [1234567,"US",321]
  ],
  "type":"table"
}]

API是用C语言开发的,所以在构建json上,为了方便,使用了CJson库。代码如下:

static char *grafana_build_reponse_query_history(MYSQL *mysql, const char *request_body, grafana_query_request_s *rst, snetflow_job_s *job)
{
    cJSON *root, *response_json, *columns, *column, *rows, *row, *json_flow, *json_bytes;
...
	/* 根据target拼接json */
	response_json = cJSON_CreateArray();
	root = cJSON_CreateObject();
	cJSON_AddItemToArray(response_json, root);
	rows = cJSON_AddArrayToObject(root, "rows");
	columns = cJSON_AddArrayToObject(root, "columns");
	cJSON_AddStringToObject(root, "type", "table");
	/* rows */
	for(i = 0, it = history_vec.begin(); it != history_vec.end(); it++)  
	{
		json_flow = cJSON_CreateString((*it).flow);
		json_bytes = cJSON_CreateNumber((*it).bytes);
		row = cJSON_CreateArray();
	    cJSON_AddItemToArray(row, json_flow);
	    cJSON_AddItemToArray(row, json_bytes);
		/* 优化cJSON_AddItemToArray(rows, row)提高速度*/ 
		cJSON_AddItemToArray(rows, row);
	}
	/* columns */
	column = cJSON_CreateObject();
	cJSON_AddStringToObject(column, "text", cfg->name);
    cJSON_AddStringToObject(column, "type", "string");
	cJSON_AddItemToArray(columns, column);
    column = cJSON_CreateObject();
	cJSON_AddStringToObject(column, "text", "bytes");
    cJSON_AddStringToObject(column, "type", "number");
	cJSON_AddItemToArray(columns, column);
	/* 释放空间 */
	out = cJSON_Print(response_json);
	cJSON_Delete(response_json);

	return out;
}

对于row,思路就是遍历一个map,然后组装一个数组,然后添加到大数组中。当map容量在几千的时候,还可有跑出结果,但是当map容量大到几十万的时候,跑了一个多小时还没有结果,而且几乎耗尽了CPU。我用gdb调试了一下,发现大部分时间是浪费在cJSON_AddItemToArray这个函数

优化cJSON_AddItemToArray函数

我们可以看一下cJSON结构体的定义:

typedef struct cJSON
{
    struct cJSON *next;
    struct cJSON *prev;
    struct cJSON *child;
    int type;
    char *valuestring;
    int valueint;
    double valuedouble;
    char *string;
} cJSON;

这里面有三个指针。以这个json格式举例:

[{
  "columns":[
    {"text":"Time","type":"time"},
    {"text":"Country","type":"string"},
    {"text":"Number","type":"number"}
  ],
  "rows":[
    [1234567,"SE",123],
    [1234567,"DE",231],
    [1234567,"US",321]
  ],
  "type":"table"
}]

其中columns、rows、type称作同级,{“text”:“Time”,“type”:“time”},是columns的一个子级,[1234567,“DE”,231],是rows的一个子级。

在这个结构体中,next指向同级的下一个,prev指向同级的上一个,也就是说,同级之前构成了一个双向链表。child指向子级的第一个节点。

那么问题来了,cJSON_AddItemToArray内部是如何实现的呢?很显然,无非就是修改这几个指针的指向,虽然这是一个双向链表,但是却没有记录头和尾。所以,每次调用cJSON_AddItemToArray的时候,只能从头部向后遍历,使用尾插法,时间复杂度是n^2。当数据量很大的时候,有多么耗时就可想而知了。
(如果用头插法,会导致json串同级数据的顺序颠倒!所以,cJSON_AddItemToArray肯定采用的尾插法)

既然是我们已经了解了cJSON_AddItemToArray实现思路,所以我们就可以使用单链表的尾插法,来自己对这个函数进行改进,把时间复杂度降到n

可以参考一下如下代码

	for(i = 0, it = history_vec.begin(); it != history_vec.end(); it++)  
	{
		json_flow = cJSON_CreateString((*it).flow);
		json_bytes = cJSON_CreateNumber((*it).bytes);
		row = cJSON_CreateArray();
	    cJSON_AddItemToArray(row, json_flow);
	    cJSON_AddItemToArray(row, json_bytes);
		/* 优化cJSON_AddItemToArray(rows, row)提高速度*/ 
		if(i == 0)
		{
			i = 1;
			cJSON_AddItemToArray(rows, row);
			prev = row;
		}
		else
		{
			row->prev = prev;
			prev->next = row;
			prev = row;
		}
	}

同理,其他的一些Add函数,如cJSON_AddItemToObject、cJSON_AddItemReferenceToObject等也可以使用这个思路优化。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值