基于MMX指令集的程序设计简介
MMX技术简介 Visual Studio .NET 2003提供了对MMX指令集特性的支持,从而可以不必编写汇编代码,直接使用C++代码就可以实现MMX指令的功能。通过参考Intel软件说明书(Intel Software manuals)[1]以及阅读MSDN中有关MMX编程技术的主题会使你更好地把握MMX编程的要点。 MMX技术实现了单道指令多道数据流(SIMD,single-instruction, multiple-data)的执行模式。考虑下面一个需要编程完成的任务,在一个字节(BYTE)数组中使其中每一个元素加上一个数,在传统的程序中,实现这个功能的算法如下: for each b in array //对数组中的每一个元素b 下面看看它的实现细节: for each b in array //对数组中的每一个元素b
for each 8 members in array //把数组中的8个字节(其中一个字节为数组中的一个单位)作为一组取出
Visual C++ MMXSwarm [4]是MSDN中提供的一个很好的使用MMX技术进行图象处理的例子,它包含了一些封装好了的类简化了使用MMX技术的操作,并向你展示了对各种不同格式图象进行处理的操作(如单色24位象素RGB、32位象素RGB等)。本文只是对使用Visual C++实现MMX程序设计的简单介绍。如果你感兴趣的话,可以参看MSDN上MMXSwarm的例子。 MMX程序设计详细介绍 包含的头文件 所有的MMX指令集函数在emmintrin.h文件中定义: __m64 数据类型 这种类型的变量可用作MMX指令的操作数,它不能被直接访问。_m64类型的变量被自动分配为8个字节的字长。 CPU对MMX指令集的支持 如果你的CPU能够具有了MMX指令集,你就可以使用Visual Studio .NET 2003提供的对MMX指令集支持的C++函数库了,你可以查看MSDN中的一个Visual C++ CPUID[3]的例子,它可以帮你检测你的CPU是否支持SSE、MMX指令集或其它的CPU功能。 饱和算法(Saturation Arithmetic)和封装模式(Wraparound Mode) MMX技术支持一种叫做saturating arithmetic(饱和算法)的计算模式。在饱和模式下,当计算结果发生溢出(上溢或下溢)时,CPU会自动去掉溢出的部分,使计算结果取该数据类型表示数值的上限值(如果上溢)或下限值(如果下溢)。饱和模式的计算用于对图象的处理。 编程实例 以下讲解了MMX技术在Visual Studio .NET 2003下的应用实例,你可以在http://www.codeproject.com/cpp/mmxintro/MMX_src.zip下载示例程序压缩包。该压缩包中含有两个项目,这两个项目是基于微软基本类库(MFC)建立的Visual C++.NET项目,你也可以按照下面的讲解建立这两个项目。 MMX8是一个单文档界面(SDI)的应用程序,用来对每象素8位的单色位图进行简单处理。源图象和处理后的图象会在窗体中显示出来。新建的ATL(活动模版库)类 Cimage用来从资源中提取图象并在窗体中显示出来。程序要对图象进行两种处理操作:图象颜色反相和改变图象的亮度。每一种处理操作可以用下面几种方法之中其中的一种来实现: 纯C++代码; 对图象进行处理计算的时间会显示在状态栏中。 用纯C++实现的图象颜色反相函数: void CImg8Operations::InvertImageCPlusPlus(
用Visual C++.NET的MMX指令函数实现图象颜色反相的函数: void CImg8Operations::InvertImageC_MMX( // 每次循环处理8个象素 __m64* pIn = (__m64*) pSource; // 输入的字节数组指针 __m64 tmp; // 临时工作变量 _mm_empty(); // 执行MMX指令:emms,初始化MMX寄存器 __m64 n1 = Get_m64(i); for ( int i = 0; i < nLoop; i++ ) pIn++; // 取下面的8个象素点 _mm_empty(); // 执行MMX指令:emms,清除MMX寄存器中的内容 __m64 CImg8Operations::Get_m64(__int64 n) mi.i = n; 虽然这个函数在非常短的时间就执行完成了,但我记录了这3种方法需要的时间,以下是在我的计算机上运行的结果: 上面的图象处理时间必须在程序Release优化编译后执行时才能体现出很好的效果。 而改变图象的亮度我采用了最简单的方法:对图象中的每一个象素的颜色值进行加减运算。相对前面的处理函数而言,这样的转换函数有些复杂,因为我们需要把处理过程分成两种情况,一种是增加象素颜色值,另一种是减少象素颜色值。
void CImg8Operations::ChangeBrightnessCPlusPlus( BYTE b = (BYTE) abs(nChange); int i, n; if ( nChange > 0 ) //增加象素颜色值 if ( n > 255 ) *pDest++ = (BYTE) n; if ( n < 0 )
void CImg8Operations::ChangeBrightnessC_MMX( BYTE* pSource, BYTE b = (BYTE) abs(nChange); __int64 c = b; for ( int i = 1; i <= 7; i++ ) // 在一次循环中处理8个象素 __m64* pIn = (__m64*) pSource; // 输入的字节数组 __m64 tmp; // 临时工作变量 _mm_empty(); // 执行MMX指令:emms __m64 nChange64 = Get_m64(c); if ( nChange > 0 ) *pOut = tmp; pIn++; // 取下面8个象素 *pOut = tmp; pIn++; //取下面8个象素 pOut++; _mm_empty(); // 执行MMX指令:emms 注意参数nChange的符号每次调用函数时在循环体外只检查一次,而不是放在循环体内,那样会被检查成千上万次。下面是在我的计算机上处理图象花费的时间: 纯C++代码 49毫秒
MMX32项目可对32位象素的RGB图象进行处理。进行的图象处理工作是图象颜色反相操作和更改图象颜色的平衡度(将象素点的每一种颜色乘以一定的值)操作。 MMX的乘法实现起来比加减法复杂得多,因为乘法运算通常得出的结果的位数不再是以前位数的大小。比如,如果乘法的操作数有一个字节(8位的BYTE)大小,那么结果会达到一个字(16位的WORD)大小。这需要额外的转换,并且使用MMX汇编指令和C++代码进行图象转换花费时间的差别不是很大(时间差为5-10%)。 用Visual C++.NET的MMX指令函数实现的更改图象颜色平衡度的函数: void CImg32Operations::ColorsC_MMX( // 设置相乘系数 __m64 nNull = _m_from_int(0); // null _mm_empty(); // 清空MMX寄存器。 __m64 nCoeff = Get_m64(c); DWORD* pIn = (DWORD*) pSource; // 输入双字数组 for ( int i = 0; i < nNumberOfPixels; i++ ) tmp = _mm_unpacklo_pi8(tmp, nNull ); //将tmp中低位的4个字节转化为字 tmp = _mm_mullo_pi16 (tmp , nCoeff); //将tmp中的每一个字相乘,将相乘结果的高位送到nCoeff,在tmp中只保留每个结果的低位。 tmp = _mm_srli_pi16 (tmp , 8); // 将tmp中的每一个字右移8位,相当于除以256 tmp = _mm_packs_pu16 (tmp, nNull); // 使用饱和模式将tmp中的结果做如下处理: *pOut = _m_to_int(tmp); // *pOut = tmp (将tmp低32位的数据放入pOut数组中) pIn++; } _mm_empty(); 你可以参看示例项目的源代码了解有关此项目的更多的细节。 SSE2 技术 SSE2技术包含有一个类似MMX中对整数操作的指令集,同时也包含128位的SSE寄存器组。比如,用SSE2技术实现更改图象颜色平衡度能够比用纯C++代码实现此功能在效率上有很大提升。SSE2同时是SSE技术的扩展,比如它不仅可以单精度浮点数数组,而且能够处理双精度浮点数数据类型的数组。用C++实现的MMXSwarm 示例项目不仅使用了MMX指令函数,而且使用了SSE2指令对整型数操作的函数。 参考文档: [1] Intel软件说明书(Intel Software manuals):http://developer.intel.com/design/archives/processors/mmx/index.htm 。 [2] MSDN中有关MMX技术的主题:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vcrefsupportformmxtechnology.asp。 [3] Microsoft Visual C++ CPUID项目示例:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/vcsamcpuiddeterminecpucapabilities.asp。 [4] Microsoft Visual C++ MMXSwarm项目示例: 作者:Alex Farber |