我正在尝试通过一次性使用整个页面来优化程序的内存分配。
我通过这样获取页面大小:sysconf(_SC_PAGESIZE);
,然后像这样计算一页能够容纳的元素总数:elements=pageSize/sizeof(Node);
我考虑当我实际调用malloc分配内存时,我会使用 malloc(elements*sizeof(Node));
。似乎sizeof(Node)的乘法和除法将会互相抵消,但是由于整数除法,我不认为会这样。
这是一次性分配整页内存的最佳方法吗?
谢谢
malloc
函数没有任何页面大小的概念。除非您分配的页面也与页面边界对齐,否则调用malloc
将不能从中受益。只需要使用malloc
分配所需数量的元素,不要担心微观优化,因为这几乎肯定不会给您带来任何好处。sysconf(_SC_PAGESIZE)
函数的结果作为大小参数。但几乎可以肯定您的分配跨越两个页面。malloc(3)
必须为交给您的程序的空间添加一些簿记空间。 - vonbrandelements=pageSize/sizeof(Node);
没有考虑到任何由malloc()
返回的内存块/分块中添加的元数据。在许多情况下,malloc()
将返回一个内存块,该内存块可能至少对齐于 min(sizeof(double),2 * sizeof(void *))
边界(顺便说一句,32字节正在变得越来越常见...)。如果malloc()
获得在页面上对齐的内存块,并添加了其块(带填充),并且您编写了整个页面大小的数据,则最后几个字节会偏离第一页:因此您最终使用了2个页面。mmap()
/ VirtualAlloc()
吗?
这里就是它:int ret;
void *ptr = NULL;
size_t page_size = sysconf(_SC_PAGESIZE);
ret = posix_memalign(&ptr, page_size, page_size);
if (ret != 0 || ptr == NULL) {
fprintf(stderr, "posix_memalign failed: %s\n", strerror(ret));
}
顺带一提,这可能涉及微优化。你可能还没有检查你的Node
是否具有缓存行的倍数大小,也没有找到提高缓存局部性的方法,也没有找到减少内存碎片化的方法。因此,你可能走错了方向:先让它工作,然后进行剖析,优化你的算法,再进行微调。
C11标准增加了aligned_alloc函数,这样你就可以做一些像这样的事情:
#include <stdlib.h>
#include <unistd.h>
void *alloc_page( void )
{
long page_size = sysconf( _SC_PAGESIZE ); /* arguably could be a constant, #define, etc. */
return ( page_size > 0 ? aligned_alloc( page_size, page_size ) : NULL );
}
#include <stdlib.h>
#include <unistd.h>
/* decent default guesses (e.g. - Linux x64) */
static size_t Page_Size = 4096;
static size_t Malloc_Overhead = 32;
/* call once at beginning of program (i.e. - single thread, no allocs yet) */
int alloc_page_init( void )
{
int ret = -1;
long page_size = sysconf( _SC_PAGESIZE );
char *p1 = malloc( 1 );
char *p2 = malloc( 1 );
size_t malloc_overhead;
if ( page_size <= 0 || p1 == NULL || p2 == NULL )
goto FAIL;
malloc_overhead = ( size_t ) ( p2 > p1 ? p2 - p1 : p1 - p2 ); /* non-standard pointer math */
if ( malloc_overhead > 64 || malloc_overhead >= page_size )
goto FAIL;
Page_Size = page_size;
Malloc_Overhead = malloc_overhead;
ret = 0;
FAIL:
if ( p1 )
free( p1 );
if ( p2 )
free( p2 );
return ret;
}
void *alloc_page( void )
{
return aligned_alloc( Page_Size - Malloc_Overhead, Page_Size - Malloc_Overhead );
}
malloc
拥有页面大小的概念。但是,当所需分配的大小与页面大小(或更大)相同时,分配器通常会提供整个页面。要求一个等于页面大小(或页面大小的倍数)的分配再自行细分并不会造成任何损害,只是需要额外的工作量。您可能会在一些机器/编译器/库组合上得到期望的行为,但也可能不会。如果您确实需要页面大小的分配和/或页面对齐的内存,则必须调用特定于操作系统的API来获取它。mmap()
而不是malloc()
。malloc()
必须始终向每个分配添加一些元数据,因此如果您执行malloc(4096)
,它肯定会分配多个页面。另一方面,mmap()
是内核映射页面到您的地址空间的API。这是malloc()
在底层使用的内容。a
上舍入为N
的倍数的常用技巧是说rounded = (a + N-1)/N*N;
。通过首先添加N-1
,您确保除法在所有情况下都向上舍入。如果a
已经是N
的倍数,则添加的N-1
将没有效果;在所有其他情况下,您将获得比rounded = a/N*N;
多一个。
mmap
/VirtualAlloc
... - nneonneomalloc
执行新的mmap
来服务请求,则需要分配至少2页才能给调用者一个页面,因为没有相邻的书记信息,无法使free
高效(O(1)时间)。该余额将不可用于应用程序并将被浪费。在实践中,malloc
将从堆中提供这样的小分配。 - R.. GitHub STOP HELPING ICEmalloc
将会触及2个页面来更新簿记信息,即使您只使用一个页面,这可能会导致额外的页面错误,如果第二个页面尚未备份或已被交换出。无论如何,正如我所说,我看不到确切页面大小分配会更好的原因,而且有很多理由它可能会更糟。我会根据您的程序需求选择一边,而不是基于对硬件或库实现行为的错误假设。 - R.. GitHub STOP HELPING ICE