【问题标题】:Reliably using C++ Small String Optimization to fread short std::strings from Files into Memory可靠地使用 C++ 小字符串优化将短 std::strings 从文件读取到内存中
【发布时间】:2016-05-16 03:38:25
【问题描述】:

我有以下类,它包含一个名为 Index 的数据结构,它的计算成本很高。 所以我将索引缓存到磁盘并再次读取它。模板类型 T 的索引元素 id 可以与多种原始数据类型一起使用。

但我也想使用类型为 std::string 的 id。我为一般情况编写了序列化/反序列化代码,并测试了它是否适用于普通的 C++ 字符串,并且如果它们足够短,它们是否可以工作。小字符串优化似乎开始了。

我还编写了一个不同的实现,只是为了安全地处理更长的字符串。但是安全代码慢了大约 10 倍,我真的很想用 fread 读取字符串(500 毫秒读取非常痛苦,而 50 毫秒完全没问题)。

如果我知道所有标识符都比可能的最长短字符串短,我如何可靠地使用我的 libcpp 小字符串优化?如何可靠地判断可能的最长小字符串有多长?

template<typename T>
class Reader {
public:
    struct Index {
        T id;
        size_t length;
        // ... values etc
    };

    Index* index;
    size_t indexTableSize;

    void serialize(const char* fileName) {
        FILE *file = fopen(fileName, "w+b");
        if (file == NULL)
            return;

        fwrite(&indexTableSize, sizeof(size_t), 1, file);
        fwrite(index, sizeof(Index), indexTableSize, file);

        fclose(file);
    }

    void deserialize(const char* fileName) {
        FILE *file = fopen(fileName, "rb");
        if (file == NULL)
            return;

        fread(&indexTableSize, sizeof(size_t), 1, file);
        index = new Index[indexTableSize];
        fread(index, sizeof(Index), indexTableSize, file);

        fclose(file);
    }


};

// works perfectly fine
template class Reader<int32_t>;

// works perfectly fine for strings shorter than 22 bytes
template class Reader<std::string>;

【问题讨论】:

  • 没有。就是不行。不要这样做。
  • 如果您必须使用 fread 而不是可以写入 std::string 的 iostreams 函数,然后创建一个 char 缓冲区 [4096] (或您喜欢的任何最大大小),fread 进去,然后构造一个带有string s(buffer, indexTableSize)的字符串
  • 您原则上可以使用自定义分配器对其进行测试,该分配器在被要求分配时立即抛出。在循环中创建逐渐变大的字符串,并捕获异常。在实践中,虽然查找所需的所有编译器可能更容易,但它可能几乎总是 22 个字符。
  • 永远不要在现实生活中编写这样的代码。它可以正常工作六个月,然后在 RHEL 6、Visual Studio 2018 或 32 位或 128 位系统上编译时出现惊人的崩溃。
  • 在 C++/CLI .NET 上也可能会失败,因为我认为 std::string 可能是某种共享 CLR 对象,以便更容易传递给其他 .NET 软件。

标签: c++ string serialization stl stdstring


【解决方案1】:

如果你想用类型 T 可靠地序列化/反序列化,你必须确保你的类型 T 是 POD 类型(或更准确地说是 standard layouttrivial)。

您可以使用std::is_trivially_copyable&lt;T&gt;std::is_standard_layout&lt;T&gt; 在模板中检查这一点。不幸的是,std::string 将失败。

如果不是这样,您必须找到一种适当的方法来序列化/反序列化类,即写入/读取允许重建对象状态的数据(这里是字符串的长度及其内容) .

三个选项:

  • 使用将 T 从/转换为字节数组的辅助模板,并为您的阅读器可能使用的每种类型编写此模板的特化。
  • 使用执行此操作的成员函数。但这对于 std 类型是不可能的。
  • 使用序列化库,例如boost::serializes11nothers

在任何情况下我都会强烈建议您不要依赖不可移植的属性,例如短字符串的长度,尤其是如果您在应该使用泛型类型的模板中有此代码。

【讨论】:

    【解决方案2】:

    std::string 不是trivially copyable。并且在 C++ 中对一个类型执行 memcpy(相当于 fwriteing 它和 freading 它返回)只有在它可以轻松复制的情况下才是合法的。因此,您想要做的事情是不可能直接完成的。

    如果你想序列化一个字符串,你必须手动完成。你必须得到字符的数量并写下来,然后自己写这些字符。要重新读取它,您必须读取字符串的大小,然后读取那么多字符。

    【讨论】:

      猜你喜欢
      • 2019-04-12
      • 2021-09-13
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      • 1970-01-01
      • 1970-01-01
      • 2012-05-13
      • 2014-01-21
      相关资源
      最近更新 更多