【问题标题】:How can I keep multiple copies of a very large dataset in memory?如何在内存中保留一个非常大的数据集的多个副本?
【发布时间】:2021-04-28 05:09:47
【问题描述】:

未来人们的 TLDR: 我继承了一个程序,需要对其进行调整以处理比现在大几个数量级的数据。我需要帮助找出管理 30GB 数组的多个副本的方法。

我最近继承了一些研究代码(用 c 编写),最初是为了在相对约 5 GB 的一小部分数据上运行而编写的。该代码要求我能够一次访问该数组的四个副本(一个char 数组和三个double 数组)。因此,当时的作者无需担心内存使用情况,因此有多个实例,其中内存中同时存在 2-4 个额外数组。

另外,这是生物数据(基因组),数组并不稀疏。

现在的问题是我必须使代码适应单个双精度数组为 30GB 的情况。

我不确定是否需要一次访问所有值,但我知道代码循环遍历所有值的情况很常见。

我尝试过的事情

用文件模拟恒定时间访问

我将每个数组拆分为 10k 个字符或双精度的集合,并将它们全部写入文件。然后我更改了对我自己的函数的所有访问权限,并让它从文件中读取或覆盖文件中的那一行。虽然这行得通。问题是它非常慢(很可能是由于所有文件的打开和关闭+写入磁盘的速度),这已经是程序的问题,我不想让它变得更糟。

在不需要时将数组写入磁盘

我注意到有很多时候程序不需要数组,我决定将它写入磁盘,然后在需要时从它读取。我面临的问题是,将整个数组写入磁盘仍然需要很长时间(10 分钟+?),只需打开文件并关闭一次(与上述方法不同)。

使用更多内存

由于这是为了研究,我确实可以访问具有 150GB RAM 的计算集群。我将此程序作为作业提交,但不幸的是,即使如此,该过程也因占用过多内存而被杀死。我最初怀疑这只是内存泄漏,但经过进一步检查,确实似乎在程序运行时创建了 >5 个双数组。就像旁注个人机器有 40(我知道的一个奇怪的数字)GB 内存一样。

愚蠢的最后一搏

我禁止内核过度使用内存,因为我注意到它不是在分配许多数组时崩溃,而是在它实际开始访问它们时崩溃。但是,我认为这最终没有做任何事情,因为它仍然过度承诺。

一天晚上,我对它一直被杀死感到非常沮丧,并决定以 -10000 的精度运行该程序,这导致我的计算机崩溃,因为它杀死了其他进程以弥补更多内存。

我也尝试过使用mmap(),但不确定这是否是我应该追求的。

为什么需要这么多内存?这是 XY 问题吗?

虽然我真的不能确定这是否是一个 XY 问题,但我很有信心我需要同时拥有至少三个数组(尽管我不会在数组中跳来跳去太多)。

有没有人知道如何解决这个问题?提前谢谢你的帮助。最后,我使用的是 Linux。

【问题讨论】:

  • 让 Linux 内存管理器为您处理所有这些。只需购买尽可能多的 RAM,购买一个不错的快速 SSD 并将其连接到系统上最快的总线,然后调整 Linux 环境的交换文件大小为数据腾出空间。
  • 感谢您的评论!我会尝试增加交换空间。有 200GB 的交换空间可以吗?
  • 内存映射文件是让您的应用程序在因违反其使用政策而被踢出局的大铁杆上被接受的方式。如果您不与其他进程共享空间,您可能会很好。使用 mmap,每个数组都有一个文件,然后调整内存块的大小以适合您的系统策略。它本质上使用与交换文件相同的算法,但范围不同。
  • 这些是运行 64 位版本的 Linux 的 64 位机器吗?
  • 我想是的。检查他们的政策,但这对我来说似乎是系统管理的东西。还有一点需要注意:如果您的磁盘有碎片,则增加交换文件的大小可能会失败。或者至少以前交换文件必须是连续的。因此,我偏爱专用交换磁盘。

标签: c linux dataset bioinformatics


【解决方案1】:

这听起来像是 mmap 的一个很好的用例。

mmap 函数可用于获取打开的文件并将其映射到内存区域。尽管您可以定期手动刷新到磁盘,但通过返回的指针对文件的读取和写入都是在内部处理的。这将允许您操作大于系统物理内存的数据结构。

这还有一个好处,就是您无需担心手动从磁盘来回移动数据。内核会为你处理它。

因此,对于这些大型数组中的每一个,您都可以创建一个由磁盘上的文件支持的内存映射。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#define DATA_LEN 30000000000LL

int main()
{
    int array1_fd = open("/tmp/array1", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (array1_fd < 0) {
        perror("open failed");
        exit(1);
    }

    // make sure file is big enough
    if (lseek(array1_fd, DATA_LEN, SEEK_SET) == -1) {
        perror("seek to len failed");
        exit(1);
    }
    if (write(array1_fd, "x", 1) == -1) {
        perror("write at end failed");
        exit(1);
    }
    if (lseek(array1_fd, 0, SEEK_SET) == -1) {
        perror("seek to 0 failed");
        exit(1);
    }

    char *array1 = mmap(NULL, DATA_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, array1_fd, 0);
    if (array1 == MAP_FAILED) {
        perror("mmap failed");
        exit(1);
    }

    // Use array1

    munmap(array1, DATA_LEN);
    close(array1_fd);
    return 0;
}

mmap 调用的重要部分是MAP_SHARED 标志。这意味着对映射内存区域的更新将传递到底层文件描述符。

【讨论】:

  • 不错。与仅依赖系统交换文件相比有优势吗?
  • 感谢两位的帮助!我遇到了总线错误。你知道这意味着什么吗?
  • 更多上下文gdb 表明print array1 提供了一个有效的内存地址,但如果我尝试x array1 它无法访问该地址的内存。此外,例如,如果我这样做 print array[10],它就无法访问它
  • @JoshGallowa 查看我的编辑。该文件首先需要与映射一样大。
  • @jwdonahue 依赖交换意味着您需要系统级支持。这对于您的本地机器可能没问题,但对于您不是管理员的机器则不行。使用mmap 意味着您只需要足够的磁盘空间。
猜你喜欢
  • 1970-01-01
  • 2013-01-11
  • 2017-04-08
  • 2022-08-15
  • 2013-11-24
  • 2013-08-06
  • 1970-01-01
  • 2020-05-11
  • 2017-10-08
相关资源
最近更新 更多