《Optimizing for SSE: A Case Study》的读后感

这几天想学SSE,读了一些汇编书毫无收获。今天看了《 Optimizing for SSE: A Case Study
( http://www.cortstratton.org/articles/OptimizingForSSE.php ),终于知道了一些东西。自以为此文堪称经典,难怪GOOGLE上排第一。

使用SSE优化程序的几个要点:
1、要使用SSE指令和XMM寄存器,一次做4个SPFP(Single Precision Floating Point) 数的加减乘除。
2、Batch Processing。如果一次处理很多数据,把数据指针传到asm中,把循环移到asm code中去,尽量避免在循环中重复的从内存中取同一些数据。
3、16-byte memory alignment。要有效使用SSE指令的重要要求。
4、Instruction Pairing。把相同的指令如MOV,ADD等放到一起,CPU可以一个cycle运行3条(现在说不定更多)!但要注意,指令之间不能有相关,即,这些放在一起的指令没有先后顺序,可以随便放不影响程序运行。
5、Prefetching。利用CPU的cache.不十分明白,反正就是加了条prefetchnta 指令。
6、Increase Temporal Locality of Memory I/O。尽量把访问内存的操作放在一起连续进行,减少访问内存的次数。取一个数据,在CPU中处理,然后在取一个,再处理,不如连续取2个数据再处理。
7、Application-Specific Specialization。根据实际情况优化算法,尽量减少不必要的计算。比如,知道自己的程序中某矩阵算完了某一项一定为1,就不要用通用的矩阵运算,而是专门写一个不算那一项的。话说回来,这样代码的重用性肯定受影响,coding的人要受累了。当然如果工作按时间算钱就无所谓了 :)。

另外,文章中用来测试性能的函数很不错,短小实用,精确到cycles。原理不懂,抄在这里备查。

__forceinline  int GetPentiumTimer()
{
    __asm
    {
        xor   eax,eax              // VC won't realize that eax is modified w/out this
                                  //   instruction to modify the val.
                                  //   Problem shows up in release mode builds
        _emit 0x0F                  // Pentium high-freq counter to edx;eax
        _emit 0x31                  // only care about low 32 bits in eax

        xor   edx,edx              // so VC gets that edx is modified
    }
}

用法:

start = GetPentiumTimer();
    BatchTransform1(m, testIn, testOut, VECLEN);
end = GetPentiumTimer();
    cout << "BatchTransform1 (+w equals 1.0):        "
        << ((end-start-timerOverhead) / VECLEN) << " cycles/vec\n";

以上得到的是程序运行所花的时钟周期。如果要知道更直观的时间秒数,可以用 clock() 函数(可能没有前面精确)。MSDN中的例子如下:

// crt_clock.c// This example prompts for how long// the program is to run and then continuously// displays the elapsed time for that period.//

#include <stdio.h>#include <stdlib.h>#include <time.h>

void sleep( clock_t wait );

int main( void ){   long    i = 6000000L;   clock_t start, finish;   double  duration;

   // Delay for a specified time.   printf( "Delay for three seconds\n" );   sleep( (clock_t)3 * CLOCKS_PER_SEC );   printf( "Done!\n" );

   // Measure the duration of an event.   printf( "Time to do %ld empty loops is ", i );   start = clock();   while( i-- )       ;   finish = clock();   duration = (double)(finish - start) / CLOCKS_PER_SEC;   printf( "%2.1f seconds\n", duration );}

// Pauses for a specified number of milliseconds.void sleep( clock_t wait ){   clock_t goal;   goal = wait + clock();   while( goal > clock() )      ;}

还有一个好东西:
IEEE-754 Calculators
http://babbage.cs.qc.edu/IEEE-754/
DEBUG的时候如果看不懂REGISTER里的数,用这个计算器转换一下吧。FLOATING POINT的数32BIT怎么表示,手工搞还是蛮复杂的哦。

2008-9-12

根据最近优化程序的经验,补充几条:

*尽可能少访问内存,开销较大. 比如可以用多余的REGISTER一次多读点数据,然后从REGISTER往REGISTER转(mov, shift, pextrw, pinsrw).
*尽可能少jump. 每次循环中多处理一些数据.
*充分利用SIMD指令的处理能力.比如phaddsw能同时算8个值,要尽可能把它们都填上有效数据, 不要只利用低64BIT或32BIT.
*写C程序时,尽量少声明临时变量,特别是非BUILT-IN的,都是开销.在LOOP里面积少成多也很厉害. 有可能的话, 把某些变量用REGISTER做,减少内存访问.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值