使用 SSE/SSE2 进行优化






3.97/5 (26投票s)
2004年10月3日
3分钟阅读

147216

884
针对其中一种优化方法的初学者介绍。

目录
目标
我发布本文的目标是分享一些简单的优化方法。未来,我将尝试花一些时间编写更多文章。
引言
本文演示了英特尔的 SIMD(单指令多数据)扩展技术。通过使用像 movdqa 这样的新型英特尔指令进行优化,将比典型的指令更快地移动(复制)数据。
回顾
在我们继续之前,让我们回顾一下我们现在已有的知识。如今,或者更常见的是,我们在家庭甚至在工业领域都在使用 32 位处理器。像 eax、ebx.. 等通用寄存器都是 32 位的。sizeof(int) = 4 (字节)。但并非所有寄存器都是 32 位的,有些寄存器具有更长的位长。十年前,英特尔推出了 MMX 扩展,其中有 8 个寄存器 mm0、mm1 .. mm7 具有 64 位的长度。之后,英特尔推出了 SSE 扩展,它又有另外 8 个新的寄存器 xmm0、xmm1 .. xmm7 具有 128 位的长度。如果您想了解更多详细信息,请访问我的 链接 部分。查找英特尔。
要求
首先问问你自己,你正在使用什么机器。它应该是 Intel P3 或更新的。您必须记住,这种优化方法是依赖于机器的,这意味着如果您的硬件不支持,您将无法看到差异。
代码
我创建的示例,我特意使其简单,使其在控制台模式下运行。不要剪切和粘贴,我更希望读者理解并自己尝试。这是示例的开始..
演示代码将让您看到这两个功能之间在达到相同目的时的差异。从这里开始,我不会解释太多,您将独自一人,请阅读代码中的注释。我确信你能够赶上。=)
等等!先准备好你的断点,坐稳了。当您进行调试时,请尝试单步执行这两个函数,您会注意到差异。
“DataTransferTypical”将在每次循环中复制一个 int (sizeof(int)=4bytes),而“DataTransferOptimised”将在每次循环中复制四个 int (4*sizeof(int)=16bytes)。
设置您的监视窗口.. 在您的监视窗口中,监视“piDst, 101”。然后你就会看到它是如何变化的... 
附注:您需要安装处理器包才能让您的 MSVC++ 编译这段代码。请参阅“链接”部分。
int DataTransferTypical(int* piDst, int* piSrc, unsigned long SizeInBytes);
int DataTransferOptimised(int* piDst, int* piSrc, unsigned long SizeInBytes);
int main(int argc, char* argv[])
{
 // var keep the start and end time. simple one. if u wish to have accurate 
 // one, please look for other article.
 unsigned long dwTimeStart = 0;
 unsigned long dwTimeEnd = 0;
 // temporary variable
 int *piSrc = NULL;
 int *piDst = NULL;
 int i = 0;
 char cKey = 0;
 unsigned long dwDataSizeInBytes = sizeof(int) * DATA_SIZE;
 // u need to install processor pack in order to get msvc++ compile this 
 // code. see Link section.
 piSrc = (int *)_aligned_malloc(dwDataSizeInBytes,dwDataSizeInBytes);
 piDst = (int *)_aligned_malloc(dwDataSizeInBytes,dwDataSizeInBytes);
 do
 {
  // initialise
  memset(piSrc, 1, dwDataSizeInBytes);
  memset(piDst, 0, dwDataSizeInBytes);
  dwTimeStart = clock();
  for(i = 0; i < ITERATION; i++)
   DataTransferTypical(piDst, piSrc, dwDataSizeInBytes);
  dwTimeEnd = clock();
  printf("== Typical Transfer of %d * %d times of %d bytes data ==\nTime 
          Elapsed = %d msec\n\n", 
          ITERATION, DATA_SIZE, sizeof(int), dwTimeEnd - dwTimeStart);
  // initialise
  memset(piSrc, 1, dwDataSizeInBytes);
  memset(piDst, 0, dwDataSizeInBytes);
  dwTimeStart = clock();
  for(i = 0; i < ITERATION; i++)
   DataTransferOptimised(piDst, piSrc, dwDataSizeInBytes);
  dwTimeEnd = clock();
  printf("== Optimised Transfer of %d * %d times of %d bytes data ==\nTime 
         Elapsed = %d msec\n\n", 
         ITERATION, DATA_SIZE, sizeof(int), dwTimeEnd - dwTimeStart);
  printf("Rerun? (y/n) ");
  cKey = getche();
  printf("\n\n");
 }while(cKey == 'y');
 _aligned_free(piSrc);
 _aligned_free(piDst);
 return 0;
}
#pragma warning(push)
#pragma warning(disable:4018 4102)
int DataTransferTypical(int* piDst, int* piSrc, unsigned long SizeInBytes)
{
 unsigned long dwNumElements = SizeInBytes / sizeof(int);
 for(int i = 0; i < dwNumElements; i++)
 {
  // i is offset.
  *(piDst + i) = *(piSrc + i);
 }
 return 0;
}
int DataTransferOptimised(int* piDst, int* piSrc, unsigned long SizeInBytes)
{
 unsigned long dwNumElements = SizeInBytes / sizeof(int);
 // not really using it, just for debuging. it keeps number of looping. 
 // it also means number of packed data.
 unsigned long dwNumPacks = dwNumElements / (128/(sizeof(int)*8));
 _asm
 {
  // remember for cleanup
  pusha;
begin:
  // init counter to SizeInBytes
  mov  ecx,SizeInBytes;
  // get destination pointer
  mov  edi,piDst;
  // get source pointer
  mov  esi,piSrc;
begina:
  // check if counter is 0, yes end loop.
  cmp  ecx,0;
  jz  end;
body:
  // calculate offset
  mov  ebx,SizeInBytes;
  sub  ebx,ecx;
  // copy source's content to 128 bits registers
  movdqa xmm1,[esi+ebx];
  // copy 128 bits registers to destination
  movdqa [edi+ebx],xmm1;
bodya:
  // we've done "1 packed == 4 * sizeof(int)" already.
  sub  ecx,16;
  jmp  begina;
end:
  // cleanup
  popa;
 }
 return 0;
}
#pragma warning(pop)
最后
这是我在 Code Project 上的第一篇文章,如果有什么不对劲的地方请多多包涵。此外,我希望我在这里上传的演示对于初学者来说足够简单。没什么花哨的。学习是乐趣,对吧? =)
链接
历史
只有当人们提出要求时,我才会更新这篇文章。示例代码将不会维护。
许可证
本文档没有明确的许可证,但可能包含文章文本或下载文件本身的使用条款。如有疑问,请通过下方的讨论区联系作者。
可以在此处找到作者可能使用的许可证列表
