current position:Home>Jsoncpp performs efficiency optimization and unconventional limit optimization. It is suitable for Linux, and the efficiency is almost improved by more than 50%

Jsoncpp performs efficiency optimization and unconventional limit optimization. It is suitable for Linux, and the efficiency is almost improved by more than 50%

2022-01-27 01:01:17 zhenmu

This is a 2 Years ago , A game server project is going online , Protocol message processing and data storage are basically used json Of , It's using jsoncpp Open source library


The main logical processing part is single thread processing , So there are more players cpu I can't bear it , To optimize ,


use gprof Wait for tools to find and find. The research found that The main json Some of the memory overhead caused by this part cpu There are too many resources .( And some smart pointers , Press the table )


Found a lot of ways to optimize jsoncpp,

1. such as  http://www.2cto.com/kf/201211/172375.html

Only some... Have been optimized .


2. in addition , Json::Value The object has a swap Interface , be-all Assignment operation can change swap All of them are used swap( Because direct = assignment , Will make an object copy )

3. then Data nested Basically not append , Use both  Json::Value &one = jv_test[jv_test.size()];   Take it out first and then assign it , This saves append A copy of when

4.StyledWriter Try to become  FastWriter Formatting

But changed a lot of code , Just a little more efficiency

5. Continue to modify jsoncpp Source code Remove the comment processing code , It seems to be of little use .


Then I took a closer look jsoncpp Code , Find out especially writer Inside , There's a string document_ Always again += , String concatenation ,

The original code It's no use using a unified writer format , A lot of them use toStyledString()



std::string Value::toStyledString() const {
  //StyledWriter writer;
  FastWriter writer;
  return writer.write(*this);
}



As one can imagine This document_ This string container is in How many times does the concatenation string need to allocate memory , It's inconceivable .

If you change the code , Too much

Just change the bottom

One 、writer.h in Note out

 //std::string document_;

Two 、json_writer.h Change the code in   

Use one Thread level Global static variables Replace 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>
// The release issue will not be considered for the time being , If the thread keeps creating and using , For automatic release, please refer to 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;
  }


The overall efficiency has been improved 50% - -!

<strong>__thread </strong>
Is to ensure that there is no problem under multithreading .


reader You can also optimize .


This optimization can be tried if necessary .   Personally, I think the overall situation Use one writer It's also good to output ( However, pay attention to the problem of multithreading ).



copyright notice
author[zhenmu],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270101135369.html

Random recommended