从 x86 生成 64 字节读取 PCIe TLP CPU

Generating a 64-byte read PCIe TLP from an x86 CPU

将数据写入 PCIe 设备时,可以使用写入组合映射来提示 CPU 它应该为设备生成 64 字节的 TLP。

是否可以对读取做类似的事情?以某种方式提示 CPU 读取整个缓存行或更大的缓冲区而不是一次读取一个单词?

Intel post编写了一份关于如何进行 64B PCIe 传输的白皮书:如何实施 64B PCIe* 突发 在英特尔® 上传输 架构

原则是:

  1. 将区域映射为 WC
  2. 用下面的代码写64B

    _mm256_store_si256(pcie_memory_address, ymm0);
    _mm256_store_si256(pcie_memory_address+32, ymm1);
    _mm_mfence();
    

其中 _mm256_store_si256(v)movdqa 的内在属性,mfence 用于订购较新的商店和 flush the WC buffer


至于我对缓存子系统WC部分的理解有限,有以下几点假设:

  1. 仅当 WC 缓冲区已满时,CPU 才将 WC 缓冲区写入突发事务:

    The only elements of WC propagation to the system bus that are guaranteed are those provided by transaction atomicity. For example, with a P6 family processor, a completely full WC buffer will always be propagated as a single 32-bit burst transaction using any chunk order. In a WC buffer eviction where data will be evicted as partials, all data contained in the same chunk (0 mod 8 aligned) will be propagated simultaneously.

    因此必须确保使用空的 WC 缓冲区,否则将进行 32B 事务,更糟糕的是,上块可能先于下块写入。
    有一个 practical experimentation on the Intel's forum 使用 FPGA,其中 WC 缓冲区有时会过早刷新。

  2. WC 缓存类型确保核心写入突发事务,但非核心也必须能够处理整个事务。
    特别是在减法解码之后,Root complex 必须能够将其作为 64B 事务进行处理。
    从上面的同一个论坛 post,似乎 uncore 能够将连续的 WC 写入合并到单个 TLP 中,但会使用写入顺序(例如交换两个 _mm256_store_si256 或为大小留一个洞小于 64B) 可能会超出 Root Complex 功能。

英特尔有a white-paper on copying from video RAM to main memory;这应该是相似的,但要简单得多(因为数据适合 2 或 4 个向量寄存器)。

它说 NT 加载会将整个高速缓存行的数据从 WC 内存拉入 LFB:

Ordinary load instructions pull data from USWC memory in units of the same size the instruction requests. By contrast, a streaming load instruction such as MOVNTDQA will commonly pull a full cache line of data to a special "fill buffer" in the CPU. Subsequent streaming loads would read from that fill buffer, incurring much less delay.

使用 AVX2 _mm256_stream_load_si256() 或 SSE4.1/AVX1 128 位版本。

填充缓冲区是一种有限的资源,因此您肯定希望编译器生成 asm 来背对背执行 64 字节缓存行的两个对齐加载,然后 存储到常规内存。

如果您一次处理一个以上的 64 字节块,请参阅 Intel 的白皮书以获取有关使用在 L1d 中保持热的小型反弹缓冲区以避免将存储与 NT 加载混合到 DRAM 的建议。 (L1d 驱逐到 DRAM,就像 NT 存储一样,也需要行填充缓冲区,LFB)。


请注意 _mm256_stream_load_si256() 根本没有用 除了 WC 之外的内存类型。 NT 提示在当前硬件上被忽略,但与常规加载相比,它无论如何都会花费额外的 ALU uop。有 prefetchnta,但那是完全不同的野兽。