0

我有一个分配给堆的巨大数组,因为如果留在堆栈上会导致错误。现在,据我所知,有两种方法可以将其发送到堆中。

#1

int i;
int x=10000, int y=10000;
double** array=(double**)malloc(sizeof(double*)*x);
if (image) {
    for (i=0; i<x; i++) {
        array[i] =(double*)malloc(sizeof(double)*y);
    }
}

#2

double *array[x][y]=(double*)malloc(sizeof(double)*x*y);

现在我想知道哪种方法是superoir?我认为#1 是在堆中要求 x 个长度为 y 的块,它们不必彼此相邻。其中 #2 要求堆中的 y*x 块。#2 要求一个巨大的 x*y 块,而 #1 要求不需要连接的块。#1 也将是 superoir,因为它可以被拆分。假设堆无法处理长度为 x*y 的巨大条带,但可以处理 x 数量的 y 条数据条。

首先这是真的吗?我错过了任何一种方法吗?我的论点是否切实可行,或者如果属实,这不是一种可能的情况?有更高级的方法吗?

谢谢你的洞察力。

4

2 回答 2

4

您是正确的,第一种方法可能更灵活,因为它不需要找到总大小的连续空闲内存范围,而第二种方法可以。一个可能的不利影响是,如果分配的平板不连续,这本身可能会导致堆的更多碎片。每个平板之间会有一些空间区域,未来的分配需要在其中找到空间。

然而,第二种选择可能会利用空间和时间的局部性。基本上,由于更多的数据彼此相邻,因此您需要的数据更有可能位于 CPU 缓存中,因此,在此内存上操作会更快。

于 2013-06-07T01:45:41.227 回答
1

这取决于您使用的内存分配器以及 x 和 y 的值。

内存分配器通常会缓存用户空间中的小内存块并处理用户空间中的小分配,同时通过mmap.

大多数内存分配器是这样工作的:

void* malloc(size_t size)
    if (size > THRESHOLD) {
        return large_alloc(size)     // forward to mmap
    }
retry:
    void* ret = small_alloc(size);   // handled in user space
    if (ret == NULL) {               // no small blocks left
        enlarge_heap();              // map more memory from kernel
        goto retry;
    }
    return ret;
}

在你的情况下 y == 10000 所以你要求一个 80000 字节的内存块。在 glibc 的默认内存分配器中,mmap 阈值为 128kB。因此,如果分配器已经缓存了足够的内存,则该请求倾向于在用户空间中处理。但是#2 会调用 mmap 调用,因为它大于 128kB。

但是,在您的示例中,x == 10000。所以您说的是单个 mmap 系统调用调用和用户空间中的 10000 个分配。相信我。#2 更快:

在现代 x86 机器上,高度优化的分配器实现中的分配总是需要 70 多个周期。10000 个分配将消耗超过 700000 个周期。但是一个典型的 mmap 调用延迟应该不超过 100000 个周期。所以#2更好。

对于其他分配器,例如 TCMalloc,它有点不同。TCMalloc 没有这样的阈值,并且总是试图在其Span结构中处理用户空间中的大分配请求。所以#2肯定要好得多,因为它只需要一次分配。


我同意 #1 更灵活,因为 #2 需要分配器找到一个大的连续内存块。但请记住,它仅在虚拟内存中是连续的,并且当您第一次接触它时,它会按需映射物理页面。这意味着它不需要在物理内存中是连续的。而且通常很容易在虚拟内存中找到一个 8 * 10000 * 10000 字节的连续内存区域。

于 2013-06-07T02:16:47.000 回答