C语言 malloc如何请求堆内存



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

# 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);

现在我想知道哪种方法是上级?我认为#1要求在堆中有x个长度为y的块,它们不需要彼此相邻。其中#2是在堆中请求一个y*x块。#2是请求一个巨大的x*y块,而#1是请求不需要连接的块。因此,既然可以拆分,那么第1号就可以成为监督者。假设堆无法处理长度为x*y的巨大条带,可以处理x数量的y条带数据。

首先这是真的吗?这两种方法我都遗漏了什么吗?我的论点实际吗?或者,即使是真的,也不太可能出现?有更高级的方法吗?

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

然而,第二种选择可以利用空间和时间局部性。基本上,由于更多的数据彼此紧挨着,您需要的数据在CPU缓存中的可能性就会增加,因此,在此内存上操作将会快得多。

这取决于您使用的内存分配器和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个周期。所以第二条更好。

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


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

相关内容

  • 没有找到相关文章

最新更新