代码之家  ›  专栏  ›  技术社区  ›  Ian Ringrose

仅当数据在CPU缓存中时才将数据加载到寄存器中?[副本]

  •  0
  • Ian Ringrose  · 技术社区  · 6 年前

    Minimizing page faults (and TLB faults) while “walking” a large graph '

    ' How to know whether a pointer is in physical memory or it will trigger a Page Fault?

    我希望能够将一些数据从内存加载到寄存器中,但如果内存当前已被调出,则执行加载中止而不是出现页面错误。我需要代码在Windows和Linux上的用户空间中工作,而不需要任何非标准权限。

    ( ,我还想中止TLB故障。)

    0 回复  |  直到 6 年前
        1
  •  5
  •   Margaret Bloom    6 年前

    TXT-NI功能的RTM(受限事务内存)部分允许抑制异常:

    事务区域中必须暴露给软件的任何错误或陷阱都将被抑制。 执行将中止 执行将转换为非事务性执行,就像错误或陷阱从未
    [...]
    需要非事务性执行。这些事件被压制得好像从未发生过一样。

    我从未使用过RTM,但它应该像这样工作:

    xbegin fallback
    
      ; Don't fault here
    
    xend
    
    ; Somewhere else
    fallback:
      ; Retry non-transactionally
    

    还要注意RTM并不是无处不在的。

        2
  •  2
  •   Peter Cordes    6 年前

    不幸的是,在x86(或我知道的任何其他I s a)上,没有一条指令只查询TLB或当前页表并将结果放入寄存器。也许应该有,因为它可以非常便宜地实现。


    像这样的CPU功能有用吗?可能有几个是的

    这样的东西很难得到回报,因为每次“错误”的尝试都是CPU时间/指令没有完成任何有用的工作。但是,这样的情况可能是一个胜利,当您不关心遍历树/图的顺序时,一些节点可能在缓存、TLB中处于热状态,甚至只是RAM,而其他节点处于冷状态,甚至被调出到磁盘。

    普通CPU(如现代x86)可以进行推测性/无序的页面遍历(以填充TLB条目),并且肯定会将推测性加载到缓存中,但不会出现页面错误。页面错误由内核在软件中处理。出现页面错误不可能是推测性的,而且是序列化的。(CPU不重命名特权级别。)

    所以,当你触摸其他内存时,软件预取可以廉价地让硬件填满TLB和缓存,如果你要触摸的第二个内存是冷的 . 如果天气很热,你先碰冷的一面,那就太不幸了。如果有一种廉价的方法来检查热/冷,那么当一个指针是热的,另一个指针是冷的时,使用它总是以正确的顺序(至少在第一步上)进行遍历可能是值得的。除非只读事务非常便宜,否则可能不值得使用玛格丽特的聪明答案。

    如果你有两个指针,你将最终取消引用,其中一个指向一个页面,而另一个页面是热的,最好的情况是以某种方式检测到这一点,并让操作系统从后台的磁盘开始在一个页面中分页,而你遍历的一侧已经在RAM中。(例如,带窗户 PrefetchVirtualMemory 或Linux madvise(MADV_WILLNEED) . 请参阅操作人员其他问题的答案: Minimizing page faults (and TLB faults) while "walking" a large graph )

    昂贵的 因此,对树中的每一对指针中的一个进行VM预取系统调用是不值得的。当所有指针都在RAM中时,会出现大幅度的减速。


    CPU设计可能性

    就像我说的,我不认为任何当前的isa都有这个功能,但我认为在硬件中使用运行类似于加载指令的指令很容易支持它,但是会根据TLB查找而不是从L1d缓存获取数据来生成结果。

    • queryTLB m8 querypage m8 这将在TLB未命中时执行页遍历,并根据是否有页表条目设置标志。将结果放入 r32

    • try_load r32, r/m32 如果可能的话,执行正常加载,但如果页遍历找不到虚拟地址的有效项,则设置标志而不是执行页错误的指令。(例如,CF=1表示有效,CF=0表示中止,整数结果=0,如 rdrand . 它可以使自己变得有用,并根据值设置其他标志(SF/ZF/PF,如果有的话)

    这个 query idea只对性能有用,而不是正确性,因为在查询和使用之间总会有一个间隙,在这个间隙中,页面可以被取消映射。(就像 IsBadXxxPtr

    一个 try_load 同时设置/清除标志而不是提高PF的insn可以避免竞争条件。您可以有不同的版本,也可以立即选择中止条件(例如,TLB未命中而未尝试页面漫游)。

    movsx r32, m8 是Intel上加载端口的单个uop),甚至 vmovddup ymm, m256

    加载到他们没有权限的TLB条目(仅内核映射)时,当前在某些x86 uarch(那些不易崩溃的uarch)上的行为非常特殊。见 The Microarchitecture Behind Meltdown

    但是,如果在TLB/页未命中或L1d未命中以外的条件下中止,则需要外部缓存级别才能支持此特殊请求。如果try_加载命中三级缓存但在三级未命中时中止,则需要三级缓存的支持。不过,我想我们可以不那样做。

    这种CPU架构思想的低挂起果实是减少页面错误和可能的页面行走,这比三级缓存未命中要昂贵得多。

    我怀疑尝试对三级缓存未命中进行分支会使您在分支未命中中付出太多代价,而不是让无序的exec做它该做的事情。特别是如果你有超线程,所以这个延迟限制进程可以发生在CPU的一个逻辑核心上,它也在做其他事情。