作为正式接触汇编的开篇,本文将研究二维数组的遍历问题。在图像处理中,通常需要遍历图像像素(即二维数组)。下面给出三个版本的遍历函数,并研究他们的汇编代码(VC2010编译器,x86版,Release模式)。
(1)在两层循环内每次根据行列索引计算元素位置。
(2)为了避免在内存循环内的乘法计算,可在每次外层循环中计算好行起始地址,内层循环每次执行++操作。
(3)将外层循环的乘法操作也去掉,在循环外部先计算好数组的起始地址,内层循环每次执行++操作即可。
测试程序实现对图像的反相操作(即B=255-A)。我们的直观感觉时他们的访问效率应该逐步提升的,本人之前也是一直用第三种方法来遍历图像像素的。但本次测试发现,效率根本没有提升。究其原因,是编译器做了优化。下面分别给出三个函数以及他们对应的汇编代码(VS中调试—>窗口-->反汇编可查看),并对汇编代码做了注释。
(1)版本1
inline void InvimageV1(uchar *A ,uchar *B,int Width,int Height)
{
for (int Y=0;Y<Height;Y++)
{
for (int X=0;X<Width;X++)
{
B[Y*Width+X]=255-A[Y*Width+X];
}
}
}
汇编:
其中黄色覆盖区域为外层循环,绿色覆盖区域为内层循环。
inline void InvimageV2(uchar *A ,uchar *B,int Width,int Height)
{
uchar *LinePS,*LinePD;
for (int Y=0;Y<Height;Y++)
{
LinePS=A+Y*Width;
LinePD=B+Y*Width;
for (int X=0;X<Width;X++)
{
LinePD[X]=255-LinePS[X];
}
}
}
汇编:
其中黄色覆盖区域为外层循环,绿色覆盖区域为内层循环。
(3)版本3
inline void InvimageV3(uchar *A ,uchar *B,int Width,int Height)
{
uchar *LinePS=A,*LinePD=B;
for (int Y=0;Y<Height;Y++)
{
for (int X=0;X<Width;X++)
{
LinePD[0]=255-LinePS[0];
LinePS++;
LinePD++;
}
}
}
汇编:
其中黄色覆盖区域为外层循环,绿色覆盖区域为内层循环。
可以看出三个版本的内层循环操作上并没有什么差异,Release模式下编译器已经将循环内计算地址中的乘法计算优化成加法,而我们第三个版本的目的正是去掉乘法计算,因此三者执行效率上并没有多大差异。
下面给出完整的测试代码:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
inline void InvimageV1(uchar *A ,uchar *B,int Width,int Height)
{
for (int Y=0;Y<Height;Y++)
{
for (int X=0;X<Width;X++)
{
B[Y*Width+X]=255-A[Y*Width+X];
}
}
}
inline void InvimageV2(uchar *A ,uchar *B,int Width,int Height)
{
uchar *LinePS,*LinePD;
for (int Y=0;Y<Height;Y++)
{
LinePS=A+Y*Width;
LinePD=B+Y*Width;
for (int X=0;X<Width;X++)
{
LinePD[X]=255-LinePS[X];
}
}
}
inline void InvimageV3(uchar *A ,uchar *B,int Width,int Height)
{
uchar *LinePS=A,*LinePD=B;
for (int Y=0;Y<Height;Y++)
{
for (int X=0;X<Width;X++)
{
LinePD[0]=255-LinePS[0];
LinePS++;
LinePD++;
}
}
}
int main()
{
Mat src,dst;
int nWidth,nHeight,iterNum=1000;
uchar *pSrc,*pDst;
int64 t1,t2;
src=imread("1.jpg",0);
dst = src.clone();
nWidth=src.cols;
nHeight=src.rows;
pSrc=src.data;
pDst=dst.data;
imshow("原始图",src);
t1=getTickCount();
for (int i=0;i<iterNum;i++)
{
InvimageV1(pSrc ,pDst,nWidth,nHeight);
}
t2=getTickCount();
cout<<"InvimageV1:"<<(t2 - t1)*1000./getTickFrequency()<<"ms"<<endl;
imshow("InvimageV1",dst);
t1=getTickCount();
for (int i=0;i<iterNum;i++)
{
InvimageV2(pSrc ,pDst,nWidth,nHeight);
}
t2=getTickCount();
cout<<"InvimageV2:"<<(t2 - t1)*1000./getTickFrequency()<<"ms"<<endl;
imshow("InvimageV2",dst);
t1=getTickCount();
for (int i=0;i<iterNum;i++)
{
InvimageV3(pSrc,pDst,nWidth,nHeight);
}
t2=getTickCount();
cout<<"InvimageV3:"<<(t2 - t1)*1000./getTickFrequency()<<"ms"<<endl;
imshow("InvimageV3",dst);
waitKey(0);
}