【问题标题】:Multiple small mallocs vs one big malloc多个小 malloc 与一个大 malloc
【发布时间】:2012-07-23 08:54:00
【问题描述】:

任务是将二进制文件解析到内存中。但是,我不知道需要分配多少内存。

哪种方法更可取:在解析例程中执行多个小型 malloc,或者首先遍历文件以确定所需的内存量,然后再次解析?

感谢任何提示。

【问题讨论】:

  • 你尝试过指数方法吗?
  • 用空间效率低下换取时间效率的优势,难道你不能根据stat()或其等价物进行初始分配,甚至避免初始遍历吗?
  • @KeithFlower 听起来像是在谈论内存结构,而不仅仅是原始字节。
  • @pst - 嗯,是的,我假设 parsed-structure-in-memory
  • 我很感兴趣——你说你正在将它解析到内存中——你的意思是把它解析成许多更小的可变大小的结构吗?如果是这样,您将如何在一个 malloc 中分配它们?

标签: c malloc binaryfiles


【解决方案1】:

几乎在所有情况下,一个大分配都比许多小分配好。这可以防止碎片,减少系统调用。它通常会通过更好的局部性带来更好的性能。

一种常见的技术是先分配一个小段,然后按固定因子重新分配一个更大的段(通常是1.5)。全部元素集齐后,如果认为过度分配较大,可以将内存固定为最大。

无论如何:首先实现最简单的。如果您有性能问题:基准测试。然后优化。事实证明,分配甚至不是您的瓶颈。

编辑:正如 R.. 所提到的,通过推理内存上限及其与文件长度的关系,您可能会很好地了解分配多少。大多数好的二进制格式还在标题段中包含长度和大小信息。如果您可以通过一些算术和/或文件搜索来计算出数据结构所需的确切大小,那么您就是赢家。

【讨论】:

  • 如果结构中包含指向彼此的指针,重新分配可能会很痛苦——如果分配移动到新地址,所有指针都将失效。此外,从技术上讲,甚至 UB 在搬迁后进行算术来修复它们。我认为将malloc 空间用于附加数据更有意义,而不是在原始块上调用realloc,并且可能将所有分配的块视为从第一个块的开头开始的链表这样您以后就可以轻松地释放它们。
  • @R.. 是的,如果您有参考数据,所有这一切都会变得更加困难。正如我所说:先做最简单的事情,然后再优化。
  • 我认为最简单的解决方案是进行一些观察,例如内存中表示的大小以文件长度的 3 倍为界,然后分配那么多内存。 :-) 如果您希望在完成后能够释放未使用的部分,请使用 mmap 获取匿名页面,然后您可以使用 munmap 您不需要的部分(因为您不能使用 realloc缩减分配而不冒将其移动到新地址的风险)。
  • 顺便说一句,我想到这些的原因是从如何设计一个不糟糕的网络浏览器的角度来看。与每页调用malloc 数千、数十或数十万次的典型当前浏览器相比,对网页的单个分配(为以后的 DOM 修改额外分配)将对内存使用/碎片/争用产生巨大影响加载。
  • @R.. 我尝试将您的想法整合到答案中。我希望我为他们伸张正义。
【解决方案2】:

您是否考虑过为此使用mmap()?请参阅this link 了解更多信息。基本上,您只需将文件映射到内存并像访问内存块一样访问它,完全避免malloc()s。

【讨论】:

  • 有趣,我不知道它存在。但恐怕我不能在这里使用它,因为我必须解析文件,而不是简单地加载它。尽管如此,我还是学到了一些东西。谢谢
  • mmap 的 +1 — 最合理的解决方案。 @Carsos:它在您的情况下完美运行-您避免编写大量代码,担心内存(操作系统将读取,映射到虚拟内存,pre-fectch 等),并且您可以像使用较小的缓冲区一样简单地解析它.
  • @VladLazarenko:解析很简单,输入文件很小,但我可以提取的信息量(相对)巨大并且(需要?)关心 malloc。
  • 使用 mmap 没有 malloc。您是否考虑过阅读文档?它甚至有例子......
  • @VladLazarenko:感谢您的关注,但我不明白您为什么坚持使用“mmap”。我的输入格式是“线性的”,即一旦我解析了某个单元,我只需要继续解析即可。有道理?所以,我的问题是关于分配多少内存(我需要这样做)来支持从我的输入中提取的信息。问候
【解决方案3】:

这是一个经典的时空权衡。假设您需要全部内容,分配大量小块的效率可能低于一个大块。

理想情况下,文件格式应该对元数据进行编码,例如块大小、块数等。考虑到磁盘访问的延迟与内存的速度相比,读取文件以确定所需的大小可能需要更长的时间。

最有效的方法还取决于需要多少处理。你提到解析,但它是一个二进制文件。大概有很多块和可变大小的结构需要遍历?

您可以尝试以下几种策略:

  • 如果文件不是太大而无法放入内存,您可以查询文件系统以查看文件有多大,将其作为一大块读取,然后将其拉到内存中。这会非常快,但会占用大量内存。

  • 根据二进制文件的结构,您可能可以执行几次fseek() 调用来确定您需要读取的块有多大(如果您不需要整个文件)并阅读这些内容。

  • 您可以使用mmap() 将文件映射到内存中,并让运行时管理将数据分页到内存中。

【讨论】:

  • 哦,我想回应一下 pmr 和 Gene 所说的 - 先实现最简单和最干净的方法,然后看看你得到了什么样的性能。过早的优化...
【解决方案4】:

遍历文件以确定其大小和所需的内存量绝对不是要走的路——磁盘 I/O 非常昂贵。

另一种选择是获取文件大小,然后分配内存。有关如何获取文件大小的详细信息,请参阅this Q/A。但是,这种方法也效率不高。

总而言之,这实际上取决于您如何读取数据以及如何解析它。例如,拥有一些相当大的数据块以及异步文件 I/O 可能最适合您。但这是实施起来相对复杂的任务。

可能最简单和最有效的方法是使用mmap 并将文件的内容“映射”到内存中。

【讨论】:

    【解决方案5】:

    没有一般性的答案,至少部分原因是您没有定义“首选”。最简单的?最快的?需要最少的堆?另外,“解析二进制文件”是什么意思?解析通常是为了创建数据结构而对人类可读的文本进行的。

    每个malloc 通常都有少量开销。但是,除非最终的数据结构很大,否则不太可能产生任何显着差异。

    使用干净的接口生成最清晰的代码,以便您以后可以替换分配方法。然后只有在你知道有问题之后才担心优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-16
      • 1970-01-01
      • 2015-09-17
      相关资源
      最近更新 更多