- 浏览: 235932 次
- 性别:
- 来自: 南京
最新评论
-
baby8117628:
vc下mp3 IDv1和IDV2的读取 -
gezexu:
你好,我按照你的步骤一步步进行但是安装libvorbis的时候 ...
linux如何搭建强大的FFMPEG环境 -
ini_always:
帅哥,转载也把格式做好点,另外出处也要注明一下吧。。。
MP3文件格式解析
本文讲述在编写C程序代码的常用优化办法,分为I/O篇,内存篇,算法篇,MMX汇编篇。
一.I/O篇
如果有文件读写的话,那么对文件的访问将是影响程序运行速度的一大因素。
提高文件访问速度的主要办法有两个:一是采用内存映射文件,二是使用内存缓冲。
可见,一般的当内存缓冲区大小为8192的时候,性能就已经是最佳的了,这也就是为什么在H.263等图像编码程序中,
缓冲区大小为8192的原因(有的时候也取2048大小)。使用内存缓冲区方法的好处主要是便于移植,占用内存少,便于硬件实现等。
下面是读取文件的C伪码:
int Len;
BYTE buffer[8192];
ASSERT(buffer==NULL);
If buffer is empty
{
Len=read(File,buffer,8192);
If(len==0) No data and exit;
}
但是如果内存比较大的时候,采用内存映射文件可以达到更佳性能,并且编程实现简单。
下面是一点建议:
① 内存映射文件不能超过虚拟内存的大小,最好也不要太大,如果内存映射文件接近虚拟内存大小的时候,
反而会大大降低程序的速度(其实是因为虚拟内存不足导致系统运行效率降低),这个时候,可以考虑分块映射,
但是我觉得如果这样,还不如直接使用内存缓冲来得直接一些。
② 可以将两种方法统一使用,如我在编大图像文件数据处理的时候(因为是Unix工作站,内存很大GB单位)使用了内存映射文件,
但是为了最佳性能,也使用了一行图像缓存,这样在读取文件中数据的时候,
就保证了仅仅是顺序读写(内存映射文件中,对顺序读写有专门的优化)。
③ 在写文件的时候使用内存映射文件要有一点小技巧:应该先创建足够大的文件,然后将这个文件映射,在处理完这个文件的时候,
用函数SetFilePointer和SetEndOfFile来对文件进行截尾。
④ 对内存映射文件进行操作与对内存进行操作类似(使用起来就象数组一样),那么如果有大块数据读写的时候,
切记使用memcpy()函数(或者CopyMemory()函数)
总之,如果要使用内存映射文件,必须:1.处理的文件比较的小,2.处理的文件很大,但是运行环境内存也很大,
并且一般在运行该程序的时候不运行其他消耗内存大的程序,同时用户对速度有特别的要求,而且对内存占用没有什么要求。
如果以上两个条件不满足的时候,建议使用内存缓冲区的办法。
二.内存篇
在上一篇中我们讲述了如何优化文件的读写,这一篇则主要讲述对内存操作的优化,主要有数组的寻址,指针链表等,还有一些实用技巧。
I.优化数组的寻址
在编写程序时,我们常常使用一个一维数组a[M×N]来模拟二维数组a[N][M],这个时候访问a[]一维数组的时候:我们经常是这样写a[j×M+i](对于a[j][i])。
这样写当然是无可置疑的,但是显然每个寻址语句j×M+i都要进行一次乘法运算。现在再让我们看看二维数值的寻址,
说到这里我们不得不深入到C编译器在申请二维数组和一维数组的内部细节上――实际在申请二位数组和一维数组,
编译器的处理是不一样的,申请一个a[N][M]的数组要比申请一个a[M×N]的数组占用的空间大!二维数组的结构是分为两部分的:
① 是一个指针数组,存储的是每一行的起始地址,这也就是为什么在a[N][M]中,a[j]是一个指针而不是a[j][0]数据的原因。
② 是真正的M×N的连续数据块,这解释了为什么一个二维数组可以象一维数组那样寻址的原因。(即a[j][i]等同于(a[0])[j×M+i])
清楚了这些,我们就可以知道二维数组要比(模拟该二维数组的)一维数组寻址效率高。因为a[j][i]的寻址仅仅是访问指针数组得到j行的地址,然后再+i,是没有乘法运算的!
所以,在处理一维数组的时候,我们常常采用下面的优化办法:(伪码例子)
int a[M*N];
int *b=a;
for(…){
b[…]=…;
…………
b[…]=…;
b+=M;
}
这个是遍历访问数组的一个优化例子,每次b+=M就使得b更新为下一行的头指针。当然如果你愿意的话,可以自己定义一个数组指针来存储每一行的起始地址。然后按照二维数组的寻址办法来处理一维数组。不过,在这里我建议你干脆就直接申请一个二维数组比较的好。下面是动态申请和释放一个二维数组的C代码。
int get_mem2Dint(int ***array2D, int rows, int columns) //h.263源代码
{
int i;
if((*array2D = (int**)calloc(rows, sizeof(int*))) == NULL) no_mem_exit(1);
if(((*array2D)[0] = (int* )calloc(rows*columns,sizeof(int ))) == NULL) no_mem_exit(1);
for(i=1 ; i<rows ; i++)
(*array2D)[i] = (*array2D)[i-1] + columns ;
return rows*columns*sizeof(int);
}
void free_mem2D(byte **array2D)
{
if (array2D){
if (array2D[0]) free (array2D[0]);
else error ("free_mem2D: trying to free unused memory",100);
free (array2D);
} else{
error ("free_mem2D: trying to free unused memory",100);
}
}
顺便说一下,如果你的数组寻址有一个偏移量的话,不要写为a[x+offset],而应该为 b=a+offset,然后访问b[x]。
不过,如果你不是处理对速度有特别要求的程序的话,这样的优化也就不必要了。记住,如果编普通程序的话,可读性和可移值性是第一位的。
II.从负数开始的数组
在编程的时候,你是不是经常要处理边界问题呢?在处理边界问题的时候,经常下标是从负数开始的,通常我们的处理是将边界处理分离出来,单独用额外的代码写。那么当你知道如何使用从负数开始的数组的时候,边界处理就方便多了。下面是静态使用一个从-1开始的数组:
int a[M];
int *pa=a+1;
现在如果你使用pa访问a的时候就是从-1到M-2了,就是这么简单。(如果你动态申请a的话,free(a)可不要free(pa)因为pa不是数组的头地址)
III.我们需要链表吗
相信大家在学习《数据结构》的时候,对链表是相当熟悉了,所以我看有人在编写一些耗时算法的时候,也采用了链表的形式。这样编写当然对内存的占用(似乎)少了,可是速度呢?如果你测试:申请并遍历10000个元素链表的时间与遍历相同元素的数组的时间,你就会发现时间相差了百倍!(以前测试过一个算法,用链表是1分钟,用数组是4秒钟)。所以这里我的建议是:在编写耗时大的代码时,尽可能不要采用链表!
其实实际上采用链表并不能真正节省内存,在编写很多算法的时候,我们是知道要占用多少内存的(至少也知道个大概),那么与其用链表一点点的消耗内存,不如用数组一步就把内存占用。采用链表的形式一定是在元素比较少,或者该部分基本不耗时的情况下。
(我估计链表主要慢是慢在它是一步步申请内存的,如果能够象数组一样分配一个大内存块的话,应该也不怎么耗时,这个没有具体测试过。仅仅是猜想)
三.算法篇
在上一篇中我们讲述了对内存操作的优化,这一篇则主要讲述一些常用的优化算法。这个东东太多,内容可能会有点凌乱,见谅。
I.从小处说起:
先说说一些小地方先:
① 比如n/2写为n>>1这个是常用的方法,不过要注意的是这两个不是完全等价的!因为:如果n=3的话,n/2=1;n>> 1=1;但是,如果n=-3的话,n/2=-1;n>>1=-2所以说在正数的时候,他们都是向下取整,但是负数的时候就不一样了。(在 JPG2000中的整数YUV到RGB变换一定要使用>>来代替除法就是这个道理)
② 还有就是a=a+1要写为a++; a=a+b要写为a+=b(估计一般用VB的才会写a=a+1)
③ 将多种运算融合:比如a[i++];就是先访问a[i],再令i加1;从汇编的角度上说,这个确实是优化的,如果写为a[i],和i++的话,有可能就会有两次的对i变量的读,一次写(具体要看编译器的优化能力了),但是如果a[i++]的话,就一定只读写i变量一次。不过这里有一个问题要注意:在条件判断内的融合一定要小心,比如:(idct变换中的0块判断,陈王算法)
if (!((x1 = (blk[8*4]<<8)) | (x2 = blk[8*6]) | (x3 = blk[8*2]) | (x4 = blk[8*1]) | (x5 = blk[8*7]) | (x6 = blk[8*5]) | (x7 = blk[8*3])))
在条件判断中融合了赋值语句,但是实际上如果条件为真的话,是不需要这些赋值语句的,也就是说当条件真的时候,多了一些垃圾语句,这些是在h263源码上的问题,虽然这些垃圾语句使得计算0块的时候,时间增加了30%,但是由于idct仅仅占1%的时间,0块又仅仅30%~70%的时间,所以这些性能损失是没有什么关系的。(这是后来我用汇编改写源码的时候得到的结论)。这里也说明了,程序优化一定重点在最耗时的地方。对于不耗时的代码优化是没有太大的实用意义的。
II.以内存换速度:
天下总是难有双得的事情,编程也是一样,大多数情况,速度同内存(或者是性能,比如说压缩性能什么的)是不可兼得的。目前程序加速的常用算法一个大方面就是利用查表来避免计算(比如在jpg有huffman码表,在YUV到RGB变换也有变换表)这样原来的复杂计算现在仅仅查表就可以了,虽然浪费了内存,不过速度显著提升,还是很划算的。在数据库查询里面也有这样的思想,将热点存储起来以加速查询。现在介绍一个简单的例子,(临时想的,呵呵):比如,在程序中要经常(一定要是经常!)计算1000到2000的阶乘,那么我们可以使用一个数组a[1000]先把这些值算好,保留下来,以后要计算1200!的时候,查表a[1200-1000]就可以了。
III.化零为整
由于零散的内存分配,以及大量小对象建立耗时很大,所以对它们的优化有时会很有效果,比如上一篇我说的链表存在的问题,就是因为大量的零散内存分配。现在就从一个vb的程序说起,以前我用vb给别人编小程序的时候,(呵呵,主要是用vb编程比vc快,半天就可以写一个)在使用MSFlexGrid控件的时候(就是一个表格控件),发现如果一行一行的增加新行,刷新速度十分的慢,所以我就每次增加100行,等到数据多到再加新行的时候,再加100行,这样就 “化零为整”了,使用这样的方法,刷新的速度比原来快了n倍!其实这样的思想应用很多,如:程序运行的时候,其实就占用了一定的空间,后来的小块内存分配是先在这个空间上的,这就保证了内存碎片尽可能的少,同时加快运行速度。
IV.条件语句或者case语句将最有可能的放在前面
优化效果不明显。想得到就用吧,想不到就算了。
V.为了程序的可读性,不去做那些编译器可以做的或者优化不明显的处理:
这个是很重要的,一个普通程序的好坏,主要是它的可读性,可移植性,可重用性,然后才是它的性能。所以,如果编译器本身可以帮助我们优化的话,我们就没有必要写那些大家都不怎么看得懂的东西。比如a=52(结束)-16(起始);这样写可能是因为在别人读程序的时候,一下就明白了a的含义。我们不用写为 a=36,因为编译器是会帮我们算出来的。
IV.具体情况具体分析:
具体情况具体分析,这是放之四海而皆准的真理。没有具体的分析,就不能针对问题灵活应用解决的办法。下面我就说说分析的方法。即如何找到程序的耗时点:(从最简单的办法说起,先说明一个函数GetTickCount(),这个函数在头尾各调用一次,返回值相减就是程序的耗时,精确到1ms)
① 对于认为是比较耗时的函数,运行两次,或者将函数内部的语句注释掉(要保证程序可以运行),看看多(或者少了)多少时间。这个办法简单不精确。
② 每个地方都用GetTickCount()函数测试时间,注意GetTickCount()只能精确到ms。一般的小于10ms就不太精确了。
③ 使用另外一个函数QueryPerformanceCounter(&Counter)和 QueryPerformanceFrequency(&Frequency),前面计算cpu时钟周期,后面是cpu频率相除就是时间。不过如果你要精确到这一步的话,建议将进程设置为最高级别,防止它被阻塞。
最后讲讲我处理的一个程序:程序要求我忘了,反正里面有一个函数,函数里面有一个大的循环,循环内部的处理比较耗时。结果最初程序表现出来的状况是开始还很快,越到后面越慢;我在跟踪程序中变量的时候,发现最初的循环在循环几次后就跳出了,而后面的循环次数越来越多。找到了为什么慢的原因,就可以对症下药了,我的处理是每次循环不是从头开始,而是从上一次循环跳出的地方开始左右循环(因为可能下一次循环跳出的地方别上一次的小,所以也要遍历前面的),这样程序的速度在后面也很快了。我讲这个的道理就是在实际运用中,要具体的分析程序慢的真正原因,才能达到最佳的优化效果。
一.I/O篇
如果有文件读写的话,那么对文件的访问将是影响程序运行速度的一大因素。
提高文件访问速度的主要办法有两个:一是采用内存映射文件,二是使用内存缓冲。
可见,一般的当内存缓冲区大小为8192的时候,性能就已经是最佳的了,这也就是为什么在H.263等图像编码程序中,
缓冲区大小为8192的原因(有的时候也取2048大小)。使用内存缓冲区方法的好处主要是便于移植,占用内存少,便于硬件实现等。
下面是读取文件的C伪码:
int Len;
BYTE buffer[8192];
ASSERT(buffer==NULL);
If buffer is empty
{
Len=read(File,buffer,8192);
If(len==0) No data and exit;
}
但是如果内存比较大的时候,采用内存映射文件可以达到更佳性能,并且编程实现简单。
下面是一点建议:
① 内存映射文件不能超过虚拟内存的大小,最好也不要太大,如果内存映射文件接近虚拟内存大小的时候,
反而会大大降低程序的速度(其实是因为虚拟内存不足导致系统运行效率降低),这个时候,可以考虑分块映射,
但是我觉得如果这样,还不如直接使用内存缓冲来得直接一些。
② 可以将两种方法统一使用,如我在编大图像文件数据处理的时候(因为是Unix工作站,内存很大GB单位)使用了内存映射文件,
但是为了最佳性能,也使用了一行图像缓存,这样在读取文件中数据的时候,
就保证了仅仅是顺序读写(内存映射文件中,对顺序读写有专门的优化)。
③ 在写文件的时候使用内存映射文件要有一点小技巧:应该先创建足够大的文件,然后将这个文件映射,在处理完这个文件的时候,
用函数SetFilePointer和SetEndOfFile来对文件进行截尾。
④ 对内存映射文件进行操作与对内存进行操作类似(使用起来就象数组一样),那么如果有大块数据读写的时候,
切记使用memcpy()函数(或者CopyMemory()函数)
总之,如果要使用内存映射文件,必须:1.处理的文件比较的小,2.处理的文件很大,但是运行环境内存也很大,
并且一般在运行该程序的时候不运行其他消耗内存大的程序,同时用户对速度有特别的要求,而且对内存占用没有什么要求。
如果以上两个条件不满足的时候,建议使用内存缓冲区的办法。
二.内存篇
在上一篇中我们讲述了如何优化文件的读写,这一篇则主要讲述对内存操作的优化,主要有数组的寻址,指针链表等,还有一些实用技巧。
I.优化数组的寻址
在编写程序时,我们常常使用一个一维数组a[M×N]来模拟二维数组a[N][M],这个时候访问a[]一维数组的时候:我们经常是这样写a[j×M+i](对于a[j][i])。
这样写当然是无可置疑的,但是显然每个寻址语句j×M+i都要进行一次乘法运算。现在再让我们看看二维数值的寻址,
说到这里我们不得不深入到C编译器在申请二维数组和一维数组的内部细节上――实际在申请二位数组和一维数组,
编译器的处理是不一样的,申请一个a[N][M]的数组要比申请一个a[M×N]的数组占用的空间大!二维数组的结构是分为两部分的:
① 是一个指针数组,存储的是每一行的起始地址,这也就是为什么在a[N][M]中,a[j]是一个指针而不是a[j][0]数据的原因。
② 是真正的M×N的连续数据块,这解释了为什么一个二维数组可以象一维数组那样寻址的原因。(即a[j][i]等同于(a[0])[j×M+i])
清楚了这些,我们就可以知道二维数组要比(模拟该二维数组的)一维数组寻址效率高。因为a[j][i]的寻址仅仅是访问指针数组得到j行的地址,然后再+i,是没有乘法运算的!
所以,在处理一维数组的时候,我们常常采用下面的优化办法:(伪码例子)
int a[M*N];
int *b=a;
for(…){
b[…]=…;
…………
b[…]=…;
b+=M;
}
这个是遍历访问数组的一个优化例子,每次b+=M就使得b更新为下一行的头指针。当然如果你愿意的话,可以自己定义一个数组指针来存储每一行的起始地址。然后按照二维数组的寻址办法来处理一维数组。不过,在这里我建议你干脆就直接申请一个二维数组比较的好。下面是动态申请和释放一个二维数组的C代码。
int get_mem2Dint(int ***array2D, int rows, int columns) //h.263源代码
{
int i;
if((*array2D = (int**)calloc(rows, sizeof(int*))) == NULL) no_mem_exit(1);
if(((*array2D)[0] = (int* )calloc(rows*columns,sizeof(int ))) == NULL) no_mem_exit(1);
for(i=1 ; i<rows ; i++)
(*array2D)[i] = (*array2D)[i-1] + columns ;
return rows*columns*sizeof(int);
}
void free_mem2D(byte **array2D)
{
if (array2D){
if (array2D[0]) free (array2D[0]);
else error ("free_mem2D: trying to free unused memory",100);
free (array2D);
} else{
error ("free_mem2D: trying to free unused memory",100);
}
}
顺便说一下,如果你的数组寻址有一个偏移量的话,不要写为a[x+offset],而应该为 b=a+offset,然后访问b[x]。
不过,如果你不是处理对速度有特别要求的程序的话,这样的优化也就不必要了。记住,如果编普通程序的话,可读性和可移值性是第一位的。
II.从负数开始的数组
在编程的时候,你是不是经常要处理边界问题呢?在处理边界问题的时候,经常下标是从负数开始的,通常我们的处理是将边界处理分离出来,单独用额外的代码写。那么当你知道如何使用从负数开始的数组的时候,边界处理就方便多了。下面是静态使用一个从-1开始的数组:
int a[M];
int *pa=a+1;
现在如果你使用pa访问a的时候就是从-1到M-2了,就是这么简单。(如果你动态申请a的话,free(a)可不要free(pa)因为pa不是数组的头地址)
III.我们需要链表吗
相信大家在学习《数据结构》的时候,对链表是相当熟悉了,所以我看有人在编写一些耗时算法的时候,也采用了链表的形式。这样编写当然对内存的占用(似乎)少了,可是速度呢?如果你测试:申请并遍历10000个元素链表的时间与遍历相同元素的数组的时间,你就会发现时间相差了百倍!(以前测试过一个算法,用链表是1分钟,用数组是4秒钟)。所以这里我的建议是:在编写耗时大的代码时,尽可能不要采用链表!
其实实际上采用链表并不能真正节省内存,在编写很多算法的时候,我们是知道要占用多少内存的(至少也知道个大概),那么与其用链表一点点的消耗内存,不如用数组一步就把内存占用。采用链表的形式一定是在元素比较少,或者该部分基本不耗时的情况下。
(我估计链表主要慢是慢在它是一步步申请内存的,如果能够象数组一样分配一个大内存块的话,应该也不怎么耗时,这个没有具体测试过。仅仅是猜想)
三.算法篇
在上一篇中我们讲述了对内存操作的优化,这一篇则主要讲述一些常用的优化算法。这个东东太多,内容可能会有点凌乱,见谅。
I.从小处说起:
先说说一些小地方先:
① 比如n/2写为n>>1这个是常用的方法,不过要注意的是这两个不是完全等价的!因为:如果n=3的话,n/2=1;n>> 1=1;但是,如果n=-3的话,n/2=-1;n>>1=-2所以说在正数的时候,他们都是向下取整,但是负数的时候就不一样了。(在 JPG2000中的整数YUV到RGB变换一定要使用>>来代替除法就是这个道理)
② 还有就是a=a+1要写为a++; a=a+b要写为a+=b(估计一般用VB的才会写a=a+1)
③ 将多种运算融合:比如a[i++];就是先访问a[i],再令i加1;从汇编的角度上说,这个确实是优化的,如果写为a[i],和i++的话,有可能就会有两次的对i变量的读,一次写(具体要看编译器的优化能力了),但是如果a[i++]的话,就一定只读写i变量一次。不过这里有一个问题要注意:在条件判断内的融合一定要小心,比如:(idct变换中的0块判断,陈王算法)
if (!((x1 = (blk[8*4]<<8)) | (x2 = blk[8*6]) | (x3 = blk[8*2]) | (x4 = blk[8*1]) | (x5 = blk[8*7]) | (x6 = blk[8*5]) | (x7 = blk[8*3])))
在条件判断中融合了赋值语句,但是实际上如果条件为真的话,是不需要这些赋值语句的,也就是说当条件真的时候,多了一些垃圾语句,这些是在h263源码上的问题,虽然这些垃圾语句使得计算0块的时候,时间增加了30%,但是由于idct仅仅占1%的时间,0块又仅仅30%~70%的时间,所以这些性能损失是没有什么关系的。(这是后来我用汇编改写源码的时候得到的结论)。这里也说明了,程序优化一定重点在最耗时的地方。对于不耗时的代码优化是没有太大的实用意义的。
II.以内存换速度:
天下总是难有双得的事情,编程也是一样,大多数情况,速度同内存(或者是性能,比如说压缩性能什么的)是不可兼得的。目前程序加速的常用算法一个大方面就是利用查表来避免计算(比如在jpg有huffman码表,在YUV到RGB变换也有变换表)这样原来的复杂计算现在仅仅查表就可以了,虽然浪费了内存,不过速度显著提升,还是很划算的。在数据库查询里面也有这样的思想,将热点存储起来以加速查询。现在介绍一个简单的例子,(临时想的,呵呵):比如,在程序中要经常(一定要是经常!)计算1000到2000的阶乘,那么我们可以使用一个数组a[1000]先把这些值算好,保留下来,以后要计算1200!的时候,查表a[1200-1000]就可以了。
III.化零为整
由于零散的内存分配,以及大量小对象建立耗时很大,所以对它们的优化有时会很有效果,比如上一篇我说的链表存在的问题,就是因为大量的零散内存分配。现在就从一个vb的程序说起,以前我用vb给别人编小程序的时候,(呵呵,主要是用vb编程比vc快,半天就可以写一个)在使用MSFlexGrid控件的时候(就是一个表格控件),发现如果一行一行的增加新行,刷新速度十分的慢,所以我就每次增加100行,等到数据多到再加新行的时候,再加100行,这样就 “化零为整”了,使用这样的方法,刷新的速度比原来快了n倍!其实这样的思想应用很多,如:程序运行的时候,其实就占用了一定的空间,后来的小块内存分配是先在这个空间上的,这就保证了内存碎片尽可能的少,同时加快运行速度。
IV.条件语句或者case语句将最有可能的放在前面
优化效果不明显。想得到就用吧,想不到就算了。
V.为了程序的可读性,不去做那些编译器可以做的或者优化不明显的处理:
这个是很重要的,一个普通程序的好坏,主要是它的可读性,可移植性,可重用性,然后才是它的性能。所以,如果编译器本身可以帮助我们优化的话,我们就没有必要写那些大家都不怎么看得懂的东西。比如a=52(结束)-16(起始);这样写可能是因为在别人读程序的时候,一下就明白了a的含义。我们不用写为 a=36,因为编译器是会帮我们算出来的。
IV.具体情况具体分析:
具体情况具体分析,这是放之四海而皆准的真理。没有具体的分析,就不能针对问题灵活应用解决的办法。下面我就说说分析的方法。即如何找到程序的耗时点:(从最简单的办法说起,先说明一个函数GetTickCount(),这个函数在头尾各调用一次,返回值相减就是程序的耗时,精确到1ms)
① 对于认为是比较耗时的函数,运行两次,或者将函数内部的语句注释掉(要保证程序可以运行),看看多(或者少了)多少时间。这个办法简单不精确。
② 每个地方都用GetTickCount()函数测试时间,注意GetTickCount()只能精确到ms。一般的小于10ms就不太精确了。
③ 使用另外一个函数QueryPerformanceCounter(&Counter)和 QueryPerformanceFrequency(&Frequency),前面计算cpu时钟周期,后面是cpu频率相除就是时间。不过如果你要精确到这一步的话,建议将进程设置为最高级别,防止它被阻塞。
最后讲讲我处理的一个程序:程序要求我忘了,反正里面有一个函数,函数里面有一个大的循环,循环内部的处理比较耗时。结果最初程序表现出来的状况是开始还很快,越到后面越慢;我在跟踪程序中变量的时候,发现最初的循环在循环几次后就跳出了,而后面的循环次数越来越多。找到了为什么慢的原因,就可以对症下药了,我的处理是每次循环不是从头开始,而是从上一次循环跳出的地方开始左右循环(因为可能下一次循环跳出的地方别上一次的小,所以也要遍历前面的),这样程序的速度在后面也很快了。我讲这个的道理就是在实际运用中,要具体的分析程序慢的真正原因,才能达到最佳的优化效果。
发表评论
-
内存屏障
2010-02-26 11:03 1509处理器的乱序和并发执行 目前的高级处理器,为了提高内部逻辑元 ... -
函数调用堆栈分析
2010-02-26 10:53 1383理解调用栈最重要的两 ... -
mtrace检测内存泄露
2010-02-25 09:50 1091[url] http://math.acadiau.ca/AC ... -
c语言编程之字符串操作
2010-02-25 09:41 8631. //在s串中查找与s1相匹配的字符串,找到后用 ... -
linux C 链接库 so制作及调用[转]
2010-02-24 16:26 2580文章分类:C++编程 [文章作者:陈毓端 若转载请标注原文链 ... -
mtrace的使用
2010-02-24 16:02 1313对于内存溢出之类的麻烦可能大家在编写指针比较多的复杂的程序的时 ... -
单片机的C语言中位操作用法(转
2010-02-24 14:27 2214单片机的C语言中位操作用法 作者:郭天祥 在对单处机进 ... -
Linux下的itoa函数
2010-02-21 17:55 1766上篇文章说到linux需要it ... -
va_list、va_start、va_arg、va_end的原理与使用
2010-02-05 10:34 29031. 概述 由于在C语言中没有函数重载,解 ... -
快速排序(quickSort)
2010-02-04 10:50 8681. #include <stdio.h> ... -
C问题---itoa函数
2010-02-04 10:36 1048------------------------------ ... -
itoa函数及atoi函数
2010-02-04 10:35 1313C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点 ... -
结构体零长度数组的作用
2010-02-04 10:21 1375在一些 C 语言编写的代码中,有时可以看到如下定义的结构: ... -
优化C代码常用的几招
2010-02-04 10:14 776性能优化方面永远注意8 ... -
我经常去的网站
2010-02-03 17:53 1624MFC相关网站 www.codeproject.com ht ... -
可重入函数与不可重入函数
2010-02-03 16:35 932原文地址:http://blog.chin ... -
linux线程池及其测试
2010-02-03 16:32 2362/*----------------------------- ... -
哈夫曼编码
2010-02-03 16:26 1317本文描述在网上能够找到的最简单,最快速的哈夫曼编码。本方法不使 ... -
优化变成了忧患:String.split引发的“内存泄露”
2010-02-01 17:39 1118一直赞叹Sun对待技术的 ... -
锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程
2010-01-26 14:47 907http://hi.baidu.com/%5F%E2%64%5 ...
相关推荐
### C语言优化技巧详解 #### 一、I/O优化篇 **1.1 文件访问速度提升** 在C语言编程中,文件的读写是常见需求之一。为了提高程序的执行效率,尤其是对于大量数据处理的应用场景,优化文件访问速度至关重要。根据...
嵌入式C语言优化是一个重要的主题,它涉及到代码效率、内存管理、性能提升以及程序可读性和维护性等多个方面。这里我们将深入探讨这些知识点。 首先,C语言的优化主要分为两个层次:代码级优化和设计级优化。代码级...
《Keil C语言优化手册》是一本专注于Intel 8051微控制器及其后裔的书籍,旨在为读者提供优化8051项目及开发流程的新技术与策略。该书并非提供各种嵌入式项目的通用解决方案,而是侧重于教授如何在特定的8051项目中...
关于ARM的C语言优化,讲解在arm系统下的c语言优化
本篇主要探讨8位单片机的C语言优化技巧,旨在帮助开发者更好地理解和应用C语言,提高程序性能。 1. **理解单片机的内存模型**: - 8位单片机通常具有有限的RAM和ROM,因此理解内存布局至关重要。了解数据在RAM中的...
用C语言实现了AES加解密,优化主要体现在加解密为对称结构。 适用于实现简单的AES加解密、AES实验参考。 里面对部分代码有注释,详细介绍内容可以参考我的博客文章。
本论文主要阐述了三级圆柱齿轮减速器的优化设计过程,包括数学建模、目标函数的确定、设计参数的约束条件和C语言优化计算程序的建立等。三级圆柱齿轮减速器的优化设计可以提高机械设计自动化的效率,并提高机械设计...
首先,让我们深入理解标题中的“C优化设计”。C语言是一种底层编程语言,因其高效性、灵活性和对硬件的直接控制而被广泛应用于系统级和性能敏感的应用中。在机械优化设计中,C语言能够提供快速的计算速度,对于需要...
根据给定文件的信息,“C语言优化啊”这一标题强调了对C语言代码进行优化的重要性,而描述部分则明确指出,通过有效的优化手段,可以显著提升C语言代码的执行效率,且这一过程并不一定需要对整个项目进行大规模重构...
在Keil C环境下优化C语言代码是提高程序性能的关键步骤,尤其对于资源有限的嵌入式系统来说,优化显得尤为重要。以下是一些基本的优化策略: 1. **选择合适的算法和数据结构**: - 熟悉各种算法,如搜索、排序,...
C语言优化方法 C语言的优化方法可以提高C语言的效率,增加系统实时性,绝对有用。程序的优化一般分为局部优化、循环优化和全局优化三个层次。 局部优化的重点在于删除程序中的无用赋值,利用语言的特性对基本赋值...
本文探讨了如何通过C语言优化DSP(数字信号处理)软件设计的方法,旨在提高系统的可视性和便捷性。随着DSP技术的发展及其应用场景的扩展,传统的汇编语言编程方式因其可读性差、移植困难等问题逐渐显示出不足之处。...
【单片机C语言优化技巧】是针对程序性能提升的关键技术,主要涉及代码优化和执行速度优化。在优化过程中,通常需要在代码尺寸和执行时间之间寻找平衡。下面将详细阐述几个重要的优化策略: 1. **程序结构优化**: ...
《C语言程序运行速度优化方法谈》 C语言作为底层编程的重要工具,其程序的运行速度优化至关重要。本文主要探讨了几个关键的优化策略,包括选择合适的算法和数据结构、使用尽量小的数据类型以及减少运算的强度。 1...
本篇将探讨如何在C语言中优化地实现栈,并分享一个简单的实现示例。 栈的基本操作包括初始化、压栈(push)、弹栈(pop)、查看栈顶元素(peek)以及检查栈是否为空(isEmpty)。在C语言中,为了提高效率,我们需要...
三级圆柱齿轮减速器的优化设计可以通过数学模型和算法来实现,例如使用C语言编写优化设计程序,以便快速计算齿轮减速器的设计参数。 知识点三:三级圆柱齿轮减速器的数学模型 三级圆柱齿轮减速器的数学模型是指对...
本文主要针对C语言,分享了一些程序员必须掌握的代码优化技巧。 1. 选择合适的算法和数据结构: 算法和数据结构的选择直接影响程序的执行效率。例如,使用二分查找法代替顺序查找,快速排序替代冒泡排序,可以显著...
### 如何优化C语言(单片机) 在嵌入式系统开发中,特别是在单片机应用领域,C语言因其高效性和资源占用低等特点被广泛使用。然而,为了提高程序的性能,开发者还需要掌握一些优化技巧。本文将详细介绍如何优化...
总的来说,"机械优化设计的黄金分割法-c程序"提供了一个用C语言实现优化设计问题的实例,它结合了黄金分割法和进退法,有助于学习者理解数值优化算法的原理,并且能够在实际工程问题中找到最优解决方案。通过分析和...