提问人:klonyyy 提问时间:10/17/2023 最后编辑:klonyyy 更新时间:10/17/2023 访问量:61
访客模式序列化内存膨胀
Visitor pattern serialization memory bloat
问:
我正在开发一个用于嵌入式目标的序列化库。该库基于经典的访问者模式,在我要序列化的每个结构中都实现了模板化方法。例:pack()
struct softwareInfo
{
uint32_t version;
std::array<uint8_t, 40> commitHash;
uint32_t buildDate;
template <class T>
void pack(T& archive)
{
archive.process(HVP(version));
archive.process(HVP(commitHash));
archive.process(HVP(buildDate));
}
};
HVP 宏将具有哈希名称的字段打包到稍后使用的 hashValuePair 类中。
constexpr uint32_t compileTimeHash(const char* str, size_t index = 0, uint32_t hash = 0)
{
return (str[index] == '\0') ? hash : compileTimeHash(str, index + 1, hash * 31 + str[index]);
}
template <typename T>
class hashValuePair
{
public:
hashValuePair(uint32_t hash, T* value) : hash(hash), value(value) {}
uint32_t getHash() const { return hash; }
T& getValueRef() { return *value; }
private:
uint32_t hash = 0;
T* const value = nullptr;
};
#define HVP(var) hashValuePair(compileTimeHash(#var), &var)
还有两个模块:
- PersistentStorage 类,用于注册可序列化的对象。它包含以下缓冲区:
std::array<StorageEntry, maxEntries> entries
哪里:
struct StorageEntry
{
std::aligned_storage_t<sizeof(Entry<void>), alignof(Entry<void>)> buffer;
EntryBase* object;
struct Header
{
uint32_t hashName;
uint32_t size;
} header;
uint32_t datacrc32;
Status packStatus;
Status unpackStatus;
};
class EntryBase
{
public:
virtual ~EntryBase() {}
virtual void pack(Packer::Packer& packer) = 0;
virtual void pack(Packer::Unpacker& unpacker) = 0;
};
template <typename T>
class Entry : public EntryBase
{
public:
explicit Entry(T* item) : item(item) {}
void pack(Packer::Packer& packer) override { item->pack(std::forward<Packer::Packer&>(packer)); }
void pack(Packer::Unpacker& unpacker) override { item->pack(std::forward<Packer::Unpacker&>(unpacker)); }
private:
T* const item;
};
它有两个主要方法 - 并且采用所有已注册的结构并使用下面所示的 和 方法一一打包/解压缩它们。doRead()
doWrite()
pack
unpack
- 第二个模块是一对 Packer/Unpacker 类,用于通过访问使用 process 方法列出的每个成员来序列化和反序列化结构。
namespace Packer
{
class Packer
{
public:
template <class T>
void process(hashValuePair<T>&& hashValuePair)
{
packType(hashValuePair.getValueRef());
}
private:
template <class T>
void packType(T& value)
{
(...)
copyToBuf(value);
}
template <class T>
void copyToBuf(T value)
{
it += serialize<T>(value, it);
}
};
class Unpacker
{
public:
template <class T>
void process(hashValuePair<T>&& hashValuePair)
{
//search for value hash
unpackType(hashValuePair.getValueRef());
}
private:
template <class T>
void unpackType(T& value)
{
(...)
tryUnpack(*it++, value);
}
template <typename T>
void tryUnpack(uint8_t maybeType, T& value)
{
if (maybeType != getType(value))
//error
copyToValue(value);
}
template <typename T>
void copyToValue(T& value)
{
value = deserialize<T>(it);
it += sizeof(value);
}
};
template <class PackableObject>
Status pack(PackableObject& obj, std::span<uint8_t>& buf, size_t& packedSize)
{
Status status{};
auto packer = Packer(buf, packedSize, &status);
obj.pack(packer);
return status;
}
template <class PackableObject>
Status unpack(PackableObject& obj, std::span<uint8_t>& buf)
{
Status status{};
auto unpacker = Unpacker(buf, &status);
obj.pack(unpacker);
return status;
}
}
我故意省略了实现的其余部分(错误处理、搜索字段哈希),只留下最基本的流程。
现在,我可以看到,与上面提供的实现相关的内存占用量相对较大。我可以想象模板化方法必须使用使用的类型进行实例化,但是我不确定我是否以最佳方式执行所有操作。例如,序列化结构使用 464B 的 FLASH 存储器 Packer FLASH 占用空间,划分为函数。当我添加另一个 uint32_t 字段时,我的大小增加了 44B,这感觉相当大,特别是考虑到我已经将这种类型与以前的uint32_t场 Packer FLASH 占用空间划分为具有附加uint32_t场的功能。生成类型为 release with -Os。softwareInfo
您能看到当前设计中的任何明显缺陷吗?在这种情况下,像 std::variant 这样的东西会有所帮助吗,因为它的运行时多态性?
答: 暂无答案
评论
\x00\x00U \x00\x04C40 \x00\x2CU
process
compileTimeHash
consteval