利用qt开源库可视化sonic-cpp与rapidjson的性能对比结果

前言

最近学习了字节开源的json库sonic-cpp,号称其性能为rapidjson的2.5倍,于是决定并验证一下,并结合qt的开源库qt-material-widgets与qcustomplot可视化测试结果,主要内容包括:1,sonic-cpp与rapidjson的benchmark测试;2,通过网络执行耗时较长的任务的解决方案;3,利用qt-material-widgets与qcustomplot等开源库生成界面。工程整体与上一篇文章类似(利用c++实现基于rpc的远端服务器参数实时查询 - 掘金 (juejin.cn))。

一、基于benchmark的sonic-cpp与rapidjson的性能对比

sonic-cpp(github.com/bytedance/s... )有c++和go版本的,下载好后,里面包含sonic-cpp、rapidjson、cjson、simdjson、yyjson等多种json库的benchmark(github.com/google/benc... )测试代码,要求c++17及以上,里面用到了std::filesystem,需要添加依赖stdc++fs;sonic-cpp提供了一个set_arch_flags的cmake函数也需要用到,其它正常编译即可。这里只对比sonic-cpp和rapidjson,删除无用代码。sonic-cpp提供的数据集里有一些几十M的的json文件,电脑性比较差的话可能会直接卡死,测试的时候将这些数据集排除掉。测试结果如下,将前3列数据的解析结果通过网络发送到客户端。 从结果上看,所有的测试场景的decode和encode,基本上都是sonic-cpp快,原因可能是进行了向量化(SIMD)优化(www.infoq.cn/article/Exk... )吧,不懂。

二、rpc通信的一些问题

1、通过网络执行耗时较长的任务的解决方案

这里耗时较长是指,客户端发起测试请求,服务端开始benchmark测试,客户端一直等待服务端返回数据。看了一下,sonic-cpp与rapidjson的性能测试共耗时大概60多秒。一般是客户端先采用发起一次请求命令,服务端会返回一个任务id,然后客户端利用任务id间隔一小段时间不停的向服务端查询结果,直至成功或失败。由于服务端需要处理多个客户端请求,因此需要用任务id来区分。按照这种方法,定义pb结构如下:

ini 复制代码
syntax = "proto2";
option cc_generic_services = true;
enum StatusType{
    FAIL = 0;
    PROGESSING = 1;
    FINISH = 2;
}
message BenchmarkJsonInfo {
      required string testName = 1;
      required uint32 testTime = 2;
      required uint32 cpuTime = 3;
};
//生成任务
message BenchmarkJsonTaskRequest {
};
message BenchmarkJsonTaskResponse {
      required int32 taskId = 1;
};
service BenchmarkJsonTaskService {
      rpc BenchmarkJsonTask(BenchmarkJsonTaskRequest) returns (BenchmarkJsonTaskResponse);
};
//查询状态
message QueryBenchmarkStatusRequest {
      required int32 taskId = 1;
};
message QueryBenchmarkStatusResponse {
      required StatusType status = 1;
      required string errInfo = 2;
      repeated BenchmarkJsonInfo benchmarkJsonInfos = 3;
};
service QueryBenchmarkStatusService {
      rpc QueryBenchmarkStatus(QueryBenchmarkStatusRequest) returns (QueryBenchmarkStatusResponse);
};

2、基于brpc的较大数据传输的数据帧拼接问题

这里服务端采用brpc实现,客户端是Windows无法使用brpc,因而采用libevent实现rpc客户端。测试数据大概有3000个字节,发现libevent分两次进入了读回调函数,因此需要拼接数据。按照brpc协议进行如下简单的拼接数据,第一次收到数据时,解析第4~8个字节获得总数据大小(注意&的优先级低于<<,需要加括号),第二次收到数据时利用总字节数进行校验。

arduino 复制代码
TcpClientCallBack::JoinDataResult TcpClientCallBack::joinDataFrame(const char* buf, unsigned int size) {
	//拼接数据帧,最多拼接两次
	std::unique_lock<std::mutex> lk(gMx);
	if (TcpBuffer::currentLen == 0) {
		if (size <= 4) return JOIN_ERROR;
		char pProtocol[5] = { 0 };
		memcpy(pProtocol, buf, 4);
		if (std::string(pProtocol).compare("PRPC")) return JOIN_ERROR;
		int packBodyLen = 0;
		packBodyLen |= (buf[4] & 0xFF) << 8 * 3;
		packBodyLen |= (buf[5] & 0xFF) << 8 * 2;
		packBodyLen |= (buf[6] & 0xFF) << 8 * 1;
		packBodyLen |= (buf[7] & 0xFF) << 8 * 0;
		TcpBuffer::totalLen = packBodyLen + 12;
		memcpy(TcpBuffer::tcpBuf + TcpBuffer::currentLen, buf, size);
		TcpBuffer::currentLen += size;
		return TcpBuffer::currentLen == TcpBuffer::totalLen ? JOIN_FINISH : JOINING;
	}
	else if (TcpBuffer::currentLen + size < MAX_RECEIVE_BUF_LEN) {
		if (TcpBuffer::currentLen + size != TcpBuffer::totalLen) return JOIN_ERROR;
		memcpy(TcpBuffer::tcpBuf + TcpBuffer::currentLen, buf, size);
		TcpBuffer::currentLen += size;
		return JOIN_FINISH;
	}
	return JOIN_ERROR;
}

三、基于qt-material-widgets与qcustomplot生成界面

qt-material-widgets(github.com/laserpants/... )是material风格的控件库,qcustomplot(www.qcustomplot.com )是用于绘制图表的库。

1、qt-material-widgets与qcustomplot编译

qt-material-widgets提供的编译方式是qt5的qmake,这里采用qt6.5的cmake方式编译,需要解决两个升级qt版本的编译错误,一个是setMarigs函数的问题,还有一个是Text枚举的问题。qt6.5下QStateMachine模块分开了需要单独引入。qt6.5编译qcustomplot需要对qcustomplot.h进行如下修改。不要添加Q_MOC_RUN预定义,moc文件正常生成即可,都是编译为静态库。 工程的搭建都是通过手写CMakeLists.txt来实现,没有使用qt creator来构建工程。需要添加qt相关模块的dll,lib,头文件,及一些exe工具moc.exe,rcc.exe,uic.exe,lrelease.exe及lupdate.exe等生成moc文件,转换ui文件,资源文件及翻译文件等。qt-material-widgets编译的CMakeLists.txt如下:

bash 复制代码
sset(TARGET material_components) 
set(CMAKE_CXX_FLAGS "/permissive- /Zc:__cplusplus") 
include(${CMAKE_INCLUDE_PATH}/qt_library.cmake)
include_qt_library()
file(GLOB moc_path "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
execute_qt_moc("components" "components" ${moc_path})
file(GLOB moc_path "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.h")
execute_qt_moc("components/lib" "components/lib" ${moc_path})
file(GLOB rcc_path "${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc")
execute_qt_rcc("components" "components" ${rcc_path})
file(GLOB HEADER 
    "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.h"
    "${CMAKE_CURRENT_SOURCE_DIR}/*.h"
)
source_group("include" FILE ${HEADER})
file(GLOB SOURCE 
    "${CMAKE_CURRENT_SOURCE_DIR}/lib/*.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
)
source_group("source" FILE ${SOURCE})
set(SRC_LIST ${SOURCE} ${HEADER})
add_library(${TARGET} STATIC ${SRC_LIST})
set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME "libmaterialComponents")
target_link_libraries(${TARGET} PRIVATE ${QT_PATH}/lib/*.lib)
scss 复制代码
macro(include_qt_library)
    set(QT_PATH ${PROJECT_SOURCE_DIR}/third_library/qt-6.5.2/msvc2019_64)
    include_directories(${QT_PATH}/include)
    include_directories(${QT_PATH}/include/QtCore)
    include_directories(${QT_PATH}/include/QtGui)
    include_directories(${QT_PATH}/include/QtWidgets)
    include_directories(${QT_PATH}/include/QtStateMachine)
endmacro()
macro(execute_qt_moc origin_dir replace_dir ${moc_path})
    foreach(H_FILE ${moc_path})
        STRING(FIND ${H_FILE} "_p.h" pos)
        if (pos EQUAL -1)
            STRING(FIND ${H_FILE} "ui_" pos)
            if (pos EQUAL -1)
                set(H_FILE_R ${H_FILE})
                string(REPLACE "/${origin_dir}/" "/${replace_dir}/moc_" H_FILE_R ${H_FILE_R})
                string(REPLACE ".h" ".cpp" H_FILE_R ${H_FILE_R})
                execute_process(COMMAND moc ${H_FILE} -o ${H_FILE_R} WORKING_DIRECTORY ${QT_PATH}/bin)
            endif ()
        endif ()
    endforeach()
endmacro()
macro(execute_qt_uic origin_dir replace_dir ${uic_path})
    foreach(UI_FILE ${uic_path})
        set(H_FILE ${UI_FILE})
        string(REPLACE "/${origin_dir}/" "/${replace_dir}/ui_" H_FILE ${H_FILE})
        string(REPLACE ".ui" ".h" H_FILE ${H_FILE})
        execute_process(COMMAND uic ${UI_FILE} -o ${H_FILE} WORKING_DIRECTORY ${QT_PATH}/bin)
    endforeach()
endmacro()
macro(execute_qt_rcc origin_dir replace_dir ${rcc_path})
    foreach(RCC_FILE ${rcc_path})
        set(H_FILE ${RCC_FILE})
        string(REPLACE "/${origin_dir}/" "/${replace_dir}/qrc_" H_FILE ${H_FILE})
        string(REPLACE ".qrc" ".cpp" H_FILE ${H_FILE})
        execute_process(COMMAND rcc ${RCC_FILE} -o ${H_FILE} WORKING_DIRECTORY ${QT_PATH}/bin)
    endforeach()
endmacro()
macro(execute_qt_translate origin_dir replace_dir ${translate_path})
    # file(REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/source/ZH_CN.ts)
    foreach(translate_FILE ${translate_path})
        set(TS_FILE ${translate_FILE})
        string(REPLACE "/${origin_dir}" "/${replace_dir}" TS_FILE ${TS_FILE})
        string(REPLACE ".cpp" ".ts" TS_FILE ${TS_FILE})
        execute_process(COMMAND lupdate ${translate_FILE} -ts ${TS_FILE} WORKING_DIRECTORY ${QT_PATH}/bin)
        set(QM_FILE ${TS_FILE})
        string(REPLACE ".ts" ".qm" QM_FILE ${QM_FILE})
        execute_process(COMMAND lrelease ${TS_FILE} -qm ${QM_FILE} WORKING_DIRECTORY ${QT_PATH}/bin)
    endforeach()
endmacro()

2、效果展示

按钮是采用了qt-material-widgets库的QtMaterialFlatButton,带有动态效果;图表是采用了qcustomplot库的axis-tags例子做了一个动态加载图。

相关推荐
张保瑞9 分钟前
十八:Spring Boot 依赖(3)-- spring-boot-starter-data-jpa 依赖详解
数据库·spring boot·后端
wqyc++26 分钟前
C/C++ 中的预处理器指令
c语言·开发语言·c++
qq_4286396138 分钟前
植物明星大乱斗1
c++·游戏
如泡似影1 小时前
【C++】拆分详解 - 继承
开发语言·c++·经验分享·笔记
边疆.1 小时前
C++类和对象 (下)
c语言·开发语言·c++·算法
single5941 小时前
c++学习:封装继承多态
开发语言·c++·git·vscode·学习
星光樱梦1 小时前
19. 异常处理
c++
c1s2d3n4cs1 小时前
lua脚本调用 c/c++中的接口
开发语言·c++·lua
Ddddddd_1581 小时前
C++ | Leetcode C++题解之第551题学生出勤记录I
c++·leetcode·题解
大叔是90后大叔1 小时前
go生成4位随机数字
开发语言·后端·golang