软件预取手动指令合理的场景

2024-05-07

我读过有关 x86 和 x86-64 Intel 的内容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_prefetch内在是实现定义的。一般来说,每个提示都有其自己的含义。

_MM_HINT_T0将数据获取到所有级别的缓存以实现包容性缓存 以及独占缓存的最低级别缓存

_MM_HINT_T1hint 将数据拉入 L2 并 不进入L1d。如果有 L3 缓存_MM_HINT_T2提示可以做类似的事情

_MM_HINT_NTA,允许告诉处理器特殊对待预取的缓存行

那么有人可以描述一下examples什么时候使用这个指令?

那么如何正确选择提示呢?


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

  • Accessing memory is very expensive the first time.
    The first time a memory address1 is accessed is must be fetched from memory, it is then stored in the cache hierarchy2.
  • Accessing memory is inherently asynchronous.
    The CPU doesn't need any resource from the core to perform the lengthiest part of a load/store3 and thus it can be easily done in parallel with other tasks4.

由于上述原因,在实际需要数据之前尝试加载是有意义的,这样当代码实际需要数据时,就不必等待。
毫无意义的是,CPU 在寻找要做的事情时可以走得很远,但不能任意深入;所以有时需要程序员的帮助才能发挥最佳性能。

就其本质而言,缓存层次结构是微体系结构的一个方面,而不是体系结构(请参阅 ISA)。 Intel 或 AMD 无法对这些指令的作用提供强有力的保证。
此外,正确使用它们并不容易,因为程序员必须清楚每条指令可以占用多少个周期。 最后,最新的 CPU 越来越擅长隐藏内存延迟并降低延迟。
因此,一般来说,预取是熟练的汇编程序员的工作。

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

关于不同级别的预取,我的理解是不同级别(L1 - L4)对应不同数量的预取sharing and 污染的.

例如prefetch0如果执行指令的线程/核心与读取变量的线程/核心相同,则很好。
然而,这将占用所有缓存中的一行,最终驱逐其他可能有用的行。 例如,当您知道您肯定需要简短的数据时,您可以使用此功能。

prefetch1有利于使数据快速可供所有核心或核心组使用(取决于 L2 的共享方式),而不污染 L1。
如果您知道自己可以使用此功能may需要数据,或者在完成另一项任务(优先使用缓存)后需要它。
这不如将数据存储在 L1 中那么快,但比将数据存储在内存中要好得多。

prefetch2由于它在 L3 缓存中移动数据,因此可用于消除大部分内存访问延迟。
它不会污染 L1 或 L2,并且在核心之间共享,因此它非常适合罕见(但可能)代码路径使用的数据或为其他核心准备数据。

prefetchnta是最容易理解的,它是一个非时间性的移动。它避免了在每个缓存行中为仅访问一次的数据创建一个条目。

prefetchw/prefetchwnt1与其他核心行类似,但使该行独占并使别名该行的其他核心行无效。
基本上,它使写入速度更快,因为它处于 MESI 协议的最佳状态(用于缓存一致性)。

最后,可以增量地完成预取,首先移动到 L3,然后移动到 L1(仅针对需要它的线程)。

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


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

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

但我知道这些例子并不是很好。


1 Actually the granularity is "cache lines" not "addresses".
2 Which I assume you are familiar with. Shortly put: It, as present, goes from L1 to L3/L4. L3/L4 is shared among cores. L1 is always private per core and shared by the core's threads, L2 usually is like L1 but some model may have L2 shared across pairs of cores.
3 The lengthiest part is the data transfer from the RAM. Computing the address and initializing the transaction takes up resources (store buffer slots and TLB entries for example).
4 However any resource used to access the memory can become a critical issue as pointed out by @Leeor and proved by the Linux kernel developer https://lwn.net/Articles/444336/.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

软件预取手动指令合理的场景 的相关文章

  • 适合初学者的良好调试器教程[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有谁知道一个好的初学者教程 在 C 中使用调试器 我感觉自己好像错过了很多 我知道怎么做 单步执行代码并查看局部变量 虽然这常常给我带来问
  • 代码 GetAsyncKeyState(VK_SHIFT) & 0x8000 中的这些数字是什么?它们是必不可少的吗?

    我试图在按下按键的简单动作中找到这些数字及其含义的任何逻辑解释 GetAsyncKeyState VK SHIFT 0x8000 可以使用哪些其他值来代替0x8000它们与按键有什么关系 GetAsyncKeyState 根据文档返回 如果
  • 如何在C(Linux)中的while循环中准确地睡眠?

    在 C 代码 Linux 操作系统 中 我需要在 while 循环内准确地休眠 比如说 10000 微秒 1000 次 我尝试过usleep nanosleep select pselect和其他一些方法 但没有成功 一旦大约 50 次 它
  • 如何判断计算机是否已重新启动?

    我曾经使用过一个命令行 SMTP 邮件程序 作为试用版的限制 它允许您在每个 Windows 会话中最多接收 10 封电子邮件 如果您重新启动计算机 您可能还会收到 10 个以上 我认为这种共享软件破坏非常巧妙 我想在我的应用程序中复制它
  • 如何填充 ToolStripComboBox?

    我发现它很难将数据绑定到ToolStripComboBox 好像没有这个ValueMember and DisplayMember特性 怎么绑定呢 访问toolstripcombobox中包装的组合框并访问其ValueMember Disp
  • 类型约束

    我有以下类层次结构 class Header IEnumerable
  • 打破 ReadFile() 阻塞 - 命名管道 (Windows API)

    为了简化 这是一种命名管道服务器正在等待命名管道客户端写入管道的情况 使用 WriteFile 阻塞的 Windows API 是 ReadFile 服务器已创建启用阻塞的同步管道 无重叠 I O 客户端已连接 现在服务器正在等待一些数据
  • IQueryable 单元或集成测试

    我有一个 Web api 并且公开了一个端点 如下所示 api 假期 name name 这是 Web api 的控制器 get 方法 public IQueryable
  • 为什么这个二维指针表示法有效,而另一个则无效[重复]

    这个问题在这里已经有答案了 这里我编写了一段代码来打印 3x3 矩阵的对角线值之和 这里我必须将矩阵传递给函数 矩阵被传递给指针数组 代码可以工作 但问题是我必须编写参数的方式如下 int mat 3 以下导致程序崩溃 int mat 3
  • 在mysql连接字符串中添加应用程序名称/程序名称[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在寻找一种解决方案 在连接字符串中添加应用程序名称或程序名称 以便它在 MySQL Workbench 中的 客户端连接 下可见 SQL
  • 检测到严重错误 c0000374 - C++ dll 将已分配内存的指针返回到 C#

    我有一个 c dll 它为我的主 c 应用程序提供一些功能 在这里 我尝试读取一个文件 将其加载到内存 然后返回一些信息 例如加载数据的指针和内存块的计数到 c Dll 成功将文件读取到内存 但在返回主应用程序时 程序由于堆损坏而崩溃 检测
  • 在屏幕上获取字符

    我浏览了 NCurses 函数列表 似乎找不到返回已打印在屏幕上的字符的函数 每个字符单元格中存储的字符是否有可访问的值 如果没有的话Windows终端有类似的功能吗 我想用它来替换屏幕上某个值的所有字符 例如 所有a s 具有不同的特征
  • 使 Guid 属性成为线程安全的

    我的一个类有一个 Guid 类型的属性 该属性可以由多个线程同时读写 我的印象是对 Guid 的读取和写入不是原子的 因此我应该锁定它们 我选择这样做 public Guid TestKey get lock testKeyLock ret
  • Unity:通过拦截将两个接口注册为一个单例

    我有一个实现两个接口的类 我想对该类的方法应用拦截 我正在遵循中的建议Unity 将两个接口注册为一个单例 https stackoverflow com questions 1394650 unity register two inter
  • 实体框架中的“it”是什么

    如果以前有人问过这个问题 请原谅我 但我的任何搜索中都没有出现 它 我有两个数据库表 Person 和 Employee 对每个类型的表进行建模 例如 Employee is a Person 在我的 edmx 设计器中 我定义了一个实体
  • 堆栈是向上增长还是向下增长?

    我在 C 中有这段代码 int q 10 int s 5 int a 3 printf Address of a d n int a printf Address of a 1 d n int a 1 printf Address of a
  • 使用 C 在 OS X 中获取其他进程的 argv

    我想获得其他进程的argv 例如ps 我使用的是在 Intel 或 PowerPC 上运行的 Mac OS X 10 4 11 首先 我阅读了 ps 和 man kvm 的代码 然后编写了一些 C 代码 include
  • GCC 的“-Wl,option”和“-Xlinker option”语法之间有区别吗?

    我一直在查看一些配置文件 并且看到它们都被使用 尽管在不同的体系结构上 如果您在 Linux 机器上使用 GCC 将选项传递给链接器的两种语法之间有区别吗 据我所知 阅读 GCC 手册时 他们的解释几乎相同 From man gcc Xli
  • 如何使用 C++11 using 语法键入定义函数指针?

    我想写这个 typedef void FunctionPtr using using 我该怎么做呢 它具有类似的语法 只不过您从指针中删除了标识符 using FunctionPtr void 这是一个Example http ideone
  • OpenCV SIFT 描述符关键点半径

    我正在深入研究OpenCV的SIFT描述符提取的实现 https github com Itseez opencv blob master modules nonfree src sift cpp 我发现了一些令人费解的代码来获取兴趣点邻域

随机推荐