与理解' _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]上,如果没有可用的预取,则不等待填充缓冲区。据推测,预取只是被丢弃。因此,大量的预取失败的行为与Intel完全不同:无论预取失败与否,它们都会很快完成,但是许多相关的行实际上不会被提取。