【问题标题】:Memory loads experience different latency on the same core内存负载在同一内核上经历不同的延迟
【发布时间】:2022-07-23 04:17:13
【问题描述】:

我试图在 C 中实现一个基于缓存的隐蔽通道,但发现一些奇怪的东西。发送方和接收方之间的物理地址通过使用映射到具有 MAP_SHARED 选项的同一文件的 mmap() 调用来共享。下面是发送方进程的代码,它从缓存中刷新地址以传输 1,并将地址加载到缓存中以传输 0。它还测量了两种情况下的加载延迟:

// computes latency of a load operation
static inline CYCLES load_latency(volatile void* p) {
        CYCLES t1 = rdtscp();
        load = *((int *)p);
        CYCLES t2 = rdtscp();
        return (t2-t1);
}
void send_bit(int one, void *addr) {

    if(one) {
        clflush((void *)addr);
        load__latency = load_latency((void *)addr);
        printf("load latency = %d.\n", load__latency);
        clflush((void *)addr);
    }
    else {
        x = *((int *)addr);
        load__latency = load_latency((void *)addr);
        printf("load latency = %d.\n", load__latency);
    }
}   
int main(int argc, char **argv) {
    if(argc == 2)
    {
        bit = atoi(argv[1]);
    }
    // transmit bit
    init_address(DEFAULT_FILE_NAME);    
    send_bit(bit, address);
    return 0;
}

load 操作在由同一进程发出时大约需要 0 - 1000 个周期(在缓存命中和缓存未命中期间)。

接收程序加载相同的共享物理地址并测量缓存命中或缓存未命中期间的延迟,其代码如下所示:

int main(int argc, char **argv) {

    init_address(DEFAULT_FILE_NAME);
    rdtscp();
    load__latency = load_latency((void *)address);
    printf("load latency = %d\n", load__latency);

    return 0;
}

(我在发送者进程终止后手动运行接收者)

但是,与第一种情况相比,在这种情况下观察到的延迟有很大不同。 load 操作大约需要 5000-1000 个周期。

通过使用taskset 命令,这两个进程都已固定到同一个core-id。因此,如果我没记错的话,在缓存命中期间,两个进程都会在缓存命中时经历 L1 缓存的加载延迟,而在缓存未命中时会遇到 DRAM 的加载延迟。然而,这两个过程经历了非常不同的延迟。这个观察结果可能是什么原因,我怎样才能让两个进程经历相同的延迟?

【问题讨论】:

  • 你在什么硬件上测试过这个?鉴于rdtscp,显然有些x86的味道。还有,你为什么要在load = *((int *)p);中抛弃volatile?为什么将加载结果分配给定时区域内的全局(?)变量?那可能是软页面错误。事实上,假设您没有使用 MAP_POPULATE 或 mlock,那么对共享数据的初始访问也是如此。这看起来不像minimal reproducible example,但是如果您只对每个 mmap 或每次运行整个程序进行一次测量,那么可能会发生页面错误。
  • 另请注意,除非您立即运行第二个进程(例如,从相同的 shell 命令),否则操作系统将有机会将该内核置于深度睡眠状态。至少在 Intel CPU 上,这会清空 L1d 和 L2,因此它可以在更深的 C 状态下关闭它们。可能也是 TLB。
  • 非常感谢!该程序确实在测量页面错误的延迟。我使用了 MAP_POPULATE,它现在似乎可以工作了!

标签: caching process operating-system cpu-architecture micro-architecture


【解决方案1】:

mmaped 区域的初始访问将出现页面错误(内核的延迟映射/分配),除非您使用mmap(MAP_POPULATE)mlock,或者先触摸页面的其他缓存行。

如果您只对每个 mmap 或每次运行整个程序进行一次测量,您可能正在计时页面错误。

(此外,您似乎没有做任何事情来预热 CPU 频率,因此一次核心周期可能是许多参考周期。L3 未命中的某些时间在内存时钟周期方面是固定的,但另一个它的一部分随核心/非核心时钟扩展。)


另外请注意,除非您立即运行第二个进程(例如,从同一个 shell 命令),否则操作系统将有机会让该内核进入深度睡眠。至少在 Intel CPU 上,这会清空 L1d 和 L2,因此它可以在更深的 C 状态下关闭它们。也可能是 TLB。

【讨论】:

    猜你喜欢
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多