我复制从N个字节pSrc来pDest.这可以在一个循环中完成:
for (int i = 0; i < N; i++)
*pDest++ = *pSrc++
Run Code Online (Sandbox Code Playgroud)
为什么这比memcpy或慢memmove?他们用什么技巧加快速度?
LOOP(英特尔参考手动输入)递减ecx/rcx,然后如果非零则跳转.这很慢,但是英特尔不能廉价地把它变得很快吗? dec/jnz已经将宏观融合成 Sandybridge家族的一个 uop; 唯一的区别是设置标志.
loop关于各种微体系结构,来自Agner Fog的说明表:
Bulldozer-family/Ryzen:1 m-op(与宏观融合测试和分支相同,或者jecxz)
P4:4次(相同jecxz)
loope/ loopne).吞吐量= 4c(loop)或7c(loope/ne).loope/ loopne). 吞吐量=每5个循环一个,这是将循环计数器保留在内存中的瓶颈!jecxz只有2 uops,吞吐量与普通吞吐量相同jcc难道解码器不能像lea rcx, [rcx-1]/ 那样解码jrcxz吗?这将是3 uops.至少那是没有地址大小前缀的情况,否则它必须使用ecx和截断RIP,EIP如果跳转; 也许奇怪的地址大小选择控制减量的宽度解释了许多uops?
或者更好,只需将其解码为不设置标志的融合分支和分支? dec ecx …
在调试时,我经常进入memcpy和memset的手写汇编实现.这些通常使用流指令(如果可用),循环展开,对齐优化等实现...我最近也遇到了由于glibc中的memcpy优化而导致的"错误".
问题是:为什么硬件制造商(英特尔,AMD)不能优化具体情况
rep stos
Run Code Online (Sandbox Code Playgroud)
和
rep movs
Run Code Online (Sandbox Code Playgroud)
被认可,并尽可能快地填写和复制他们自己的架构?
我试图通过SSE和AVX提高复制操作的性能:
#include <immintrin.h>
const int sz = 1024;
float *mas = (float *)_mm_malloc(sz*sizeof(float), 16);
float *tar = (float *)_mm_malloc(sz*sizeof(float), 16);
float a=0;
std::generate(mas, mas+sz, [&](){return ++a;});
const int nn = 1000;//Number of iteration in tester loops
std::chrono::time_point<std::chrono::system_clock> start1, end1, start2, end2, start3, end3;
//std::copy testing
start1 = std::chrono::system_clock::now();
for(int i=0; i<nn; ++i)
std::copy(mas, mas+sz, tar);
end1 = std::chrono::system_clock::now();
float elapsed1 = std::chrono::duration_cast<std::chrono::microseconds>(end1-start1).count();
//SSE-copy testing
start2 = std::chrono::system_clock::now();
for(int i=0; i<nn; ++i)
{
auto _mas = mas;
auto _tar = tar; …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中数据移动的每次迭代的一个周期.
(强调我的)
没有进一步提及这种启动成本.它是什么?它做了什么,为什么总是需要更多的时间?