这是2年多前, 一个游戏服务器项目要上线了,协议消息处理和数据存放都基本用json的,用的是jsoncpp开源库
主要逻辑处理部分是单线程处理,所以玩家一多cpu就吃不消了, 要优化,
用gprof等工具找啊找研究发现是 主要json部分引起的一些内存开销占用cpu资源过多。(还有一些智能指针,按下不表)
找了很多方法优化jsoncpp,
1.比如 http://www.2cto.com/kf/201211/172375.html
只优化了一些。
2.另外, Json::Value对象有个 swap接口,所有的 赋值操作能改 swap的都用swap(因为直接 = 赋值,会做一次对象拷贝)
3. 然后 数据嵌套的 基本不用append , 都用 Json::Value &one = jv_test[jv_test.size()]; 先取出来再赋值,这样就省了 append时的一次拷贝
4.StyledWriter尽量都变成 FastWriter的格式化
但改了好多代码,只是稍稍提高了点效率
5. 继续修改jsoncpp源码 把注释的处理代码去掉,好像用处也不大。
后来仔细看了一下jsoncpp代码, 发现 特别是writer里面, 有个字符串document_ 一直再 += , 拼接字符串,
原先代码 没用用一个统一的writer格式化, 很多都是用toStyledString()
std::string Value::toStyledString() const {
//StyledWriter writer;
FastWriter writer;
return writer.write(*this);
}
可想而知 这个document_ 这个字符串容器在 拼接字符串要分配多少次内存哈,不可想象。
如果改代码,量太大
就直接改底层的
一、writer.h里 注掉
//std::string document_;
二、json_writer.h里改一下代码
使用一个 线程级的 全局静态变量 替换 document_;
std::string valueToQuotedString(const char *value, <strong>std::string* document</strong> /* = NULL*/) {
if (value == NULL)
return "";
// Not sure how to handle unicode...
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
!containsControlCharacter(value))
{<strong>
if(document != NULL)
{
std::string tmp = std::string("\"") + value + "\"";
*document += tmp;
return "";
}
else
{
return std::string("\"") + value + "\"";
}</strong>
}
// We have to walk value and escape any special characters.
// Appending to std::string is not efficient, but this should be rare.
// (Note: forward slashes are *not* rare, but I am not escaping them.)
std::string::size_type maxsize =
strlen(value) * 2 + 3; // allescaped+quotes+NULL
std::string new_result;
std::string* result = &new_result;
if(document != NULL)
{
result = document;
}
else
{
(*result).reserve(maxsize); // to avoid lots of mallocs
}
(*result) += "\"";
for (const char *c = value; *c != 0; ++c) {
switch (*c) {
case '\"':
(*result) += "\\\"";
break;
case '\\':
(*result) += "\\\\";
break;
case '\b':
(*result) += "\\b";
break;
case '\f':
(*result) += "\\f";
break;
case '\n':
(*result) += "\\n";
break;
case '\r':
(*result) += "\\r";
break;
case '\t':
(*result) += "\\t";
break;
// case '/':
// Even though \/ is considered a legal escape in JSON, a bare
// slash is also legal, so I see no reason to escape it.
// (I hope I am not misunderstanding something.
// blep notes: actually escaping \/ may be useful in javascript to avoid </
// sequence.
// Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring.
default:
if (isControlCharacter(*c)) {
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << static_cast<int>(*c);
(*result) += oss.str();
} else {
(*result) += *c;
}
break;
}
}
(*result) += "\"";
return new_result;
}
<strong>
//暂不考虑释放问题,如果线程不停创建使用,自动释放请参考http://www.searchtb.com/2012/09/tls.html
static __thread std::string* jw_document_ = NULL;
#define document_ (*jw_document_)</strong>
// Class Writer
// //
Writer::~Writer() {}
// Class FastWriter
// //
FastWriter::FastWriter()
: yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false) {
<strong> if(NULL == jw_document_)
{
jw_document_ = new std::string();
//printf("###FastWriter::FastWriter() new string()\n");
}</strong>
}
void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
std::string FastWriter::write(const Value &root) {
<strong> document_.clear();</strong>
writeValue(root);
//document_ += "\n";
return document_;
}
void FastWriter::writeValue(const Value &value) {
switch (value.type()) {
case nullValue:
if (!dropNullPlaceholders_)
document_ += "null";
break;
case intValue:
document_ += valueToString(value.asLargestInt());
break;
case uintValue:
document_ += valueToString(value.asLargestUInt());
break;
case realValue:
document_ += valueToString(value.asDouble());
break;
case stringValue:<strong>
//document_ += valueToQuotedString(value.asCString());
valueToQuotedString(value.asCString(),&document_);</strong>
break;
case booleanValue:
document_ += valueToString(value.asBool());
break;
case arrayValue: {
document_ += "[";
int size = value.size();
for (int index = 0; index < size; ++index) {
if (index > 0)
document_ += ",";
writeValue(value[index]);
}
document_ += "]";
} break;
case objectValue: {
document_ += "{";
Value::ObjectValues* value_map = value.getValueMap();
if(value_map != NULL)
{
Value::ObjectValues::iterator it = value_map->begin();
for ( Value::ObjectValues::iterator it = value_map->begin();
it != value_map->end();
++it )
{
if ( it != value_map->begin() )
document_ += ",";
const char* name = it->first.c_str();
valueToQuotedString( name, &document_ );
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
writeValue( it->second );
}
}
/*
Value::Members members(value.getMemberNames());
document_ += "{";
for (Value::Members::iterator it = members.begin(); it != members.end();
++it) {
const std::string &name = *it;
if (it != members.begin())
document_ += ",";
document_ += valueToQuotedString(name.c_str());
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
writeValue(value[name]);
}
*/
document_ += "}";
} break;
}
整体效率一下提高了50% - -!
<strong>__thread </strong>
是保证多线程下没有问题。
reader也可以优化看看。
这个优化如果有需要可以拿去试试。 个人觉得全局 统一用一个writer来输出也不错(不过要注意多线程的问题)。