当前位置: 首页 > news >正文

使用 C++ 进行高效序列化和反序列化的实现(优化版本)

文章目录

    • 0. 引言
    • 1. 设计思路
    • 2. 代码实现
      • 2.1 `serialize.hpp`头文件
      • 2.2 代码解析
    • 3. 测试程序
    • 4. 测试结果
    • 5. 优缺点分析
      • 5.1 优点
      • 5.2 缺点

0. 引言

之前的文章 二进制序列化与反序列化:支持C++基础类与STL容器已介绍了serialize.hpp,本文将给一个支持C++14以上版本的优化版本。

1. 设计思路

使用模板和 SFINAE(Substitution Failure Is Not An Error)机制,以支持多种数据类型。通过重载序列化和反序列化函数,处理不同的数据类型。

2. 代码实现

2.1 serialize.hpp头文件

#ifndef SERIALIZE_HPP
#define SERIALIZE_HPP#include <algorithm>
#include <cstdint>
#include <iterator>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>namespace Serialization {// 用于判断是否可以平凡复制
template <typename T>
constexpr bool IsTriviallyCopyable = std::is_trivially_copyable<T>::value;// 序列化
template <typename T>
void serialize(std::ostream &os, const T &val, typename std::enable_if<IsTriviallyCopyable<T>, int>::type = 0) {os.write(reinterpret_cast<const char *>(&val), sizeof(T));
}// 处理 std::pair
template <typename K, typename V>
void serialize(std::ostream &os, const std::pair<K, V> &val) {serialize(os, val.first);serialize(os, val.second);
}// 处理 std::string
void serialize(std::ostream &os, const std::string &val) {const std::size_t size = val.size();os.write(reinterpret_cast<const char *>(&size), sizeof(size));os.write(val.data(), size);  // 注意这里的数据类型匹配
}// 序列化容器
template <typename Container>
void serialize(std::ostream &os, const Container &container,typename std::enable_if<std::is_same<typename std::iterator_traits<typename Container::iterator>::value_type,typename Container::value_type>::value,int>::type = 0) {const std::size_t size = container.size();os.write(reinterpret_cast<const char *>(&size), sizeof(size));for (const auto &item : container) {serialize(os, item);}
}// 序列化元组
template <typename Tuple, std::size_t... Indices>
void serializeTuple(std::ostream &os, const Tuple &tup, std::index_sequence<Indices...>) {// 使用循环替代折叠表达式int dummy[] = {(serialize(os, std::get<Indices>(tup)), 0)...};  // 创建一个无用数组来展开索引static_cast<void>(dummy);                                       // 消除未使用变量的警告
}template <typename... Args>
void serialize(std::ostream &os, const std::tuple<Args...> &val) {serializeTuple(os, val, std::index_sequence_for<Args...>{});
}// 反序列化
template <typename T>
void deserialize(std::istream &is, T &val, typename std::enable_if<IsTriviallyCopyable<T>, int>::type = 0) {is.read(reinterpret_cast<char *>(&val), sizeof(T));
}// 处理 std::string
void deserialize(std::istream &is, std::string &val) {std::size_t size = 0;is.read(reinterpret_cast<char *>(&size), sizeof(size));val.resize(size);is.read(&val[0], size);  // 使用 &val[0] 以获得 char* 类型
}// 反序列化容器
template <typename Container>
void deserialize(std::istream &is, Container &container,typename std::enable_if<std::is_same<typename std::iterator_traits<typename Container::iterator>::value_type,typename Container::value_type>::value,int>::type = 0) {std::size_t size = 0;is.read(reinterpret_cast<char *>(&size), sizeof(size));container.clear();container.reserve(size);for (std::size_t i = 0; i < size; ++i) {typename Container::value_type item;deserialize(is, item);container.emplace_back(std::move(item));}
}// 反序列化元组
template <typename Tuple, std::size_t... Indices>
void DeserializeTuple(std::istream &is, Tuple &tup, std::index_sequence<Indices...>) {// 使用循环替代折叠表达式int dummy[] = {(deserialize(is, std::get<Indices>(tup)), 0)...};  // 创建一个无用数组来展开索引static_cast<void>(dummy);                                         // 消除未使用变量的警告
}template <typename... Args>
void deserialize(std::istream &is, std::tuple<Args...> &val) {DeserializeTuple(is, val, std::index_sequence_for<Args...>{});
}}  // namespace Serialization#endif  // SERIALIZE_HPP

2.2 代码解析

  1. 平凡复制检测:使用 std::is_trivially_copyable 检查类型是否可以平凡复制,以决定是否使用简单的内存拷贝。

  2. 序列化函数:重载多个 serialize 函数以支持基本类型、std::string、容器(如 std::vectorstd::map)及 std::tuple。元组的序列化使用了索引序列来展开参数。

  3. 反序列化函数:类似于序列化,定义了多个 deserialize 函数以支持不同的数据类型和容器。

3. 测试程序

#include <chrono>
#include <iostream>
#include <list>
#include <map>
#include <sstream>
#include <tuple>
#include <unordered_map>
#include <vector>#include "serialize.hpp"struct Rect_t {uint32_t x = 0;uint32_t y = 0;uint32_t w = 0;uint32_t h = 0;bool operator==(const Rect_t &other) const {return x == other.x && y == other.y && w == other.w && h == other.h;}
};struct BBox_t {std::vector<Rect_t> rects;uint32_t index = 0;bool operator==(const BBox_t &other) const {return rects == other.rects && index == other.index;}
};// 测试函数
void runPerformanceTest(size_t numBoxes, size_t numRectsPerBox) {std::vector<BBox_t> bboxs(numBoxes);// 初始化数据for (size_t j = 0; j < numBoxes; ++j) {BBox_t &bbox = bboxs[j];bbox.rects.reserve(numRectsPerBox);for (size_t i = 0; i < numRectsPerBox; ++i) {Rect_t rect;rect.x = static_cast<uint32_t>(i + 100);rect.y = static_cast<uint32_t>(i + 200);rect.w = static_cast<uint32_t>(i + 300);rect.h = static_cast<uint32_t>(i + 400);bbox.rects.push_back(rect);}bbox.index = static_cast<uint32_t>(j);}// 序列化性能测试std::ostringstream ss;auto startSerialize = std::chrono::high_resolution_clock::now();for (const auto &bbox : bboxs) {Serialization::serialize(ss, bbox.rects);Serialization::serialize(ss, bbox.index);}auto endSerialize = std::chrono::high_resolution_clock::now();std::chrono::duration<double, std::milli> serializeDuration = endSerialize - startSerialize;std::cout << "Serialization Time: " << serializeDuration.count() << " ms" << std::endl;// 反序列化性能测试std::istringstream is(ss.str());auto startDeserialize = std::chrono::high_resolution_clock::now();std::vector<BBox_t> deserializedBboxes;while (!is.eof()) {BBox_t bbox;Serialization::deserialize(is, bbox.rects);Serialization::deserialize(is, bbox.index);deserializedBboxes.push_back(bbox);}auto endDeserialize = std::chrono::high_resolution_clock::now();std::chrono::duration<double, std::milli> deserializeDuration = endDeserialize - startDeserialize;std::cout << "Deserialization Time: " << deserializeDuration.count() << " ms" << std::endl;// 检查序列化和反序列化结果的一致性bool consistent = true;for (size_t i = 0; i < bboxs.size(); ++i) {if (!(bboxs[i] == deserializedBboxes[i])) {consistent = false;break;}}if (consistent) {std::cout << "Serialization and deserialization were successful and consistent!" << std::endl;} else {std::cout << "Mismatch between original and deserialized data!" << std::endl;}// 测试 std::tupleusing TupleType = std::tuple<int, float, std::string>;std::vector<TupleType> tuples = {{1, 1.1f, "one"},{2, 2.2f, "two"},{3, 3.3f, "three"},};// 序列化 std::tuplestd::ostringstream tupleStream;for (const auto &tuple : tuples) {Serialization::serialize(tupleStream, tuple);}// 反序列化 std::tuplestd::istringstream tupleIs(tupleStream.str());std::vector<TupleType> deserializedTuples;while (!tupleIs.eof()) {TupleType tuple;Serialization::deserialize(tupleIs, tuple);deserializedTuples.push_back(tuple);}// 检查 std::tuple 的一致性consistent = true;for (size_t i = 0; i < tuples.size(); ++i) {if (tuples[i] != deserializedTuples[i]) {consistent = false;break;}}if (consistent) {std::cout << "Tuple serialization and deserialization were successful and consistent!" << std::endl;} else {std::cout << "Mismatch between original and deserialized tuples!" << std::endl;}// 测试其他容器类型std::map<int, Rect_t> rectMap;for (size_t i = 0; i < numRectsPerBox; ++i) {rectMap[i] = {static_cast<uint32_t>(i + 100), static_cast<uint32_t>(i + 200), static_cast<uint32_t>(i + 300),static_cast<uint32_t>(i + 400)};}std::ostringstream mapStream;Serialization::serialize(mapStream, rectMap);std::unordered_map<int, Rect_t> rectUnorderedMap;for (size_t i = 0; i < numRectsPerBox; ++i) {rectUnorderedMap[i] = {static_cast<uint32_t>(i + 100), static_cast<uint32_t>(i + 200),static_cast<uint32_t>(i + 300), static_cast<uint32_t>(i + 400)};}std::ostringstream unorderedMapStream;Serialization::serialize(unorderedMapStream, rectUnorderedMap);
}int main() {const size_t numBoxes = 10000;const size_t numRectsPerBox = 2;runPerformanceTest(numBoxes, numRectsPerBox);return 0;
}

4. 测试结果

test@t:~/performance_test$ g++ -std=c++14 performance_test.cpp -o performance_test -O2
test@t:~/performance_test$ ./performance_test
Serialization Time: 2.94966 ms
Deserialization Time: 4.25228 ms
Serialization and deserialization were successful and consistent!
Tuple serialization and deserialization were successful and consistent!
test@t:~/performance_test$ ./performance_test
Serialization Time: 2.99065 ms
Deserialization Time: 4.30422 ms
Serialization and deserialization were successful and consistent!
Tuple serialization and deserialization were successful and consistent!

5. 优缺点分析

以下是这个 C++ 序列化库的优缺点:

5.1 优点

  • 简单易用

    • 提供简单的 API,易于序列化和反序列化多种数据类型,包括基本类型、容器和元组。
  • 高性能

    • 采用直接内存读写,支持平凡复制的类型,提供较高的序列化和反序列化速度。
  • 类型安全

    • 使用模板和类型检查,确保只有支持的类型能够被序列化和反序列化,从而减少运行时错误。
  • 可扩展性

    • 可以为自定义类型提供特定的序列化和反序列化逻辑,只需重载相关函数。
  • 支持多种容器

    • 内置对标准库容器(如 std::vectorstd::map 等)的支持,方便处理常见数据结构。

5.2 缺点

  • 缺乏跨平台兼容性

    • 使用原始字节序列可能在不同平台之间产生兼容性问题,尤其是在字节顺序(endianness)和数据对齐方面。
  • 不支持复杂类型

    • 对于复杂类型(如包含指针、动态分配内存等的类型),需要用户自行实现序列化和反序列化逻辑。
  • 错误处理不足

    • 当前实现缺乏对序列化和反序列化过程中的错误处理,可能导致数据损坏或未定义行为。
  • 内存管理

    • 在处理大型数据时,使用 std::ostringstreamstd::istringstream 可能会引入额外的内存开销。
  • 功能有限

    • 与成熟的序列化库(如 Google Protobuf 或 FlatBuffers)相比,功能较为简单,不支持版本控制、数据描述等高级特性。

http://www.mrgr.cn/news/64465.html

相关文章:

  • 使用AWS Lambda构建无服务器应用程序
  • Java8->Java19的初步探索
  • Java Web开发入门教程
  • 论文学习笔记(二)
  • Jenkins 构建时候提示超时错误被终止
  • java脚手架系列13-IoT
  • 数据丢失不用愁!10款必备的数据恢复软件全介绍
  • 线性排序:如何根据年龄给 100 万用户数据排序?
  • 在使用 AMD GPU 的 PyTorch 中实现自动混合精度
  • 【大模型LLM面试合集】大语言模型架构_tokenize分词
  • 软件测试基础四(服务端知识)
  • AUTOSAR从入门到精通-BswM模块(二)
  • mqtt.fx激活方法
  • 项目的风险
  • 在排序数组中查找元素的第一个和最后一个位置
  • Python酷库之旅-第三方库Pandas(190)
  • 纯前端生成PDF(jsPDF)并下载保存或上传到OSS
  • CSS中的 BFC,是啥呀?
  • 无源元器件-磁珠选型参数总结
  • 32单片机HAL库的引脚初始化
  • C语言第11节:指针(1)
  • 05 Django 框架模型介绍(一)
  • 虚拟机安装Ubuntu系统
  • 网络请求优化:理论与实践
  • 【Python项目管理】“无法创建虚拟环境”报错原因及解决方法
  • JZ2440开发板——LCD