JsonCpp使用優化

最近一個項目在使用JsonCpp,JsonCpp簡潔易用的接口讓人印象深刻。但是在實際使用過程中,我發現JsonCpp的性能卻不盡如人意,所以想著方法優化下性能。

代碼理解

1、JsonCpp中一切都是Value,Value用union指向自己保存的數據。Value的類型分為兩種,一種是容器類型,比如arrayValue和objectValue。二者都是用map保存數據,隻是arrayValue的key為數字而已。另外一種是基本類型,比如字符串,整型數字等等。 www.aiwalls.com

2、解釋JSON數據時,JsonCpp在operator[]函數開銷比較大。JsonCpp內部使用std::map,map在查找性能上不如hash_map,但是將map替換成hash_map有一定的困難,因為map的key為CZString,而這個類又是Value的內部類,導致不能定義hash_map需要的hash結構體。

本來想嘗試下internal map,結果開啟JSON_VALUE_USE_INTERNAL_MAP這個宏之後,根本通不過編譯,因為value.h中有一處uion聲明裡面居然放的是結構體,不知道什麼編譯器支持這種語法。

基準測試程序

#include <iostream>
#include <string>
#include <sys/time.h>
#include <time.h>
#include <json/json.h>

using namespace std;

int64_t getCurrentTime()
{
    struct timeval tval;
    gettimeofday(&tval, NULL);
    return (tval.tv_sec * 1000000LL + tval.tv_usec);
}

char * str = "abcdefghijklmnopqrstuvwxyz";

void test1()
{
    int doc_count = 40;
    int outer_field_count = 80;

    Json::Value common_info;

    int64_t start_time = getCurrentTime();

    for(size_t i=0; i<doc_count; ++i)
    {
        Json::Value auc_info;
        for( size_t j=0 ; j<outer_field_count; ++j )
        {
            auc_info.append(str);
        }
        common_info.append(auc_info);
    }

    int64_t end_time = getCurrentTime();

    cout << "append time: " << end_time – start_time << endl;
}

void test2()
{
    int doc_count = 40;
    int outer_field_count = 80;

    Json::Value common_info;

    int64_t start_time = getCurrentTime();

    Json::Value auc_info;

    for(size_t i=0; i<doc_count; ++i)
    {
        for( size_t j=0 ; j<outer_field_count; ++j )
        {
            auc_info[j] = str;
        }
        common_info.append(auc_info);
    }

    int64_t end_time = getCurrentTime();

    cout << "opt append time: " << end_time – start_time << endl;
}

void test3()
{
    int doc_count = 40;
    int outer_field_count = 80;

    Json::Value common_info;

    int64_t start_time = getCurrentTime();

    Json::Value auc_info;

    for(size_t i=0; i<doc_count; ++i)
    {
        for( size_t j=0 ; j<outer_field_count; ++j )
        {
            auc_info[j] = Json::StaticString(str);
        }
        common_info.append(auc_info);
    }

    int64_t end_time = getCurrentTime();

    cout << "StaticString time: " << end_time – start_time << endl;
}

int main(int argc, const char *argv[])
{
    test1();
    test2();
    test3();

    return 0;
}
編譯優化

默認情況下,JsonCpp編譯時並沒有帶優化參數,自己可以加上優化參數。Linux環境下在下面這段代碼中的CCFLAGS加入”O2″。

elif platform.startswith('linux-gcc'):
    env.Tool( 'default' )
    env.Append( LIBS = ['pthread'], CCFLAGS = "-Wall -fPIC O2" )
    env['SHARED_LIB_ENABLED'] = True
可以看到使用O2優化比默認編譯的版本性能提升一倍多。

append time: 4946
opt append time: 3326
StaticString time: 2750

append time: 1825
opt append time: 1082
StaticString time: 845
使用方法上的優化

測試代碼中第三種方法比第一種方法效率提升瞭一倍多。第三種方法之所以效率更高,有兩個原因。

1、首先是在循環中一直復用auc_info對象。第一個循環就能將auc_info的長度初始化為doc_count。通過下標的訪問方法,一直復用數組中的元素。

2、如果key和value內存不會被釋放,那麼使用StaticString效率會更高,省去瞭構造CZString時拷貝的開銷。

代碼優化

因為在JsonCpp中一切都是Value,所以會有大量的隱性類型轉換,要構造大量的Value對象。為瞭提高性能,可以在實現繞過這個機制,犧牲一致性。

因為Value最常用的類型是字符串,因此給Value增加一個setValue函數。

void
Value::setValue( const StaticString& value )
{
   type_ = stringValue;
   allocated_ = false;
   value_.string_ = const_cast<char *>( value.c_str() );
}
再測試一下性能,可以發現性能較第三種方法還有提升。

append time: 1849
opt append time: 1067
StaticString time: 843
setValue time: 570
最後還有一個辦法就是靜態鏈接。JsonCpp庫本身非常小,將其靜態鏈接能稍微提升一點性能。下面是靜態鏈接時基準測試程序的耗時情況。

append time: 1682
opt append time: 1011
StaticString time: 801
setValue time: 541

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *