X86 理解clwb指令的性能和行为

X86 理解clwb指令的性能和行为,x86,intel,cpu-architecture,cpu-cache,persistent-memory,X86,Intel,Cpu Architecture,Cpu Cache,Persistent Memory,我试图了解clwb指令的读/写性能,并测试它在写入缓存线的情况下与我只读取它时的变化。我希望对于写案例,所花费的时间应该高于读案例。为了测试这一点,这里有一个小代码片段,我正在英特尔至强CPU(skylake)上运行,并使用非易失性内存(NVM)进行读写存储 /* nvm_alloc allocates memory on NVM */ uint64_t *array = (uint64_t *) nvm_alloc(pool, 512); uint64_t *p = &array[0]

我试图了解clwb指令的读/写性能,并测试它在写入缓存线的情况下与我只读取它时的变化。我希望对于写案例,所花费的时间应该高于读案例。为了测试这一点,这里有一个小代码片段,我正在英特尔至强CPU(skylake)上运行,并使用非易失性内存(NVM)进行读写存储

/* nvm_alloc allocates memory on NVM */
uint64_t *array = (uint64_t *) nvm_alloc(pool, 512);
uint64_t *p = &array[0];
/* separated p & q by the size of write unit in Optane (256B) */
uint64_t *q = &array[32];

uint64_t time_1 = 0;
uint64_t time_2 = 0;
uint64_t start;

volatile uint64_t x;
for(int i = 0; i < 1000000; i++)
{
        /* issues an mfence instruction */
        mfence();
        /* this is for the read case, bring p into cache */
        /* commented read case */
        //x = *p;
        /* this is for the write case, update cacheline containing p */
        *p = *p + 1;
        *q = *q + 1;
        /* rdtscp here to flush instruction pipeline */
        start = rdtscp();
        /* issue clwb on cacheline containing p */
        clwb(p);
        time_1 += rdtsc() - start;

        start = rdtsc();
        clwb(q);
        time_2 += rdtsc() - start;
}
/*nvm\u alloc在nvm上分配内存*/
uint64_t*数组=(uint64_t*)nvm_alloc(池,512);
uint64_t*p=&数组[0];
/*按Optane(256B)中写入单元的大小分隔p&q*/
uint64_t*q=&数组[32];
uint64时间=0;
uint64_t time_2=0;
uint64_t启动;
挥发性uint64_t x;
对于(int i=0;i<1000000;i++)
{
/*发布mfence指令*/
mfence();
/*这是在读的情况下,将p带到缓存中*/
/*注释读取案例*/
//x=*p;
/*这适用于写案例,更新包含p的缓存线*/
*p=*p+1;
*q=*q+1;
/*rdtscp在此刷新指令管道*/
start=rdtscp();
/*在包含p的缓存线上发出clwb*/
clwb(p);
时间_1+=rdtsc()-开始;
start=rdtsc();
clwb(q);
时间_2+=rdtsc()-开始;
}

由于clwb没有显式地逐出缓存线,读取的下一次迭代可能会从缓存本身提供服务。在写操作的情况下,在每次迭代中修改缓存线,然后发出clwb将其写回。然而,写的时间几乎等于读的时间,我无法理解。如果写入时间不包括将脏缓存线写回内存(或内存控制器)的时间

不幸的是,CLWB不能保证缓存线保持存在;在SKX上,它的运行方式与CLFLUSHOPT相同。引入存根支持可以说比让指令的未来用户检查CPU支持以避免SIGILL要好,尽管如此。@PeterCordes加上计时代码是错误的,因为
rdtsc
在编译时和运行时都没有与
clwb
一起排序。如果让
rdtsc
clwb
完成之前读取TSC,那是毫无意义的,你甚至都没有计时
clwb
退役,更不用说一路完成DRAM或任何它去的地方了。在发出
clwb
之后,您所要计时的就是执行
rdtsc
的顺序错误。甚至不是因为
rdtscp
只确保前面的指令是完整的;它也不会在完成读取时间之前停止后续指令的执行。您添加的位更糟糕:开始和停止都是无序的
rdtsc
指令。另外,为什么要使用两个背对背的rdtsc?只要读一遍时间,你说的“交错两个clwb操作”是什么意思?另外,Skylake处理器不支持Optane持久性记忆棒。也许您正在使用Cascade Lake(第二代SP)?您好,正如@HadiBrais已经提到的,Skylake处理器不支持Optane持久内存。另一方面,在Cascade lake体系结构上,使用NVM DIMM,您有两种配置模式:内存模式和应用程序直接模式。所以我想补充的是,在应用程序直接模式中,两条
clwb
指令很可能是相互排序的。我已经观察到,Clwb是按照以下对同一缓存线的写入操作进行排序的,这给出了与clflush类似的行为,这就是为什么假设可以交错两个Clwb可能不完全正确的原因。