C++ SSE42&;STTNI-PcmpEstrM比PcmpIstrM慢两倍,是真的吗?
我正在试验SSE42和STTNI指令,并得到了奇怪的结果-PcmpEstrM(适用于显式长度字符串)的运行速度比PcmpIstrM(隐式长度字符串)慢两倍C++ SSE42&;STTNI-PcmpEstrM比PcmpIstrM慢两倍,是真的吗?,c++,performance,sse,sse4,C++,Performance,Sse,Sse4,我正在试验SSE42和STTNI指令,并得到了奇怪的结果-PcmpEstrM(适用于显式长度字符串)的运行速度比PcmpIstrM(隐式长度字符串)慢两倍 在我的i7 3610QM上,差异是2366.2毫秒与1202.3毫秒-97% 在i5 3470上,差异不太大,但仍然显著=3206.2毫秒vs.2623.2毫秒-22% 两者都是“常春藤桥”——奇怪的是它们有如此不同的“差异”(至少我看不出它们规格上有任何技术差异) 《英特尔64和IA-32体系结构优化参考手册》提到PcmpEstrM和P
- 在我的i7 3610QM上,差异是2366.2毫秒与1202.3毫秒-97%李>
- 在i5 3470上,差异不太大,但仍然显著=3206.2毫秒vs.2623.2毫秒-22%李>
#包括“stdafx.h”
#包括
#定义开始计时器(名称)\
{ \
大整数频率\
大整数\
大整数t1\
双tms\
const char*\uuu tname=名称\
char__tbuf[0xff]\
\
查询性能频率(和频率)\
查询性能计数器(&u_t0);
#定义结束计时器()\
查询性能计数器(&_t1)\
__tms=(uu t1.QuadPart-u t0.QuadPart)*1000.0/u freq.QuadPart\
sprintf_s(uuu tbuf,sizeof(uu tbuf),“%-32s=%6.1f ms\n”,u tname,uu tms)\
OutputDebugStringA(uu-tbuf)\
printf(uu tbuf)\
}
//4.1.3聚合操作
#定义SSE42_聚合_比特基2
#定义SSE42_AGGOP_EQUAL_ANY根据代码表, PCMPESTRM 取8个OP,而PCMPISTRM 在大多数架构上需要3个OP。这将解释您观察到的性能差异。考虑改写代码,如果可能的话,您可以使用<代码> PCMPISTRM 而不是<代码> PCMPESTRM < /代码>。用rep字符串打褶?在IvyBridge上,他们本应该改进的,现在还说不出来,但是以前的经验(我相信这已经在IvyBridge上测试过了)显示REP的性能接近“for”循环,在字符串比较的情况下,算法上更差,因为调用REP需要指定计数、需要计算字符串长度的内容、需要花费时间的内容以及在大多数情况下实际上不需要的内容……Agner的表中指出,pcmpestrm
为8µops,而pcmpistrm
仅为3µops,带有寄存器操作数。这可以解释差异。感谢您参考Agner的表(我不知道它们)。那里的信息似乎是关键。你能发布一个答案让我接受吗?mov-eax,+++++'
会快得多。或者imul-eax,eax,0x01010101
在movd
之前广播一个字节。然后使用pshufd
广播到一个向量。另外,你不需要在内联的sm.MSVC为您做这件事。任何超过4个UOP的东西都是微代码,因此这是一个定性的前端差异。此外,Agner确实将吞吐量列为4对3(接近Intel的数字),但周围代码对前端吞吐量的影响是Intel的延迟/吞吐量数字无法帮助您了解的。请参阅中的“性能分析”部分总结了延迟/前端/执行端口如何成为描述指令序列(无分支或缓存未命中)的三个主要“维度”。
#include "stdafx.h"
#include <windows.h>
#define BEGIN_TIMER(NAME) \
{ \
LARGE_INTEGER __freq; \
LARGE_INTEGER __t0; \
LARGE_INTEGER __t1; \
double __tms; \
const char* __tname = NAME; \
char __tbuf[0xff]; \
\
QueryPerformanceFrequency(&__freq); \
QueryPerformanceCounter(&__t0);
#define END_TIMER() \
QueryPerformanceCounter(&__t1); \
__tms = (__t1.QuadPart - __t0.QuadPart) * 1000.0 / __freq.QuadPart; \
sprintf_s(__tbuf, sizeof(__tbuf), "%-32s = %6.1f ms\n", __tname, __tms ); \
OutputDebugStringA(__tbuf); \
printf(__tbuf); \
}
// 4.1.3 Aggregation Operation
#define SSE42_AGGOP_BITBASE 2
#define SSE42_AGGOP_EQUAL_ANY (00b << SSE42_AGGOP_BITBASE)
#define SSE42_AGGOP_RANGES (01b << SSE42_AGGOP_BITBASE)
#define SSE42_AGGOP_EQUAL_EACH (10b << SSE42_AGGOP_BITBASE)
#define SSE42_AGGOP_EQUAL_ORDERED (11b << SSE42_AGGOP_BITBASE)
int _tmain(int argc, _TCHAR* argv[])
{
int cIterations = 1000000;
int cCycles = 1000;
int cchData = 16 * cIterations;
char* testdata = new char[cchData + 16];
memset(testdata, '*', cchData);
testdata[cchData - 1] = '+';
testdata[cchData] = '\0';
BEGIN_TIMER("PcmpIstrI") {
for( int i = 0; i < cCycles; i++ ) {
__asm {
push ecx
push edx
push ebx
mov edi, testdata
mov ebx, cIterations
mov al, '+'
mov ah, al
movd xmm1, eax // fill low word with pattern
pshuflw xmm1, xmm1, 0 // fill low dqword with pattern
movlhps xmm1, xmm1 // ... and copy it hi dqword
loop_pcmpistri:
PcmpIstrM xmm1, [edi], SSE42_AGGOP_EQUAL_EACH
add edi, 16
sub ebx, 1
jnz loop_pcmpistri
pop ebx
pop edx
pop ecx
}
}
} END_TIMER();
BEGIN_TIMER("PcmpEstrI") {
for( int i = 0; i < cCycles; i++ ) {
__asm {
push ecx
push edx
push ebx
mov edi, testdata
mov ebx, cIterations
mov al, '+'
mov ah, al
movd xmm1, eax // fill low word with pattern
pshuflw xmm1, xmm1, 0 // fill low dqword with pattern
movlhps xmm1, xmm1 // ... and copy it hi dqword
mov eax, 15
mov edx, 15
loop_pcmpestri:
PcmpEstrM xmm1, [edi], SSE42_AGGOP_EQUAL_EACH
add edi, 16
sub ebx, 1
jnz loop_pcmpestri
pop ebx
pop edx
pop ecx
}
}
} END_TIMER();
return 0;
}