丰台成都网站建设,wordpress 去掉 index.php,哪些公司是wordpress,wordpress 树状目录结构wuhanbingwhdx提到了数据相关也会影响流水线#xff08;http://blog.csdn.net/zyl910/article/details/1330614#xff09;。 他的说法是有一定道理的。但是#xff0c;在很多时候我们并不仅仅处理一个数值。比如将循环展开#xff0c;在内循环处理2个或更多个的数值。而… wuhanbingwhdx提到了数据相关也会影响流水线http://blog.csdn.net/zyl910/article/details/1330614。 他的说法是有一定道理的。但是在很多时候我们并不仅仅处理一个数值。比如将循环展开在内循环处理2个或更多个的数值。而现代编译器面对循环展开时在编译优化操作中会调整指令顺序错开有相关性指令。因现代处理器支持超标量这样的指令顺序调整能获得较好的指令级并行度从而优化了性能。 其次就算编译器对循环展开优化的不够彻底没将相关性指令错开。但因现代处理器支持乱序执行当遇到相关性指令需要等待时处理器会处理后面未相关的指令从而保持处理器满载尽量减轻相关性等待造成的性能损失。 第三现代处理器还支持寄存器重命名技术——当两处代码用到同名的寄存器时译码器会做寄存器重命名处理给它们分配不同的寄存器使数据不会干扰从而获得更高的指令级并行度。 上面说了很多理论知识实际性能到底怎么样呢还是写代码测一测吧。 一、目标——将64位像素转为32位像素 将64位像素转为32位像素是饱和处理的最典型应用。 64位像素有4个通道每个通道16位带符号16位整数。每像素8个字节。 32位像素有4个通道每个通道8位无符号8位整数。每像素4个字节。 转换方法为——将每个像素的4个通道由16位带符号16位整数转为8位无符号8位整数。因为每次都是对4个通道进行处理所以能获得较高的指令级并行度。 具体的存储格式为—— 注1.内存地址由低到高从下到上垂直方向的每一格是一个字节。0代表数据基址1代表数据基址1以此类推。2.左为“64位像素”数组右为“32位像素”数组。3.图中使用了用双边线来分隔像素。因“64位像素”是8字节而“32位像素”是4字节。所以对于16个字节的空间左侧能存放2个像素、右侧能存放4个像素。4.图中使用了用实线来分隔通道。“64位像素”的通道是16位占用2个字节。“32位像素”的通道是8位只占用1个字节。5.图中使用了用虚线来分隔字节。主要用于“64位像素”。6.这里采用了Windows位图通道规则即通道顺序为B、G、R、A从低到高。例如“B0”代表像素0的B蓝色通道、“A0”代表像素0的A不透明度通道、“A1”代表像素1的A通道……以此类推。7.这里采用了小端Little Endian方式的字节序Endianness即最低地址存放的最低字节。采用小写的“h”、“l”来表示“64位像素”通道的高、低字节。例如“B0l”代表像素0的B通道的低字节、“A0h”代表像素0的A通道的高字节……以此类推。 上面貌似挺复杂又是图表又是大段文字的。其实代码写起来很简单的一般情况下不需要理会通道顺序与字节序问题—— // 用if分支做饱和处理void f0_if(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] (pS[0]0) ? 0 : ( (pS[0]255) ? 255 : (BYTE)pS[0] ); pD[1] (pS[1]0) ? 0 : ( (pS[1]255) ? 255 : (BYTE)pS[1] ); pD[2] (pS[2]0) ? 0 : ( (pS[2]255) ? 255 : (BYTE)pS[2] ); pD[3] (pS[3]0) ? 0 : ( (pS[3]255) ? 255 : (BYTE)pS[3] );// next pS 4; pD 4; }} 参数说明——pbufD目标缓冲区的地址。如“64位像素”数组的首地址。pbufS源缓冲区的地址。如“32位像素”数组的首地址。cnt像素个数。 使用方法—— signed short bufS[DATASIZE*4]; // 源缓冲区。64位的颜色4通道每通道16位BYTE bufD[DATASIZE*4]; // 目标缓冲区。32位的颜色4通道每通道8位f0_if(bufD, bufS, DATASIZE); 对于数据处理来说用指针比用数组写起来更简洁而且执行速度更快。 而且C语言中的指针支持下标运算符能够用下标访问后面的元素“pD[1]”相当于“*(pD 1)”简化了不少代码。指针下标可参考 http://www.lupaworld.com/home-space-uid-77885-do-blog-id-28843.html 例如“pD[1] (pS[1]0) ? 0 : ( (pS[1]255) ? 255 : (BYTE)pS[1] );”这行代码的数组写法为—— pbufD[i*41] (pbufS[i*41]0) ? 0 : ( (pbufS[i*41]255) ? 255 : (BYTE)pbufS[i*41] ); 因为条件语句“if”的代码写起来比较繁琐所以这里用到了条件运算符“?:”来简化代码。例如“pD[1] (pS[1]0) ? 0 : ( (pS[1]255) ? 255 : (BYTE)pS[1] );”这行代码的“if”写法为—— if (pS[1]0) pD[1]0elseif (pS[1]255) pD[1]255else pD[1](BYTE)pS[1]; 二、测试方法——测试程序的框架 前面已经编写了一个函数f0_if随后我们会编写多个函数分别测试性能。具体怎么测试呢难道是为每一个函数都写一套测试代码……不那样的话太糟糕了。 我们可以利用函数指针进行统一的测试。函数指针定义如下与f0_if的参数列相同—— // 测试时的函数类型typedef void (*TESTPROC)(BYTE* pbufD, const signed short* pbufS, int cnt); 有了函数指针后进行测试就很简单了只需要将要测试的函数传递过去就行了。例如这样测试f0_if—— runTest(f0_if, f0_if); runTest代码如下—— // 进行测试void runTest(char* szname, TESTPROC proc){int i,j; DWORD tm0, tm1; // 存储时间 for(i1; i3; i) // 多次测试 {//tm0 GetTickCount(); tm0 timeGetTime();// main for(j1; j4000; j) // 重复运算几次延长时间避免计时精度问题 { proc(bufD, bufS, DATASIZE); }// show//tm1 GetTickCount() - tm0; tm1 timeGetTime() - tm0; printf(%s[%d]:\t%u\n, szname, i, tm1); }} printf输出的是测试时间单位毫秒。值越小表示所花时间越少、运行速度越快、性能越高。 这里用到了timeGetTime来计算时间要注意加上winmm.lib库—— 对于bufD、bufS、DATASIZE我是这样定义的—— // 数据规模#define DATASIZE 16384 // 128KB / (sizeof(signed short) * 4)// 缓冲区signed short bufS[DATASIZE*4]; // 源缓冲区。64位的颜色4通道每通道16位BYTE bufD[DATASIZE*4]; // 目标缓冲区。32位的颜色4通道每通道8位 缓冲区的尺寸是特意规定的。对于现在主流CPU来说Intel处理器的二级缓存一般是每核心256KB而AMD处理器的二级缓存一般是每核心512KB。所以数据最好不要超过256KB这样就能在二级缓存上完成处理避免了内存访问延时造成的干扰。 于是我给bufS分配了128KB给bufD分配了64KB。 三、更多的测试 用min、max做饱和处理—— // 用min、max饱和处理void f1_min(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] min(max(0, pS[0]), 255); pD[1] min(max(0, pS[1]), 255); pD[2] min(max(0, pS[2]), 255); pD[3] min(max(0, pS[3]), 255);// next pS 4; pD 4; }} 用位掩码做饱和处理用求负生成掩码—— // 用位掩码做饱和处理.用求负生成掩码void f2_neg(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] LIMITSU_BYTE(pS[0]); pD[1] LIMITSU_BYTE(pS[1]); pD[2] LIMITSU_BYTE(pS[2]); pD[3] LIMITSU_BYTE(pS[3]);// next pS 4; pD 4; }} 用位掩码做饱和处理用带符号右移生成掩码—— // 用位掩码做饱和处理.用带符号右移生成掩码void f3_sar(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] LIMITSW_BYTE(pS[0]); pD[1] LIMITSW_BYTE(pS[1]); pD[2] LIMITSW_BYTE(pS[2]); pD[3] LIMITSW_BYTE(pS[3]);// next pS 4; pD 4; }} 四、全部代码 全部代码为—— // 用位掩码做饱和处理.用求负生成掩码#define LIMITSU_FAST(n, bits) ( (n) -((n) 0) | -((n) (1(bits))) )#define LIMITSU_SAFE(n, bits) ( (LIMITSU_FAST(n, bits)) ((1(bits)) - 1) )#define LIMITSU_BYTE(n) ((BYTE)(LIMITSU_FAST(n, 8)))// 用位掩码做饱和处理.用带符号右移生成掩码#define LIMITSW_FAST(n, bits) ( ( (n) | ((signed short)((1(bits)) - 1 - (n)) 15) ) ~((signed short)(n) 15) )#define LIMITSW_SAFE(n, bits) ( (LIMITSW_FAST(n, bits)) ((1(bits)) - 1) )#define LIMITSW_BYTE(n) ((BYTE)(LIMITSW_FAST(n, 8)))// 数据规模#define DATASIZE 16384 // 128KB / (sizeof(signed short) * 4)// 缓冲区signed short bufS[DATASIZE*4]; // 源缓冲区。64位的颜色4通道每通道16位BYTE bufD[DATASIZE*4]; // 目标缓冲区。32位的颜色4通道每通道8位// 测试时的函数类型typedef void (*TESTPROC)(BYTE* pbufD, const signed short* pbufS, int cnt);// 用if分支做饱和处理void f0_if(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] (pS[0]0) ? 0 : ( (pS[0]255) ? 255 : (BYTE)pS[0] ); pD[1] (pS[1]0) ? 0 : ( (pS[1]255) ? 255 : (BYTE)pS[1] ); pD[2] (pS[2]0) ? 0 : ( (pS[2]255) ? 255 : (BYTE)pS[2] ); pD[3] (pS[3]0) ? 0 : ( (pS[3]255) ? 255 : (BYTE)pS[3] );// next pS 4; pD 4; }}// 用min、max饱和处理void f1_min(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] min(max(0, pS[0]), 255); pD[1] min(max(0, pS[1]), 255); pD[2] min(max(0, pS[2]), 255); pD[3] min(max(0, pS[3]), 255);// next pS 4; pD 4; }}// 用位掩码做饱和处理.用求负生成掩码void f2_neg(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] LIMITSU_BYTE(pS[0]); pD[1] LIMITSU_BYTE(pS[1]); pD[2] LIMITSU_BYTE(pS[2]); pD[3] LIMITSU_BYTE(pS[3]);// next pS 4; pD 4; }}// 用位掩码做饱和处理.用带符号右移生成掩码void f3_sar(BYTE* pbufD, const signed short* pbufS, int cnt){const signed short* pS pbufS; BYTE* pD pbufD;int i;for(i0; icnt; i) {// 分别对4个通道做饱和处理 pD[0] LIMITSW_BYTE(pS[0]); pD[1] LIMITSW_BYTE(pS[1]); pD[2] LIMITSW_BYTE(pS[2]); pD[3] LIMITSW_BYTE(pS[3]);// next pS 4; pD 4; }}// 进行测试void runTest(char* szname, TESTPROC proc){int i,j; DWORD tm0, tm1; // 存储时间 for(i1; i3; i) // 多次测试 {//tm0 GetTickCount(); tm0 timeGetTime();// main for(j1; j4000; j) // 重复运算几次延长时间避免计时精度问题 { proc(bufD, bufS, DATASIZE); }// show//tm1 GetTickCount() - tm0; tm1 timeGetTime() - tm0; printf(%s[%d]:\t%u\n, szname, i, tm1); }}int main(int argc, char* argv[]){int i; // 循环变量//printf(Hello World!\n); printf( noif:VC6 );// 初始化 srand( (unsigned)time( NULL ) );for(i0; iDATASIZE*4; i) { bufS[i] (signed short)((rand()0x1FF) - 128); // 使数值在 [-128, 383] 区间 }// 准备开始。可以将将进程优先级设为实时 if (argc1) { printf(Press any key to continue); getch(); printf(\n); }// 进行测试 runTest(f0_if, f0_if); runTest(f1_min, f1_min); runTest(f2_neg, f2_neg); runTest(f3_sar, f3_sar);// 结束前提示 if (argc1) { printf(Press any key to exit); getch(); printf(\n); }return 0;} 五、测试结果 将程序编译为“Release”版然后分别在不同的系统环境中进行测试。 在32位winXP上的测试结果—— noif:VC6 Press any key to continuef0_if[1]: 2016f0_if[2]: 2016f0_if[3]: 2015f1_min[1]: 2063f1_min[2]: 2062f1_min[3]: 2063f2_neg[1]: 718f2_neg[2]: 719f2_neg[3]: 719f3_sar[1]: 672f3_sar[2]: 687f3_sar[3]: 672 在64位win7上的测试结果—— noif:VC6 Press any key to continuef0_if[1]: 2075f0_if[2]: 2012f0_if[3]: 2028f1_min[1]: 2059f1_min[2]: 2075f1_min[3]: 2075f2_neg[1]: 717f2_neg[2]: 718f2_neg[3]: 718f3_sar[1]: 670f3_sar[2]: 687f3_sar[3]: 686 硬件环境——CPUIntel Core i3-2310M, 2100 MHz内存DDR3-1066 源码下载——http://files.cnblogs.com/zyl910/noifVC6.rar建议阅读编译器生成的汇编代码位于Release\noifVC6.asm 转载于:https://www.cnblogs.com/zyl910/archive/2012/03/27/noifopex3.html