_mm_prefetch 是异步的吗? Profiling显示上面有很多循环
Is _mm_prefetch asynchronous? Profiling shows a lot of cycles on it
与Understanding `_mm_prefetch`相关。
我明白 _mm_prefetch()
导致请求的值被提取到处理器的缓存中,并且我的代码将被执行 而 一些预取。
但是,我的 VS2017 分析器指出 5.7% 花在了访问我的 cache
的线上,8.63% 花在了 _mm_prefetch
线上。探查器错了吗?如果我正在等待获取数据,我需要它做什么?我可以在下一个函数调用中等待,当我需要它时...
另一方面,总体时序显示该预取调用的显着优势。
所以问题是:数据是异步获取的吗?
附加信息。
我有多个缓存,用于各种密钥宽度,最多 32 位密钥(我目前正在分析)。对缓存的访问和预取被提取到单独的 __declspec(noinline)
函数中,以将它们与周围的代码隔离。
uint8_t* cache[33];
__declspec(noinline)
uint8_t get_cached(uint8_t* address) {
return *address;
}
__declspec(noinline)
void prefetch(uint8_t* pcache) {
_mm_prefetch((const char*)pcache, _MM_HINT_T0);
}
int foo(const uint64_t seq64) {
uint64_t key = seq64 & 0xFFFFFFFF;
uint8_t* pcache = cache[32];
int x = get_cached(pcache + key);
key = (key * 2) & 0xFFFFFFFF;
pcache += key;
prefetch(pcache);
// code that uses x
}
探查器显示 int x = get_cached(pcache + key);
行为 7.22%,prefetch(pcache);
为 8.97%,而周围代码显示每行 0.40-0.45%。
基本上 乱序 CPU 上的所有内容 按照您描述的方式是“异步的”(实际上,运行 并行和乱序)。从这个意义上说,预取与常规加载并没有什么不同,后者也可能 运行 乱序或与其他指令“异步”。
理解后,预取的确切行为取决于硬件,但据我观察:
在 Intel 上,预取指令可以在 数据到达之前 退出。因此,成功开始执行的预取指令之后不会阻塞 CPU 管道。但是,请注意“成功执行”:预取指令仍然需要一个行填充缓冲区 (MSHR),如果它在 L1 中未命中,并且在 Intel 上它将等待该资源(如果不可用)。因此,如果您并行发出大量预取未命中,它们最终会等待填充缓冲区,这使得它们在那种情况下的行为与原始加载非常相似。
在 AMD Zen [2] 上,如果 none 可用,预取 不会 等待填充缓冲区。据推测,预取只是被丢弃了。因此,大量预取未命中的行为与 Intel 截然不同:它们将很快完成,无论是否未命中,但实际上不会获取许多关联行。
与Understanding `_mm_prefetch`相关。
我明白 _mm_prefetch()
导致请求的值被提取到处理器的缓存中,并且我的代码将被执行 而 一些预取。
但是,我的 VS2017 分析器指出 5.7% 花在了访问我的 cache
的线上,8.63% 花在了 _mm_prefetch
线上。探查器错了吗?如果我正在等待获取数据,我需要它做什么?我可以在下一个函数调用中等待,当我需要它时...
另一方面,总体时序显示该预取调用的显着优势。
所以问题是:数据是异步获取的吗?
附加信息。
我有多个缓存,用于各种密钥宽度,最多 32 位密钥(我目前正在分析)。对缓存的访问和预取被提取到单独的 __declspec(noinline)
函数中,以将它们与周围的代码隔离。
uint8_t* cache[33];
__declspec(noinline)
uint8_t get_cached(uint8_t* address) {
return *address;
}
__declspec(noinline)
void prefetch(uint8_t* pcache) {
_mm_prefetch((const char*)pcache, _MM_HINT_T0);
}
int foo(const uint64_t seq64) {
uint64_t key = seq64 & 0xFFFFFFFF;
uint8_t* pcache = cache[32];
int x = get_cached(pcache + key);
key = (key * 2) & 0xFFFFFFFF;
pcache += key;
prefetch(pcache);
// code that uses x
}
探查器显示 int x = get_cached(pcache + key);
行为 7.22%,prefetch(pcache);
为 8.97%,而周围代码显示每行 0.40-0.45%。
基本上 乱序 CPU 上的所有内容 按照您描述的方式是“异步的”(实际上,运行 并行和乱序)。从这个意义上说,预取与常规加载并没有什么不同,后者也可能 运行 乱序或与其他指令“异步”。
理解后,预取的确切行为取决于硬件,但据我观察:
在 Intel 上,预取指令可以在 数据到达之前 退出。因此,成功开始执行的预取指令之后不会阻塞 CPU 管道。但是,请注意“成功执行”:预取指令仍然需要一个行填充缓冲区 (MSHR),如果它在 L1 中未命中,并且在 Intel 上它将等待该资源(如果不可用)。因此,如果您并行发出大量预取未命中,它们最终会等待填充缓冲区,这使得它们在那种情况下的行为与原始加载非常相似。
在 AMD Zen [2] 上,如果 none 可用,预取 不会 等待填充缓冲区。据推测,预取只是被丢弃了。因此,大量预取未命中的行为与 Intel 截然不同:它们将很快完成,无论是否未命中,但实际上不会获取许多关联行。