【问题标题】:Get memory granularity of a processor获取处理器的内存粒度
【发布时间】:2020-06-07 13:12:29
【问题描述】:

如何在 C 中获取 CPU 的memory granularity

假设我想分配一个数组,其中所有元素都正确对齐内存。我可以将每个元素填充到一定大小 N 来实现这一点。我如何知道 N 的值?

注意:我正在尝试创建一个内存池,其中每个插槽都是内存对齐的。任何建议将不胜感激。

【问题讨论】:

  • 编译器和内存管理器(malloc 等)会为你做这些。
  • 有趣的问题与 C 的基础之一相冲突:可移植性。该代码被认为与处理器架构无关,并且所有端口特定的调整都是在编译器端口相关代码中完成的,正如@PaulOgilvie 所说。无论如何,在某些情况下,它可能是对机器特定代码的真正要求,例如在极端效率成就问题或电路板开发中。不幸的是,C 语言对此没有任何标准支持。您必须使用 #if/#else/#endif 的旧条件编译。
  • 您的问题不清楚。有word对齐(针对单个对象),有page对齐(针对MMU/VM),还有cache对齐(多级)。所有这些都可以存在或不存在,具体取决于架构。

标签: c memory-management memory-alignment granularity


【解决方案1】:

理论上

如何在 C 中获取 CPU 的内存粒度?

首先,您阅读了指令集架构手册。它可能指定某些指令需要某些对齐,甚至某些指令中的寻址形式不能表示未对齐的地址。它可以指定其他关于对齐的属性。

其次,您阅读处理器手册。它可以指定性能特征(例如支持未对齐的加载或存储,但可能比对齐的加载或存储更慢或使用更多资源),并且可以指定指令集架构允许的各种选项。

第三,您阅读了操作系统文档。一些体系结构允许操作系统选择与对齐相关的功能,例如是否使未对齐的加载和存储失败或受支持,尽管性能比对齐的加载或存储慢。操作系统文档应该有这些信息。

实践中

对于许多编程情况,您需要了解的不是 CPU 的“内存粒度”,而是您使用的 C 实现(或您使用的任何语言)的对齐要求。而且,在大多数情况下,您不需要直接了解对齐要求,而只需要遵循有关管理对象的语言规则——使用具有声明类型的对象,不要使用强制转换在不兼容类型之间转换指针,超出特定规则允许的范围它,使用由malloc 提供的适当对齐的内存,而不是调整你自己的字节指针,等等。遵循这些规则将为您的程序中的对象提供良好的对齐。

在 C 中,当您定义一个数组时,元素大小将自动成为 C 实现对其对齐所需的大小。例如,long double x[100]; 可以为每个数组元素使用 16 个字节,即使硬件对 long double 仅使用 10 个字节。或者,对于您定义的任何struct foo,编译器将根据需要在结构中自动包含填充以提供所需的对齐,并且任何数组struct foo x[100]; 将已经包含该填充。 sizeof(struct foo) 将与 sizeof x[0] 相同,因为每个结构对象都有内置的填充,即使只是针对单个结构对象,而不仅仅是针对数组中的元素。

当您确实需要知道 C 实现对类型所需的对齐方式时,可以使用 C 的 _Alignof 运算符。表达式_Alignof(type) 提供type 所需的对齐方式。

其他

…正确的内存对齐。

正确对齐是度数问题:

  • 处理器支持的内容可能决定您的程序是否有效。不正确的对齐会导致您的程序陷入陷阱。
  • 对于单独的加载和存储而言,什么是有效的可能会影响程序运行的速度。不正确的对齐会导致您的程序执行更慢。
  • 在某些性能关键的情况下,与缓存和内存映射功能相关的对齐也会影响性能。

【讨论】:

  • 结构的元素会自动对齐,但数组元素不会自动对齐。考虑一个大小为 12 的 struct foo 和 foo A[10] 的数组。 A[0] 已对齐,但 A[1] 未对齐。 A[1] 的地址从 A[0] + 12 开始。
  • @PrabhakarTayenjam:结构在末尾包含填充以提供结构数组所需的对齐方式。如果在 C 实现中结构的大小为 12 字节,那是因为在 C 实现中结构及其内部成员最多需要 4 字节对齐,并且该结构的数组中的每个元素都将满足该要求它们相隔 12 个字节。如果结构的某些成员需要超过 8 字节或 16 字节对齐,那么结构的大小将不是 12 字节;编译器将添加填充使其成为 16 字节。
【解决方案2】:

简答

使用 64 字节。

长答案

数据以称为缓存行的单元从内存中加载和存储。如果您的程序仅在缓存行中加载部分数据,则整行将被加载到 CPU 缓存中。或许更重要的是,多核 CPU 中的 algorithm used for moving data between cores 在完整的高速缓存行上运行;将数据与缓存线对齐可以避免 错误共享,即缓存线在内核之间反弹的情况,因为它包含由不同线程操作的数据。

过去的情况是缓存行取决于架构,从 16 字节到 512 字节不等。但是,当前所有处理器(Intel、AMD、ARM、MIPS)都使用 64 字节的缓存线。

【讨论】:

  • 另一个稍微不相关的问题:如果一个数据不适合一个缓存行并且需要两个,cpu 必须读取两次。所以数据是否对齐并不重要。它不会影响性能(从性能的角度来看)。是这样吗?
  • 不完全是,一旦访问缓存中的未对齐数据,您需要支付少量罚款。
【解决方案3】:

这在很大程度上取决于您使用的 cpu 微架构。

在很多情况下,操作符的内存地址应该是操作数大小的倍数,否则执行会很慢(甚至可能抛出异常)。

但也有一些 CPU 根本不关心内存中操作数的特定对齐方式。

通常,C 编译器会为您处理这些细节。但是,您应该确保编译器采用正确的目标(微)架构,例如通过使用正确的编译器标志指定它(gcc 上的-march=?)。

【讨论】:

    猜你喜欢
    • 2018-07-14
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 2018-09-22
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多