gpt4 book ai didi

c++ - 软件预取手册说明合理时的方案

转载 作者:太空狗 更新时间:2023-10-29 20:21:55 24 4
gpt4 key购买 nike

我已经在x86和x86-64上阅读过有关此信息,英特尔gcc提供了特殊的预取指令:

#include <xmmintrin.h>
enum _mm_hint
{
_MM_HINT_T0 = 3,
_MM_HINT_T1 = 2,
_MM_HINT_T2 = 1,
_MM_HINT_NTA = 0
};
void _mm_prefetch(void *p, enum _mm_hint h);

程序可以在任何
程序中的指针。和 _mm_prefetch一起使用的不同提示
内在的是实现定义的。普遍说的是,每个提示都有其自己的含义。

_MM_HINT_T0 fetches data to all levels of the cache for inclusive caches and to the lowest level cache for exclusive caches

_MM_HINT_T1 hint pulls the data into L2 and not into L1d. If there is an L3 cache the _MM_HINT_T2 hints can do something similar for it

_MM_HINT_NTA, allows telling the processor to treat the prefetched cache line specially



那么有人可以在使用此指令时描述示例吗?

以及如何正确选择提示?

最佳答案

预取的想法基于以下事实:

  • 第一次访问内存非常昂贵。
    必须从内存中提取第一次访问内存地址1的地址,然后将其存储在高速缓存层次结构2中。
  • 访问内存本质上是异步的。
    CPU不需要内核提供任何资源来执行加载/存储3中最长的部分,因此可以轻松地与其他任务4并行完成。

  • 由于上述原因,在实际需要加载之前尝试加载是有意义的,这样,当代码实际需要数据时,就不必等待。
    寻找工作要做时,CPU可以走得很远,但又不至于太深,这是毫无值(value)的。因此有时需要程序员的帮助才能实现最佳性能。

    从本质上说,缓存层次结构是微体系结构的一个方面,而不是体系结构(读取ISA)。英特尔或AMD无法对这些说明的作用提供有力保证。
    此外,正确使用它们并不容易,因为程序员必须清楚每个指令可以占用多少个周期。
    最后,最新的CPU在隐藏和减少内存延迟方面越来越好。
    因此,通常,预取是熟练的汇编程序员的工作。

    也就是说,唯一可能的情况是每次调用时一段代码的时间必须保持一致。
    例如,如果您知道中断处理程序总是更新状态并且它必须尽可能快地执行,那么在设置使用此类中断的硬件时,值得预取状态变量。

    关于不同的预取级别,我的理解是不同的级别(L1-L4)对应于不同数量的共享和污染。

    例如,如果执行指令的线程/内核与将读取变量的线程/内核相同,则 prefetch0很好。
    但是,这将在所有缓存中占一行,最终驱逐其他可能有用的行。
    例如,当您知道肯定会需要数据时,可以使用此方法。
    prefetch1可以使数据快速提供给所有核心或核心组(取决于L2的共享方式)而不会污染L1。
    如果您知道可能需要数据,或者在完成另一项任务(使用缓存优先)之后需要数据,则可以使用此方法。
    这不像将数据存储在L1中那样快,但是要好于将其存储在内存中。
    prefetch2可用于消除大部分内存访问延迟,因为它会移动L3缓存中的数据。
    它不会污染L1或L2,并且在内核之间共享,因此它对于少数(但可能)代码路径使用的数据或为其他内核准备数据很有用。
    prefetchnta是最容易理解的,它是非临时性的。这样可以避免在每个高速缓存行中为只能访问一次的数据创建一个条目。
    prefetchw/prefetchwnt1与其他代码相似,但使该行排他,并使其他以此为别名的核心行无效。
    基本上,它可以使写入速度更快,因为它处于MESI协议(protocol)的最佳状态(用于高速缓存一致性)。

    最后,可以通过先进入L3,然后进入L1(仅适用于需要它的线程)来逐步进行预取。

    简而言之,每条指令都可以让您决定污染,共享和访问速度之间的折衷。
    由于所有这些都需要非常仔细地跟踪高速缓存的使用(您需要知道它不值得在L1中创建和输入,而应该在L2中),因此只能将其用于特定的环境。
    在现代操作系统中,无法跟踪缓存,您可以进行预取只是为了发现您的量子已过期,并且您的程序被另一个驱逐刚刚加载的行的程序所替代。

    至于一个具体的例子,我有点想法。
    过去,我必须尽可能一致地衡量一些外部事件的发生时间。
    我使用和中断来定期监视事件,在这种情况下,我预取了中断处理程序所需的变量,从而消除了第一次访问的等待时间。

    预取的另一种非常规的用法是将数据移入缓存。
    如果要测试缓存系统或依靠缓存将设备从内存中取消映射,以使数据保留更长的时间,此功能很有用。
    在这种情况下,转移到L3就足够了,并不是所有的CPU都拥有L3,所以我们可能需要转移到L2。

    我理解这些示例并不是很好。

    1实际上,粒度是“缓存行”而不是“地址”。
    2我以为您熟悉。简而言之:它目前从L1到L3/L4。 L3/L4在内核之间共享。 L1始终是每个内核专用的,并由内核的线程共享。L2通常类似于L1,但是某些模型可能会在成对的内核之间共享L2。
    3最长的部分是从RAM传输数据。计算地址并初始化事务会占用资源(例如,存储缓冲区插槽和TLB条目)。
    4但是,@ Leeor和 proved by the Linux kernel developer指出,用于访问内存的任何资源都可能成为关键问题。

    关于c++ - 软件预取手册说明合理时的方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40549809/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com