一个卓有成效的汇编优化范例--使用SSE2指令优化进制转化

我的一个感兴趣的编程方向是大数计算,因此用汇编语言写了很多大数计算方面的小程序,上周突然想出一个使用SSE2指令将整数转为16进制字符串的好主意,遂付诸实现。原以为至多可提速500%,那知测试后发现,相对于最初的C语言版本,速度竟提高20倍以上,兴奋之余,遂有了这篇博客文章。

这个程序主要示范将64bit一个整数转化为16进制字符串的功能,功能和算法都比较简单。我相信许多人都写过类似的程序,但不知有没有人尝试去你优化它。这个示范程序包括3个C语言版和1个使用SSE2指令的汇编语言版。下面我们给出代码和说明。

先看这个函数最初的版本,UINT64_2_hexString_c1,为了性能起见,我们使用 __fastcall 函数约定,__fastcall 接口的函数使用寄存器来传递参数,免除了调用时压栈的开销,而且被调函数可以省去保存/恢复寄存器等指令。

//这是C语言普通版,直接使用64位整数逻辑指令和算术指令
void __fastcall UINT64_2_hexString_c1(UINT64 *p, char *buff)
{
  UINT64 x=*p;
  int i;
  for (i=15;i>=0;i--)
  {
    char c= (x & 0xf)+'0';
    if ( c>'9')
      c+=7;
    buff[i]=c;
    x>>=4;
  }
  buff[16]=0;
}

上面这个函数虽然简单,然而速度却仍不理想。我们知道,在32位运行环境,对64位整数计算的C语言语句要翻译成多条指令,故速度较慢,下面这个版本使用完全的32位整数处理,故速度快于上面的版本。

//这是C语言改进版,使用32位整数逻辑指令和算术指令
void __fastcall UINT64_2_hexString_c2(UINT64 *p, char *buff)
{
	DWORD *pDW=(DWORD *)p;
	DWORD x;
	int i;
	for (x=pDW[1],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		if ( c>'9')
			c+=7;
		buff[i]=c;
		x>>=4;
	}
	for (x=pDW[0],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		if ( c>'9')
			c+=7;
		buff[8+i]=c;
		x>>=4;
	}
	buff[16]=0;
}

上面这个函数首先将64位整数地址转化32位整数的地址,然后使用32位整数运算。代码是复杂了,但速度更快了。尽管如此,程序仍有优化的空间。

我们注意到,上面的函数包括2个循环,在每个循环中又有一个if语句,站在汇编语言视角,循环和if语句都是分支语句,可编译成比较跳转指令对。分支对CPU是个麻烦事儿,由于现代CPU普遍采用管线技术,在执行当前指令时,后面的指令已经被取到甚至译码完成。CPU遇到分支时,就需要预测那个分支最可能被执行到,从而将最可能被执行到的那个分支的代码加载到管线。当分支预测成功时,所有的指令可流畅地无停顿的执行。一旦分支预测失败,则不得不将管线中已加载测的指令全部丢弃,重新从正确的分支点取指和译码。分支预测的技术很复杂,完整的讲述需要一本书的内容。我们这里仅作简单介绍。分支预测的的实现通常是这样的,在首次遇到分支时,执行非跳转分支。在每次执行分支指令时,将实际执行情况(执行那个分支)存入历史记录。以后再遇到这个分支时,则可以根据历史记录来判断那个分支最可能被执行到。最简单的一种判断算法是,总是预测执行概率比较高的那个分支,这种分支预测方案对循环引起的分支最有效。比如一个循环次数为n的for循环,前n次总是从循环体底部跳转到头,只有最后一次循环不跳转,换言之,跳转分支执行的概率远高于非跳转分支,故CPU总是预测跳转分支。就上面的例子而言,两个循环都是固定次数的循环且循环次数很少。在这种情况下,编译器可使用循环展开的方法来消除分支,但是对于语句"if
( c>‘9‘ ) c+=7" 这样的分支,分支预测技术很难奏效,0-15之间的随机数,大于9的概率37.5%,即使CPU总是预测<=‘9‘的那个分支,也有37.5%的预测失败的概率,分支预测失败,CPU需要付出相当的代价,需要几个甚至10个额外的周期。

我的下一个版本用到的技术就是分支消除技术,通过消除分支来提高函数执行速度,为了消除分支,不得不使用额外的语句,虽然代码变多了,但函数执行速度大大加快。

//这是使用消除分支技术的C语言版,在i7-4700HQ,速度是上一个版本的2.6倍
void __fastcall UINT64_2_hexString_c3(UINT64 *p, char *buff)
{
	DWORD *pDW=(DWORD *)p;
	DWORD x;
	int i;
	for (x=pDW[1],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		char mask= 0 -( c>'9');	//the flag is 0xff when c>'9'
		buff[i]= c + ( mask & 7);
		x>>=4;
	}
	for (x=pDW[0],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		char mask= 0 -( c>'9');	//the flag is 0xff when c>'9'
		buff[i+8]= c + ( mask & 7);
		x>>=4;
	}
	buff[16]=0;
}

下面是终极版本,使用SSE2指令的汇编版,直接使用ALU指令编程的优化作用有限,甚至不如编译器。我们这里直接使用SSE2指令,SSE2指令主要使用XMM寄存器来工作,1个XMM寄存器可看成是4个DWORD,8个WORD,16个BYTE,1个UINT64位数转化为字符串后有16个字符,可全部装在一个XMM寄存器中,所以这个工作正好适合用SSE2指令来做。下面的汇编版的代码,用到几个16字节数组,要求16直接对齐,尽管在汇编中也可以定义16字节对齐,但我们这里把数组的定义放在C文件中,放在C文件中的好处是易于扩展,比如可在C文件中定义32字节对齐,我曾尝试在汇编文件中定义32字节对齐时,但汇编器总是报错。这里给出C语言中的常数数组的定义。

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

typedef unsigned char  BYTE;

#if defined(__GNUC__)    // GCC
    #define INTRIN_ALIGN(n)    __attribute__((aligned(n)))
#else
    #define INTRIN_ALIGN(n)    __declspec(align(n))
#endif    // #if defined(__GNUC__)    // GCC

INTRIN_ALIGN(16) BYTE i256_num_0s[16]=
{
	'0','0','0','0','0','0','0','0',
	'0','0','0','0','0','0','0','0',
};

INTRIN_ALIGN(16) BYTE i256_num_9s[16]=
{
	'9','9','9','9','9','9','9','9',
	'9','9','9','9','9','9','9','9',
};

INTRIN_ALIGN(16) BYTE i256_num_full7[16]=
{
	7,7,7,7,7,7,7,7,
	7,7,7,7,7,7,7,7,
};

下面是微软汇编器ml.exe格式的汇编语言源代码。

 	.686P
	.XMM
	.model	flat

_DATA	SEGMENT
	EXTRN _i256_num_0s:BYTE
	EXTRN _i256_num_9s:BYTE
	EXTRN _i256_num_full7:BYTE
_DATA	ENDS

PUBLIC @[email protected]

_TEXT	SEGMENT

; The XMM register definition for function _UINT64_2_hexString_sse2
XMM_R_DB_STR_0  TEXTEQU <XMM2>
XMM_R_DB_STR_9  TEXTEQU <XMM3>
XMM_R_DB_7	TEXTEQU <XMM4>
XMM_R_TMP	TEXTEQU <XMM1>

@[email protected] PROC

	movq    XMM0, mmword ptr [ecx]
	PSHUFD  XMM0, XMM0, 11001101b		;now XMM0 contain 2 QWORD and low 32bits in every QWORD is valid

	; We denote the value of xmm register with a string, "N" means a byte, "0" means a byte whose value is 0,
	; We use low-bytes first order,
  	; Now, the value of XMM0 is (NNNN0000,NNNN0000),contain 2 QWORD and contain 32 bits in every qword

	; the first round transform, 2 QWORD => 4 DWORD
	MOVDQA  XMM_R_TMP, XMM0

	PSLLQ  XMM0, 48				;toward high shift 48 bits
	; now  value of  XMM0 is (000000NN, 000000NN), now bit0-bit15 of every original QWORD is in XMM0 

	PSRLQ  XMM_R_TMP, 16		;toward low shift 16 bits
	; now  value of  XMM1 is (NN000000,NN000000), bit16-bit31 of every original QWORD is in XMM1

	PSRLQ  XMM0, 16				;toward low shift 16 bits
	; now  value of  XMM0 is  (0000NN00,0000NN00), now bit0-bit15 of every original QWORD is in XMM0 

	POR		XMM0, XMM_R_TMP		;merge bit0-bit15 and bit16-bit31
	; now  value of  XMM0 is (NN00,NN00,NN00,NN00), contain 4 DWORD, and 16 bits in every DWORD is valid

	; the second round transform, 4 DWORD  => 8 WORD
	MOVDQA  XMM_R_TMP, XMM0
	PSLLD  XMM0, 24				;toworad high shift 24 bits
	; now  value of  XMM0 is (000N,000N,000N,000N), the bit0-bit7 of every original DWORD is in XMM0 

	PSRLD  XMM_R_TMP, 8			;toworad low shift 8 bits
	; now  value of  XMM0 is (N000,N000,N000,N000), the bit8-bit15 of every original DWORD is in XMM1

	PSRLD  XMM0, 8				;toworad low shift 8 bits
	; now  value of  XMM0 is (00N0,00N0,00N0,00N0), the bit0-bit7 of every original DWORD is in XMM0 

	POR		XMM0, XMM_R_TMP		;merge bit0-bit7 and bit8-bit16
	; now  value of  XMM0 is (N0,N0,N0,N0,N0,N0,N0,N0), contain 8 WORD, and 8 bits in every WORD is valid

	; the third round transform, 8 WORD  => 16 BYTE
	MOVDQA  XMM_R_TMP, XMM0
	PSLLW  XMM0, 12				;toworad high shift 12 bits
	; the bit0-bit3 of every original WORD is in XMM0 

	PSRLW  XMM_R_TMP, 4			;toworad low shift 4 bits
	; now  the bit4-bit7 or every original WORD is in XMM1

	PSRLW  XMM0, 4				; toworad low shift 4 bits
	; the bit0-bit3 of every original WORD is in XMM0 

	POR		XMM0, XMM_R_TMP		;merge bit0-bit3 and bit4-7
	; now  value of  XMM0 is (N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,) contain 16 BYTE, the value range of every byte is 0-15

	; pre-load array to XMM  register
	MOVDQA  XMM_R_DB_STR_0, xmmword ptr _i256_num_0s
	MOVDQA  XMM_R_DB_STR_9, xmmword ptr _i256_num_9s

	POR     XMM0, XMM_R_DB_STR_0		;now the BYTE[i] of XMM0 is '0' to '0'+15
	MOVDQA  XMM_R_TMP, XMM0			 ;now the BYTE[i] of XMM_R_TMP is '0' to '0'+15

	MOVDQA  XMM_R_DB_7,	xmmword ptr _i256_num_full7		;now XMM_R_DB_7 is full 7

	PCMPGTB  XMM_R_TMP, XMM_R_DB_STR_9	; BYTE[i] of XMM_R_TMP (0<=i<15) is -1 if BYTE[i]>'9'
	PAND     XMM_R_TMP, XMM_R_DB_7		; BYTE[i] of XMM_R_TMP (0<=i<15) is 7, if BYTE[i]>'9'

	PADDB    XMM0, XMM_R_TMP		;final result,now the BYTE[i] of XMM0 is '0'-'9' or 'A' to 'F'

	MOVDQU   xmmword ptr [edx], XMM0
	mov    byte ptr [edx+16], 0
	ret	0
@[email protected] ENDP

_TEXT	ENDS

END

上面我没有将英文注释翻译成中文。这是因为,对于汇编代码,高手不用讲,初学者不会看,故这里就不再给出更多的说明了。

下面给出主程序中的全部代码。

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

/*
这个程序示范使用优化技术来实现将64位整数转化为16进制字符串。
包括3个C语言的版本和一个使用SSE2指令的汇编版本。
经测试,在我的电脑上(I7-4700HQ),汇编版本的速度是C语言普通版20倍,是分支消除技术版本的7.5倍。

作者:Baocheng Liang
完成日期:2015-7-30, 版权所有。
*/

#define ARRAY_LEN   4096
#define LOOP_COUNT	2048

typedef unsigned long long UINT64;
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;

UINT64 g_nums[ARRAY_LEN];
char g_buff[ARRAY_LEN*16+16];

extern double currTime(); //使用高精度计时器

extern void __fastcall UINT64_2_hexString_sse2(UINT64 *p, char *buff);		//使用SSE2指令的汇编版本

extern void __fastcall UINT64_2_hexString_c1(UINT64 *p, char *buff);		//最普通的C语言版本

extern void __fastcall UINT64_2_hexString_c2(UINT64 *p, char *buff);		//最普通32位整数的的C语言版本

extern void __fastcall UINT64_2_hexString_c3(UINT64 *p, char *buff);		//使用分支消除技术的C语言版本

typedef void ( __fastcall *lpfn_UInt64_2_hexString)(UINT64 *p, char *buff);

//这是C语言普通版,直接使用64位整数逻辑指令和算术指令
void __fastcall UINT64_2_hexString_c1(UINT64 *p, char *buff)
{
	UINT64 x=*p;
	int i;
	for (i=15;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		if ( c>'9')
			c+=7;
		buff[i]=c;
		x>>=4;
	}
	buff[16]=0;
}

//这是C语言改进版,使用32位整数逻辑指令和算术指令
void __fastcall UINT64_2_hexString_c2(UINT64 *p, char *buff)
{
	DWORD *pDW=(DWORD *)p;
	DWORD x;
	int i;
	for (x=pDW[1],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		if ( c>'9')
			c+=7;
		buff[i]=c;
		x>>=4;
	}
	for (x=pDW[0],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		if ( c>'9')
			c+=7;
		buff[8+i]=c;
		x>>=4;
	}
	buff[16]=0;
}

//这是使用消除分支技术的C语言版,在i7-4700HQ,速度是上一个版本的2.6倍
void __fastcall UINT64_2_hexString_c3(UINT64 *p, char *buff)
{
	DWORD *pDW=(DWORD *)p;
	DWORD x;
	int i;
	for (x=pDW[1],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		char mask= 0 -( c>'9');	//the flag is 0xff when c>'9'
		buff[i]= c + ( mask & 7);
		x>>=4;
	}
	for (x=pDW[0],i=7;i>=0;i--)
	{
		char c= (x & 0xf)+'0';
		char mask= 0 -( c>'9');	//the flag is 0xff when c>'9'
		buff[i+8]= c + ( mask & 7);
		x>>=4;
	}
	buff[16]=0;
}

void test_UINT64_2_hexString(lpfn_UInt64_2_hexString fp, const char *funcName)
{
	UINT64 arr[]={
		0x0123456789abcdef,
		0x02468ACE13579BDF,
		0xaaaaaaaaaaaaaaaa,
		0xffffffffffffffff,
	};
	int i;
	char buff[17*4];
	memset(buff,0,sizeof(buff));
	printf("Test function %s\n",funcName);
	for (i=0;i<4;i++)
	{
		fp(arr+i,buff+i*17);
		printf("%s\n", buff+i*17);
	}
}

void perf_UINT64_2_hexString(lpfn_UInt64_2_hexString fp,const char *funcName)
{
	int i,j;
	UINT64 x;
	char *p;
	double t;

	//初始化全局数组g_nums
	for (i=0;i<ARRAY_LEN;i++)
	{
		p=(char *)(&x);
		for (j=0;j<8;j++)
			p[j]= (rand() & 0xff);//get a 64bit random number
		g_nums[i]=x;
	}

	t=currTime();	//计时开始
	g_buff[0]=0;
	for ( i=0;i<LOOP_COUNT;i++)
	{
		p=g_buff;
		for (j=0;j<ARRAY_LEN;j++)
		{
			fp( g_nums+j,p);
			p+=16;
		}
	}
	t=(currTime()-t)*1000000000;	//转化时间到纳秒
	printf("It take %.2f ns to run function %s\n",t/(LOOP_COUNT*ARRAY_LEN),funcName);
	printf("strlen(buff) is %d\n",strlen(g_buff)); 

}

//功能测试
void test_function()
{
	test_UINT64_2_hexString(UINT64_2_hexString_c1,  "UINT64_2_hexString_c1");
	test_UINT64_2_hexString(UINT64_2_hexString_c2,  "UINT64_2_hexString_c2");
	test_UINT64_2_hexString(UINT64_2_hexString_c3,  "UINT64_2_hexString_c3");
	test_UINT64_2_hexString(UINT64_2_hexString_sse2,"UINT64_2_hexString_sse2");
}

//性能测试
void perf_function()
{
	perf_UINT64_2_hexString(UINT64_2_hexString_c1,  "UINT64_2_hexString_c1");
	perf_UINT64_2_hexString(UINT64_2_hexString_c2,  "UINT64_2_hexString_c2");
	perf_UINT64_2_hexString(UINT64_2_hexString_c3,  "UINT64_2_hexString_c3");
	perf_UINT64_2_hexString(UINT64_2_hexString_sse2,"UINT64_2_hexString_sse2");
}

int main(int argc, char* argv[])
{
	test_function();
	perf_function();
	return 0;
}

补充:

这里给出跨平台的计时函数currTime的代码。

#if defined(_WIN32)

#include <windows.h>

static LARGE_INTEGER freq;

static BOOL initFreq()
{
	BOOL ret;
	if ( !QueryPerformanceFrequency( &freq) )
	{	ret=FALSE;	}
	else
	{	ret=TRUE;	}
	return ret;
}

double currTime() //使用高精度计时器
{
	LARGE_INTEGER performanceCount;
	BOOL result;
	double time=0.0;
	BOOL bRet=TRUE;

	if (freq.QuadPart==0)
	{
		bRet=initFreq();
	}

	if (bRet)
	{
		result=QueryPerformanceCounter(  &performanceCount );
		time= performanceCount.HighPart * 4294967296.0 + performanceCount.LowPart;
		time=time / (   freq.HighPart * 4294967296.0 + freq.LowPart);
	}
	return time;
}

#elif defined(__linux__)
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>

double currTime()
{
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return (double)(tv.tv_sec) + (double)(tv.tv_usec)/1000000.00;
}
#else
 #error do not support this complier
#endif

关于编译:本程序使用C语言和汇编语言混合编程。C语言源文件直接加到VC工程中即可。汇编语言使用VC中自带的汇编器ml.exe来编译。

方法

1. 将汇编文件加入到VC工程中。

2.选中文件,右键属性菜单,Item type选Custom Build Tool. 在Command Line一栏: 输入“ml /coff /c %(FullPath)", 在Outputs 一栏输入"%(Filename).obj;%(Outputs)"

测试结果
函数 c1 c2 c3 sse2
时间(纳秒) 67.95 52.71 20.16 3.18
相对速度 100% 129% 335% 2138%



版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 23:19:11

一个卓有成效的汇编优化范例--使用SSE2指令优化进制转化的相关文章

输入一个十进制数N,将它转换成R进制数输出(运用递归实现)

#include<stdio.h> int cnt=0;                             //用来记录每个进制存放的位置 char num[20];   //用来存放每个进制的数 void fun(int a,int b) { if(a==0)   //递归的终止条件 return; fun(a/b,b);                       //顺序递归 num[cnt++] = a%b;                  //逆序递归 } int main(

给定一个十进制数,将其转化为N进制数-----17年滴滴笔试题

题目:给定一个十进制数M,将其转化为N进制数,其中2<=N<=16,其中N为32为整型数; 输入:M N,如7 2 输出转化结果:111 注意点:考虑负数的情况,记得添加负号: 思路:1. 使用一个辅助栈来存放M对N取余的结果(M%N):   2. 处理余数<=9和>9两种情况,因为按照16进制,>9的数用ABCDEF取代   3. 再更新取余后M的取值:M=M/N:   4. 循环处理2,3两个步骤,直到M=0: 5. 处理最终结果,出栈,直到栈为空: 代码如下: pack

[转]一个用户SQL慢查询分析,原因及优化

来源:http://blog.rds.aliyun.com/2014/05/23/%E4%B8%80%E4%B8%AA%E7%94%A8%E6%88%B7sql%E6%85%A2%E6%9F%A5%E8%AF%A2%E5%88%86%E6%9E%90%EF%BC%8C%E5%8E%9F%E5%9B%A0%E5%8F%8A%E4%BC%98%E5%8C%96/ 问题描述 一个用户反映先线一个SQL语句执行时间慢得无法接受.SQL语句看上去很简单(本文描述中修改了表名和字段名):SELECT cou

网站推广优化(SEO,网站关键字优化,怎么优化网站,如何优化网站关键字)

网站推广优化教程100条(完整版)下面介绍新手建站推广完美教程,各位根据自己的实际情况酌情选用: 1.准备个好域名.①.尽量在5位数内,当然也不一定,反正要让用户好记.(看个人):②.尽量用顶级的域名,搜索排名感觉好一点.③.做中文站最好用拼音注册,不要问为什么.看百度(baidu.com)就是很好证明.④.域名的安全性,最好选择有些权威的注册商.⑤.好域名或者说主域名最好注册3年以上,因为百度在扒正规站的毛的时候,会看这些资料,他会觉得你不会很快转行的…… 2.网站空间很重要①.速度打开速度慢

深入理解图优化与g2o:图优化篇

前言 本节我们将深入介绍视觉slam中的主流优化方法——图优化(graph-based optimization).下一节中,介绍一下非常流行的图优化库:g2o. 关于g2o,我13年写过一个文档,然而随着自己理解的加深,越发感觉不满意.本着对读者更负责任的精神,本文给大家重新讲一遍图优化和g2o.除了这篇文档,读者还可以找到一篇关于图优化的博客: http://blog.csdn.net/heyijia0327 那篇文章有作者介绍的一个简单案例,而本文则更注重对图优化和g2o的理解与评注. 本

汇编中的跳转指令

能修改CS以及IP的指令都是转移指令.它分为段内转移,段间转移. 段内转移:只修改IP的值 段间转移:同时修改CS以及IP的值 段内转移根据转移的距离远近分为:短转移,近转移 短转移:转移范围为-128 – 127 近转移:转移范围为-32768 –32767 根据转移情况又分为: 无条件转移指令 条件转移指令 循环指令 过程 中断 <1> jmp short xxx(行号) 这个是短转移指令,实现段内的转移,在翻译成机器码的时候,码内并没有目标地址,有的只是转移位移,这样做的好处就是防止目标

根据自己的需要,把别人开发好的东西搬过来,优化and重构,在优化的过程中,甚至也会弄出一套全新的东西(转)

赵海平在今年三月份来到阿里,听毕玄(他现任主管)说去年五六月份就跟赵海平聊上了.有人问:为啥 BAT 三大巨头,你看中了阿里巴巴?在今天现场达一千多人的分享中赵海平给出了回复:“因为百度和腾讯没找我呗~”,他笑道,“百度以搜索为核心,优化了很多年了,估计也没啥可以优化的了:而腾讯除了 QQ 和微信,也没什么大型应用(别跟人家说哦)”.这不是原话哈,赵海平还是相当谦虚并且能言的,思维很开阔,两个小时的分享内容丰富,时不时还插两个故事,起初进场的手机和电脑都很自觉的收起来了~旁边的同事侃道:“高 P

Android 性能优化——之控件的优化

Android 性能优化——之控件的优化 前面讲了图像的优化,接下来分享一下控件的性能优化,这里主要是面向自定义View的优化. 1.首先先说一下我们在自定义View中可能会犯的3个错误: 1)Useless calls to onDraw():我们知道调用View.invalidate()会触发View的重绘,有两个原则需要遵守,第1个是仅仅在View的内容发生改变的时候才去触发invalidate方法, 第2个是尽量使用ClipRect等方法来提高绘制的性能. 2)Useless pixel

【SQL server初级】数据库性能优化三:程序操作优化

数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第三部分 数据库性能优化三:程序操作优化 概述:程序访问优化也可以认为是访问SQL语句的优化,一个好的SQL语句是可以减少非常多的程序性能的,下面列出常用错误习惯,并且提出相应的解决方案 一.操作符优化 1. IN.NOT IN 操作符 IN和EXISTS 性能有外表和内表区分的,但是在大数据量的表中推荐用EXISTS 代替IN . Not IN 不走索引的是绝对不能用的,可以用NOT EXISTS 代替 2. IS