【问题标题】:Storing records in a byte array vs using an array of structs将记录存储在字节数组中与使用结构数组
【发布时间】:2014-09-05 21:05:16
【问题描述】:

我有 2 亿条记录,其中一些具有可变大小的字段(字符串、可变长度数组等)。我需要对它们执行一些过滤器、聚合等(面向分析的查询)。

我只想将它们全部放在内存中(足以放入一个大盒子),然后对它们进行线性扫描。我可以采用两种方法,我想听听您对哪种方法更好的意见,以最大限度地提高速度:

  1. 使用带有char* 和int* 等结构的数组来处理可变长度字段
  2. 使用大字节数组,像二进制流一样扫描字节数组,然后解析记录

您会推荐哪种方法?

更新:使用 C。

【问题讨论】:

  • 我建议你先选择一种语言。
  • @crashmstr:刚刚更新了我的问题 - 使用 c/c++
  • @jeffreyveon c 和 C++ 是两种不同的语言,哪一种?
  • C/C++ 不是一种语言。现代 C 和现代 C++ 自首次创建以来都发生了变化和分歧,并且惯用(“本机”)解决方案可能非常不同。
  • 很公平。我想我并没有使用太多现代 C++,所以出于所有实际目的,它是 C。

标签: c arrays struct


【解决方案1】:

不幸的答案是,“这取决于你没有提供的细节”,虽然这是真的,但并不是特别有用。解决此类问题的一般建议是从最简单/最明显的设计开始,然后根据需要对其进行分析和优化。如果它真的很重要,您可以使用您的确切数据和用例对一些设计进行一些非常基本的基准测试,以更准确地了解您应该采取的方向。

总体上看一些具体的设计及其一般的优点/缺点:

一个大缓冲区

 char* pBuffer = malloc(200000000);
  • 假设您的数据可以一次全部放入内存。
  • 对于所有文本(或主要是文本)数据会更好。
  • 不会是我处理大数据的首选,因为它只是镜像磁盘上的数据。最好只使用硬件/软件文件缓存/预读并直接从驱动器中读取数据,或者在需要时对其进行映射。
  • 对于线性扫描,这是一种很好的格式,但如果它需要复杂的解析(尤其是当您必须进行多次扫描时),您会损失一些。
  • 假设您可以一个接一个地打包结构,则可能会产生最少的开销。

静态结构

 typedef struct {
     char  Data1[32];
     int   Data2[10];
 } myStruct;

 myStruct *pData = malloc(sizeof(myStruct)*200000000);
  • 最简单的设计,并且可能以内存为代价实现速度的最佳潜力(无需实际分析)。
  • 如果您的可变长度数组的大小范围很广,您将浪费大量内存。由于您有 2 亿条记录,您可能没有足够的内存来使用此方法。
  • 对于线性扫描,由于内存缓存/预取,这可能是最好的内存结构。

动态结构

 typedef struct {
     char* pData1;
     int*  pData2;
 } myStruct2;

 myStruct2 *pData = malloc(sizeof(myStruct2)*200000000);
  • 有 2 亿条记录,这将需要大量动态内存分配,这很可能会对速度产生重大影响。
  • 如果您的动态数组具有广泛的大小范围,则可能会提高内存效率(尽管请参阅下一点)。
  • 注意指针大小的开销。在 32 位系统上,这个结构需要 8 个字节(忽略填充)来存储指针,仅 1.6 GB 就可以存储 2 亿条记录!如果您的动态数组通常很小(或为空),您可能会在开销上花费比实际数据更多的内存。
  • 对于数据的线性扫描,这种类型的结构可能会表现不佳,因为您以非线性方式访问内存,而预取器无法预测。

流式传输

  • 如果您只需要对数据进行一次扫描,那么我会考虑一种流式解决方案,您可以一次从文件中读取一小部分数据。
  • 适用于无法放入内存的超大型数据集。
  • 这里的主要限制是磁盘读取速度和解析的复杂程度。
  • 即使您必须通过文件缓存执行多次传递,这在速度上也可能与其他方法相当。

哪些是“最好的”实际上取决于您的具体情况......我可以想到每种情况都是首选方法。

【讨论】:

    【解决方案2】:

    您确实可以使用结构,但您必须非常小心对齐和别名,并且当存在可变长度部分时,所有这些都需要修补。特别是,您可以不使用此类结构的数组,因为数组中的所有条目都必须是恒定大小。

    我建议使用平面阵列方法。然后添加健康剂量的抽象;你不希望你的“业务逻辑”做一些小事。

    更好的是,如果您需要对整个数据集进行一次线性扫描,那么您应该将其视为数据流,并将记录反序列化(复制)到适当的本机结构中,一次一个。

    【讨论】:

      【解决方案3】:

      “你会推荐哪种方法?”其实也没有。有了这么多数据,我的建议就像你的结构的链接列表。但是,如果您 100% 确定能够为所有数据分配所需的内存量(通过 1 个 malloc 调用),那么请使用结构数组。

      【讨论】:

        猜你喜欢
        • 2022-11-21
        • 2019-12-22
        • 2022-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多