分配页面对齐的内存块有什么好处?

时间:2015-08-21 11:22:16

标签: c memory-management alignment cpu

我意识到大多数CPU更擅长读取对齐内存地址的数据,即内存地址是CPU字的倍数。但是,在许多地方,我读到了有关分配页面对齐内存的信息。为什么有人想要获得页面对齐的内存地址?它只是为了更大的表现吗?

3 个答案:

答案 0 :(得分:4)

"传统"分配内存的方法是将它放在一个连续的地址空间中("堆",通过调用sbrk()向上增长)。每次进入页面边界时,都会出现页面错误,并且您将映射到新页面。这个策略有两个后果:

  1. 页面只有在释放该页面内的所有分配并且所有其他分配都映射到较低地址时才能被释放。 (堆碎片的典型影响)。
  2. 较大的分配可能占用一个页面而不是严格需要(如果它们从页面中间的某个位置开始)。
  3. 因此,此策略仅适用于您不想要浪费的小型内存块。每个分配的整页。

    对于更大的块,最好使用mmap()直接将新页面映射到某个地方,这样就可以获得页面对齐的内存"。使用此功能,您的分配不会与其他分配共享页面。只要您不再需要内存,就可以将其返回给操作系统。请注意,许多malloc()实施会自动选择是使用sbrk()还是mmap()进行分配,具体取决于所需分配的大小。

答案 1 :(得分:1)

对齐限制通常与direct IO相关联 - 它绕过页面缓存,将数据直接复制到磁盘或从磁盘复制到进程的地址空间。这可以在不需要页面缓存的情况下提供显着的性能改进 - 例如流式传输数千兆字节的数据,尤其是在向极快速磁盘系统执行IO时。

请注意,只有部分文件系统支持直接IO。

在Linux上,RedHat's documentation部分是:

  

直接I / O最佳实践

           

用户必须始终注意使用正确对齐且大小合适的IO。   这对于直接I / O尤其重要   访问。直接I / O应该在' logical_block_size'上对齐。   边界和' logical_block_size'的倍数。使用原生4K   设备(logical_block_size是4K)现在至关重要   应用程序执行直接I / O,它是设备的倍数   ' logical_block_size&#39 ;.这意味着没有的应用程序   执行4K对齐的I / O,但512字节对齐的I / O,将打破   原生4K设备。应用程序可以咨询设备的I / O限制"   确保他们使用正确对齐和大小的I / O. " I / O.   限制"通过sysfs和块设备ioctl公开   接口(另见:libblkid)。

     

sysfs接口

     

/ SYS /块// alignment_offset

     

/ SYS /块/// alignment_offset

     

/ SYS /块//队列/ physical_block_size

     

/ SYS /块//队列/ logical_block_size

     

/ SYS /块//队列/ minimum_io_size

     

/ SYS /块//队列/ optimal_io_size

请注意,直接IO的使用可能受到实际硬件和软件的限制。如RedHat文档中所述,物理设备限制很重要。

要在Linux上使用直接IO,需要使用O_DIRECT标志打开文件:

int fd = open( filename, O_RDONLY | O_DIRECT );

根据我的经验,在某些情况下,直接IO可以使IO性能提高20-30%。这些情况通常涉及在非常快的文件系统上向/从文件传输大量数据,而应用程序不执行任何调用或seek()次调用。

答案 2 :(得分:1)

对齐总是会产生一些性能问题。当你写(2)读(2)一个文件时,最好是你可以调整读数的限制来阻止对齐,因为你让内核做两个块读而不是一个。最糟糕的情况是在块边界上读取两个字节。假设您的块大小为1024字节,此代码为:

char var[2];
int fd;

fd = open("/etc/passwd", O_RDONLY);
lseek(fd, 1023UL, SEEK_SET);
read(fd, &var, sizeof var);

将使内核强制执行两次块读取(最多,因为之前已经缓存了块),仅用于两个字节 read(2)调用。

在内存的情况下,所有这些东西通常由 malloc(3)管理,并且,由于您没有因页面错误而失败,因此您不会受到任何性能损失(这是你没有任何标准库函数来获得对齐内存的原因,即使在需求分页虚拟系统中,只要你消耗内存,内核就会在页面中为你分配它。处理器虚拟内存系统使页面对齐几乎透明。只有在你有一个未对齐的内存访问的情况下(假设你访问一个32位整数访问错位 - 不可能 - 到两个页面,并且这两个页面已被内核换掉,你将不得不等待内核交换两页内存而不是一页 - 但这是不太可能发生的事情,编译器通常强制内部循环在页面边界之间不失败以最小化发生这种情况的可能性,并且你还有指令缓存到应对这些事情)

说这个,如果你有点对齐内存,有些地方你可以获得性能提升。我会试着向你展示一个这样的场景:

假设您需要动态管理许多小型结构(假设长16字节),并计划使用 malloc()管理它们。 malloc(3)管理内存,包括分配的每个内存块中的标头(假设此标头长度为8个字节),使内存开销比理想值高50%。如果你安排以64个结构的块为单位获取内存,那么每个64*16 = 1024个字节只会得到其中一个标题(8字节)(大约只有8%)

要管理这个,你必须考虑知道所有这些结构属于哪个块(这样你可以在不使用时免费(3)块),你可以在两个中执行此操作方法:1.-使用指针(为每个结构大小添加4个字节 - 这是毫无意义的,因为你将为每个结构添加4个字节,再次损失25%的内存)指向chunck,或2.- *强制chunck对齐,因此可以从struct地址轻松计算块地址(您只需要将其余的division mod chunksize减去struct结构地址)来获取块地址。最后一种方法不会对定位chunck施加任何开销,但强制所有块的做法是块对齐(不是页面对齐)。

通过这种方式,您可以大大提高性能,因为您大大减少了 malloc(3)调用的数量以及分配少量内存所带来的内存浪费。

顺便说一句,malloc不会向操作系统询问每次调用时你要求的内存。它以类似于此处所述的方式分配内存,并且正常实现甚至不能再将已分配的内存返回给系统(在分配新内存之前重用已释放的内存)它控制对 sbrk(2)系统调用,这意味着如果您使用此系统调用,您将干扰malloc。

当您使用 shmat(2)系统调用时,Linux / unix将为您提供页面对齐的内存。请尝试阅读此文档和相关文档。