我想使用增强的REP MOVSB(ERMSB)为自定义获得高带宽memcpy.
ERMSB引入了Ivy Bridge微体系结构.如果您不知道ERMSB是什么,请参阅英特尔优化手册中的"增强型REP MOVSB和STOSB操作(ERMSB)" 部分.
我知道直接执行此操作的唯一方法是使用内联汇编.我从https://groups.google.com/forum/#!topic/gnu.gcc.help/-Bmlm_EG_fE获得了以下功能
static inline void *__movsb(void *d, const void *s, size_t n) {
asm volatile ("rep movsb"
: "=D" (d),
"=S" (s),
"=c" (n)
: "0" (d),
"1" (s),
"2" (n)
: "memory");
return d;
}
Run Code Online (Sandbox Code Playgroud)
然而,当我使用它时,带宽远小于memcpy.
使用我的i7-6700HQ(Skylake)系统,Ubuntu 16.10,DDR4 @ 2400 MHz双通道32 GB,GCC 6.2,__movsb获得15 GB/s并memcpy获得26 GB/s.
为什么带宽如此低REP MOVSB?我该怎么做才能改善它?
这是我用来测试它的代码.
//gcc -O3 -march=native -fopenmp foo.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include …Run Code Online (Sandbox Code Playgroud) 引用英特尔 ®64 和IA-32架构优化参考手册,§2.4.6"REP String Enhancement":
使用REP字符串的性能特征可归因于两个组件: 启动开销和数据传输吞吐量.
[...]
对于较大粒度数据传输的REP字符串,随着ECX值的增加,REP String的启动开销呈逐步增加:
- 短串(ECX <= 12):REP MOVSW/MOVSD/MOVSQ的延迟约为20个周期,
快速字符串(ECX> = 76:不包括REP MOVSB):处理器实现通过移动尽可能多的16字节数据来提供硬件优化.如果其中一个16字节数据传输跨越缓存行边界,则REP字符串延迟的延迟会有所不同:
- 无拆分:延迟包括大约40个周期的启动成本,每个64字节的数据增加4个周期,
- 高速缓存拆分:延迟包括大约35个周期的启动成本,每64个字节的数据增加6个周期.
中间字符串长度:REP MOVSW/MOVSD/MOVSQ的延迟具有大约15个周期的启动成本加上word/dword/qword中数据移动的每次迭代的一个周期.
(强调我的)
没有进一步提及这种启动成本.它是什么?它做了什么,为什么总是需要更多的时间?
我对缓存一致性系统在现代多核 CPU 中的功能有些困惑。我已经看到基于侦听的协议,如基于 MESIF/MOESI 侦听的协议已在 Intel 和 AMD 处理器中使用,另一方面,基于目录的协议似乎对多核更有效,因为它们不广播但发送消息具体节点。
AMD 或 Intel 处理器中的现代缓存一致性解决方案是什么,它是基于侦听的协议,如 MOESI 和 MESIF,还是仅基于目录的协议,还是两者的组合(基于侦听的协议,用于同一节点内的元素之间的通信? ,以及基于节点到节点通信的目录)?
我目前正在深入研究std::atomicsC++ 内存模型。真正对我的思维模型有帮助的是 CPU 的存储和加载缓冲区的概念,它基本上是一个 fifo 队列,用于存储必须写入 L1 缓存或从 L1 缓存读取的数据,至少在英特尔架构中存在。据我了解,原子操作基本上是对 CPU 的指令,可防止包装类型在编译时或运行时撕裂并跨屏障重新排序写入或读取指令。为了说明我的思维模型中的差距,我很快就想出了这个例子:
#include <atomic>
#include <iostream>
#include <thread>
int a;
int b;
int c;
std::atomic<int> x;
int e = 0;
auto thread1() {
while(1) {
a = 3;
b = 5;
c = 1;
x.store(10, std::memory_order::release);
e++;
std::cout << "stored!" << std::endl;
}
}
auto thread2() {
while(1) {
x.load(std::memory_order::acquire);
std::cout << b << std::endl;
}
}
int main() {
[[maybe_unused]] auto t1 = …Run Code Online (Sandbox Code Playgroud) 谈到效率,缓存是核心。
我知道缓存通常会自动发生。
但是,我想自己控制缓存的使用,因为我认为我可以比一些不知道确切程序的启发式方法做得更好。
因此,我需要汇编指令来直接移入或移出高速缓存单元。
喜欢:
movL1 address content
Run Code Online (Sandbox Code Playgroud)
我知道有一些指令会给出“缓存系统”提示,但我不确定这是否足够,因为这些提示可能会被忽略,或者它们可能不足以表达任何可以通过这种移入/移出缓存来表达的内容命令。
是否有任何允许完全缓存控制的汇编程序?
旁注:为什么我想改进缓存:
考虑一个具有 1 个寄存器和一个包含 2 个单元的缓存的假设 CPU。
考虑以下两个程序:
(其中 x,y,z,a 是存储单元)
"START"
"move 1 to x"
"move 2 to y"
"move 3 to z"
"move 4 to a"
"move z to x"
"move y to x"
"END"
"START"
"move 1 to x"
"move 2 to y"
"move 3 to z"
"move 4 to a"
"move a to x"
"move y to x"
"END"
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,您将寄存器和缓存用于 x,y,z(a 只写入一次)在第二种情况下,您将寄存器和缓存用于 a,x,y (z只写入一次)
如果 CPU …
assembly ×3
x86 ×3
cpu-cache ×2
performance ×2
atomic ×1
c ×1
c++ ×1
caching ×1
gcc ×1
memcpy ×1
memory-model ×1
mesi ×1
optimization ×1
stdatomic ×1