小编Ana*_*ani的帖子

英特尔的 CLWB 指令使缓存行无效

我正在尝试为英特尔的clwb指令找到不会使缓存行无效的配置或内存访问模式。我正在使用 NVDIMM 对 Intel Xeon Gold 5218 处理器进行测试。Linux 版本是 5.4.0-3-amd64。我尝试使用 Device?DAX 模式并直接将此字符设备映射到地址空间。我还尝试将此非易失性内存添加为新的 NUMA 节点,并使用numactl --membind命令将内存绑定到它。在这两种情况下,当我使用clwb缓存地址时,它都会被驱逐。我正在观察 PAPI 硬件计数器的驱逐,并禁用预取器。

这是我正在测试的一个简单循环。array 和 tmp 变量,都被声明为 volatile,所以加载是真正执行的。

for(int i=0; i < arr_size; i++){
    tmp = array[i];
    _mm_clwb(& array[i]);
    _mm_mfence();
    tmp = array[i];    
}
Run Code Online (Sandbox Code Playgroud)

两次读取都会导致缓存未命中。

我想知道是否还有其他人试图检测是否有某种配置或内存访问模式会在缓存中留下缓存行?

x86 intel cpu-architecture cpu-cache persistent-memory

8
推荐指数
1
解决办法
758
查看次数

为什么_mm_mfence()会产生ALL_LOADS perf事件的计数?

我正在测试一些内在操作的行为.当我注意到_mm_mfence()从用户空间发出加载指令时,我感到很惊讶,但它不计入L1数据缓存 - 未命中,命中或填充缓冲区命中.我正在使用papi的本机事件(如MEM_INST_RETIRED和MEM_LOAD_RETIRED)来读取性能计数器.这段代码:

 for(int i=0; i < 1000000; i++){

    _mm_mfence();

  }
Run Code Online (Sandbox Code Playgroud)

计数ALL_LOADS:737030,L1_HIT:99,L1_MISS:10,FB_HIT:25.虽然没有mfence,读取计数器的开销是这样的:ALL_LOADS:125,L1_HIT:94,L1_MISS:11,FB_HIT:24

我查了一下,sfence和lfence没有这种影响.我正在使用-O3进行编译.从编译文件我想它调用__builtin_ia32_mfence函数,但我找不到它.

我一般都了解_mm_mfence()的作用以及我们使用它的原因,但现在问题更多的是它是如何工作的.如果有人能够解释或给出任何相关文章来理解这种行为,那将是很棒的.

c x86 intrinsics papi perf

6
推荐指数
1
解决办法
163
查看次数

为什么在L1缓存中将MFENCE与存储指令块预取一起使用?

我有一个64字节大小的对象:

typedef struct _object{
  int value;
  char pad[60];
} object;
Run Code Online (Sandbox Code Playgroud)

在主要我正在初始化对象数组:

volatile object * array;
int arr_size = 1000000;
array = (object *) malloc(arr_size * sizeof(object));

for(int i=0; i < arr_size; i++){
    array[i].value = 1;
    _mm_clflush(&array[i]);
}
_mm_mfence();
Run Code Online (Sandbox Code Playgroud)

然后再次遍历每个元素。这是我正在为以下事件计数的循环:

int tmp;
for(int i=0; i < arr_size-105; i++){
    array[i].value = 2;
    //tmp = array[i].value;
     _mm_mfence();
 }
Run Code Online (Sandbox Code Playgroud)

拥有mfence在这里没有任何意义,但是我在捆绑其他东西,无意间发现,如果我有存储操作,而没有mfence,我将收到50万次RFO请求(以papi L2_RQSTS.ALL_RFO事件衡量),这意味着另外50万L1命中,在需求之前预取。但是,包含mfence会导致一百万个RFO请求,从而产生RFO_HIT,这意味着仅在L2中预取了缓存行,不再在L1缓存中预取了。

除了英特尔文档以某种方式另有说明的事实之外:“可以在执行MFENCE指令之前,之中或之后将数据推测性地带入缓存”。我检查了加载操作。如果没有mfence,我最多可获得2000 L1命中率,而如果具有mfence,则我最多可获得100万L1命中率(以papi MEM_LOAD_RETIRED.L1_HIT事件衡量)。高速缓存行在L1中预取以用于加载指令。

因此,不应该包含mfence块预取。存储和加载操作几乎都需要花费相同的时间-不需5-6毫秒,而需20毫秒。我经历了有关mfence的其他问题,但未提及预取对它的预期行为,我没有看到足够好的理由或解释,为什么它仅使用存储操作会阻止L1缓存中的预取。还是我可能缺少某些功能描述?

我正在Skylake微体系结构上进行测试,但是与Broadwell进行了核对,并获得了相同的结果。

performance x86 intel prefetch memory-barriers

6
推荐指数
1
解决办法
106
查看次数

启用/禁用硬件锁定清除

我正在使用glibc 2.24版本。它具有通过事务同步扩展(例如_xbegin()和_xend())用于pthread_mutex_lock实现的锁省略路径。我认为硬件应该支持锁定删除,因为hle CPU标志用于硬件锁定删除。我正在使用的处理器是采用Skylake架构的Intel®Xeon®Gold 6130。

首先,我想禁用Lock Elision,但是当我运行使用pthread_mutex_lock且具有perf stat -T来监视事务周期的程序时,我得到0。我认为这意味着pthread_mutex_lock根本不使用事务路径。谷歌搜索之后,我发现可能有必要先使用export GLIBC_TUNABLES = glibc.elision.enable = 1来启用锁定清除,但是在此步骤之后,我仍然看不到使用perf进行任何事务。

另一方面,当我包含_xbegin()时;和_xend(); 直接在此过程中,我获得了一些具有perf stat -T的交易周期,这应该意味着我希望能找到与perf匹配的计数器。

因此,有关如何启用锁定清除的任何建议都将有所帮助。还是我检查不正确?

TSX的更新我在主要功能中使用了这两条指令,就像这样:

_xbegin();
_xend();
Run Code Online (Sandbox Code Playgroud)

我不确定它需要哪个库,我已经包含了数十个。对于编译,我使用以下标志:-O3 -march = native -lpthread与该示例相关。

对于锁,我有互斥锁:

pthread_mutex_t * mutex;
mutex = (pthread_mutex_t *) malloc(10 * sizeof(pthread_mutex_t));
for(int k=0; k<10; k++){
    pthread_mutex_init(&mutex[k], NULL);
  }
Run Code Online (Sandbox Code Playgroud)

也许为了省略,我应该以不同的方式初始化它?

c x86 glibc intel tsx

6
推荐指数
1
解决办法
328
查看次数

_mm_lfence()时间开销是不确定的?

我试图确定读取元素所需的时间,以确保它是缓存命中或缓存未命中.为了阅读顺序我使用_mm_lfence()函数.我得到了意想不到的结果,经过检查后,我看到lfence函数的开销不确定.所以我正在执行程序,在例如100 000次迭代的循环中测量这种开销.我得到一次迭代的超过1000个时钟周期的结果,下次是200.这可能是lfence函数开销之间存在这种差异的原因,如果它是如此不可靠,我怎样才能正确判断缓存命中和缓存未命中的延迟?我试图使用与此帖相同的方法:使用时间戳计数器进行内存延迟测量

给出不可靠结果的代码是这样的:

for(int i=0; i < arr_size; i++){
  _mm_mfence();
  _mm_lfence();
   t1 = __rdtsc();
  _mm_lfence();
  _mm_lfence();
   t2 = __rdtsc();
  _mm_lfence();

   arr[i] = t2-t1;
}
Run Code Online (Sandbox Code Playgroud)

arr中的值在不同的范围内变化,arr_size为100 000.

c performance x86 intrinsics rdtsc

5
推荐指数
1
解决办法
150
查看次数

Rust 中的链向量和 IntoIterator 元素

我想在 Rust 中编写一个函数,该函数将返回由起始整数、所有中间整数和结束整数组成的向量。它应该持有的断言是这样的:

assert_eq!(intervals(0, 4, 1..4), vec![0, 1, 2, 3, 4]);
Run Code Online (Sandbox Code Playgroud)

提示是对迭代器使用链方法。函数声明是预定义的,我是用一种方式实现的,代码如下:

pub fn intervals< I>(start: u32, end: u32, intermediate: I) -> Vec<u32>
where
    I: IntoIterator<Item = u32>,
{
    let mut a1 = vec![];
    a1.push(start);

    let inter: Vec<u32> = intermediate.into_iter().collect();
    let mut iter : Vec<u32> = a1.iter().chain(inter.iter()).map(|x| *x).collect();

    iter.push(end);
    return iter;
}
Run Code Online (Sandbox Code Playgroud)

但我非常确信这并不是实现此目的的最佳方式。我确信我在中间两行做了很多不必要的事情。我尝试像这样直接使用中间体:

let mut iter: Vec<u32> = a1.iter().chain(intermediate).map(|x| *x).collect();
Run Code Online (Sandbox Code Playgroud)

但是我收到链方法的这个错误,我不知道如何解决它:

type mismatch resolving <I as std::iter::IntoIterator>::Item==&u32, 
expected u32, found &u32
Run Code Online (Sandbox Code Playgroud)

我是 Rust 的超级新手,所以任何建议都有助于了解在此处使用中间参数的正确方法是什么。

iterator rust

1
推荐指数
1
解决办法
640
查看次数