如何记忆地图巨大的矩阵?

时间:2011-01-29 20:22:07

标签: c++ d mmap

假设您有一个巨大的(40+ GB)特征值(浮点)矩阵,行是不同的特征,列是样本/图像。

该表是按列预先计算的。 然后,它被完全访问行和多线程(每个线程加载整行)几次。

处理这个矩阵的最佳方法是什么?我特别琢磨超过5分:

  1. 由于它是在x64 PC上运行的,我可以在内存中立即映射整个矩阵,但这有意义吗?
  2. 多线程的影响怎么样(多线程初始计算?)?
  3. 如何布局矩阵:行或列专业?
  4. 在预计算完成后将矩阵标记为只读会有帮助吗?
  5. 是否可以使用像http://www.kernel.org/doc/man-pages/online/pages/man2/madvise.2.html之类的东西来加快速度?

3 个答案:

答案 0 :(得分:5)

整个文件的内存映射可以使这个过程更容易。

您希望布置数据以针对最常见的访问模式进行优化。听起来好像数据将被写入一次(逐列)并多次读取(逐行)。这表明数据应按行主要顺序存储。

一旦完成预计算就将矩阵标记为只读可能无助于性能(有一些可能的低级优化,但我认为没有任何实现它们),但它会防止意外错误写入您不想要的数据。不妨。

一旦你的应用程序被编写并正常工作,

madvise最终可能会有用。

我的总体建议:以最简单的方式编写程序,首先按顺序编写,然后将计时器放在整个事物和各种主要操作上。确保主要操作时间总和为总时间,因此您可以确定您没有遗漏任何东西。然后将性能改进工作的目标转向实际花费最多时间的组件。

根据JimR在评论中提到的4MB页面,您可能最终想要查看hugetlbfs或使用具有透明大页面支持的Linux内核版本(合并为2.6.38,可能会被修补到早期版本中)。这可能会为您节省大量的TLB未命中,并说服内核以足够大的块来执行磁盘IO以分摊任何搜索开销。

答案 1 :(得分:3)

  1. 也许,见下文。
  2. 所有线程的总工作集大小不得超过可用RAM,否则程序将因为交换而以蜗牛速度运行。
  3. 布局应与访问模式匹配,只要符合条件2即可。
  4. “标记为只读”是什么意思?
  5. 测量它。
  6. Re 3:如果你有8个CPU但没有足够的RAM加载8行,你应该让每个线程以可管理的块顺序处理它的行。在这种情况下,矩阵的块布局是有意义的。如果线程必须在内存中有整行来处理它,我担心你不能使用所有的CPU,因为进程将开始颠簸,即从ram中取出矩阵的一些子集并重新加载另一个需要的子集这比完全交换稍微不那么糟糕,因为矩阵永远不会被修改,所以页面的内容在被踢出之前不需要写入交换文件。但它仍然严重损害了性能。

    此外,从多个线程进行随机访问I / O是一个坏主意,如果您使用mmap(),这将是您最终要做的事情。您(可能)只有一个磁盘,并行I / O只会让它变慢。所以mmap()可能没有意义,你可以通过顺序读入ram来实现更好的I / O性能。

    请注意,40GB是大约1050万页的4096字节。通过执行mmap(),在最坏的情况下,您将通过许多硬盘搜索来减慢计算速度。每次搜索8ms(取自维基百科),你最终会浪费83666秒,即差不多一整天!

答案 2 :(得分:2)

如果你可以将整个东西放入主内存中,那么是:内存映射全部,无论是列专业还是行专业都无关紧要。但是,在40+ Gb时,我确信它对于主内存来说太大了。在这种情况下:

  1. 不,不要映射整个事情!至少,如果您将内存全部映射,请不要指望内存像普通内存一样工作。如果你没有妥善处理i / o问题,你的程序将永远存在。
  2. 如果您将行存储为主行,则解决了多线程访问问题(听起来您没有多线程列写入)。
  3. 你应该按行排列,假设每个单元格被写入一次,然后多次读取。
  4. 是的,我认为将矩阵标记为只读后会有用,但纯粹是为了防止错误(意外写入)。它不会影响性能。
  5. 不,没有多少聪明的内核预读可以解决您的性能问题。您需要在算法级别解决它。
  6. 我认为你会遇到一个天真实现的性能问题。要么是在写入时使用thrash的计算机(如果存储行主要),要么在查询时抖动(如果存储列专业)。后者可能更糟糕,但两方面都存在问题。

    正确的解决方案是使用既不是行主要也不是列主要但是“大正方形”的中间表示。获取前50,000个列并将它们存储在内存映射文件中(阶段1)。无论是列专业还是行专业都没关系,因为它只是纯粹的内存驻留。然后,取每一行并将其写入最终的行主要内存映射文件(阶段2)。然后重复下一个50,000列的循环,依此类推。