利用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例子做了一个动态加载图。

相关推荐
Rust研习社8 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒9 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro9 小时前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax10 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH10 小时前
Koa和Express的区别
后端
MariaH10 小时前
Koa框架的使用
后端
luckdewei11 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某12 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy13 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom13 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github