使用__builtin_prefetch(..., 1)内部函数(准备写入时的预取)完成后期预取的成本是多少?也就是说,在需求加载或写入需要它之前没有到达L1缓存的预取?
例如
void foo(std::uint8_t* line) {
__builtin_prefetch(line + std::hardware_constructive_interference_size, 1);
auto next_line = calculate_address_of_next_line(line);
auto result = transform(line);
write(next_line, result)
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,如果成本transform低于预取,那么这个代码最终会比没有预取的效率低吗?关于缓存预取的维基百科文章讨论了for循环的最佳步幅,但未提及该场景中次优预取的影响(例如,如果k太低会发生什么?).
这是否足够流水线以至于次优预取无关紧要?出于这个问题的目的,我只考虑Intel x86(Broadwell时代的处理器).
我的目标是将静态结构加载到 L1D 缓存中。之后使用这些结构成员执行一些操作,并在完成操作后运行invd以丢弃所有修改过的缓存行。所以基本上我想在缓存内创建一个安全的环境,这样在缓存内执行操作时,数据就不会泄漏到 RAM 中。
为此,我有一个内核模块。我在结构的成员上放置了一些固定值。然后我禁用抢占,禁用所有其他 CPU(当前 CPU 除外)的缓存,禁用中断,然后使用__builtin_prefetch()将我的静态结构加载到缓存中。之后,我用新值覆盖之前放置的固定值。之后,我执行invd(清除修改后的缓存行),然后为所有其他 CPU 启用缓存,启用中断并启用抢占。我的理由是,当我在原子模式下这样做时,INVD将删除所有更改。从原子模式回来后,我应该看到我之前放置的原始固定值。然而,这并没有发生。退出原子模式后,我可以看到用于覆盖先前放置的固定值的值。这是我的模块代码,
奇怪的是,重新启动PC后,我的输出发生了变化,我只是不明白为什么。现在,我根本没有看到任何变化。我正在发布完整的代码,包括@Peter Cordes 建议的一些修复,
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("test INVD");
static struct CACHE_ENV{
unsigned char in[128];
unsigned char out[128];
}cacheEnv __attribute__((aligned(64)));
#define cacheEnvSize (sizeof(cacheEnv)/64)
//#define change "Hello"
unsigned char change[]="hello";
void disCache(void *p){
__asm__ __volatile__ (
"wbinvd\n"
"mov %%cr0, %%rax\n\t"
"or $(1<<30), %%eax\n\t"
"mov %%rax, %%cr0\n\t"
"wbinvd\n"
::
:"%rax"
);
printk(KERN_INFO "cpuid %d --> cache disable\n", …Run Code Online (Sandbox Code Playgroud) 在一次采访中,有人问我是否知道x64指令的行为有所不同,具体取决于使用的CPU,我在任何地方找不到任何文档,有谁知道这些指令是什么以及为什么会这样?
x86 ×4
assembly ×2
c ×2
prefetch ×2
c++ ×1
cpu-cache ×1
gcc ×1
linux-kernel ×1
optimization ×1
performance ×1
x86-64 ×1