性能优化小技巧-消除低效循环,让你的程序快到飞起

开发 前端
由于编译器的优化非常小心,它必须确保优化前后执行的效果是保持一致的,因此有些时候它会变得保守,并不能帮你优化太多。本文所需要的是在平常不需要花费太多力气,养成习惯,并且对程序性能有好处的小技巧。

在分享这些性能优化技巧之前,需要说明以下几点:

  • 不要过早优化性能
  • 现代编译器的优化能力很强大
  • 80%的性能问题集中于20%的代码中

但是由于编译器的优化非常小心,它必须确保优化前后执行的效果是保持一致的,因此有些时候它会变得保守,并不能帮你优化太多。

本文所需要的是在平常不需要花费太多力气,养成习惯,并且对程序性能有好处的小技巧。

[[322875]]

示例程序

为了说明本文所提到的技巧效果,先看一个示例程序,程序的目的非常简单,就是将字符串中的小写字母转换为大写),以下是完整可编译运行代码:

  1. #include<stdlib.h> 
  2. #include<stdio.h> 
  3. #include<time.h> 
  4. #include<ctype.h> 
  5. #include<string.h> 
  6. #include<sys/time.h> 
  7. #define MAX_LEN  1024*1024 
  8. void printCostTime(struct timeval *start,struct timeval *end) 
  9.     if(NULL == start || NULL == end) 
  10.     { 
  11.         return; 
  12.     } 
  13.     long cost = (end->tv_sec - start->tv_sec) * 1000 + (end->tv_usec - start->tv_usec)/1000; 
  14.     printf("cost time: %ld ms\n",cost); 
  15. int main(void) 
  16.     srand(time(NULL)); 
  17.  
  18.     int min = 'a'
  19.     int max = 'z'
  20.     char *str = malloc(MAX_LEN); 
  21.     //申请失败则退出 
  22.     if(NULL == str) 
  23.     { 
  24.         printf("failed\n"); 
  25.         return -1; 
  26.     } 
  27.     unsigned int i = 0
  28.     while(i < MAX_LEN)//生成随机数 
  29.     { 
  30.         str[i] = ( rand() % ( max - min ) ) + min; 
  31.         i++; 
  32.     } 
  33.     str[MAX_LEN - 1] = 0;  
  34.     //统计时间 
  35.     struct timeval start,end; 
  36.     gettimeofday(&start,NULL); 
  37.     for(i = 0;i < strlen(str) ;i++) 
  38.     { 
  39.         str[i]  = toupper( str[i] ); 
  40.     } 
  41.     gettimeofday(&end,NULL); 
  42.     printCostTime(&start,&end); 
  43.     free(str); 
  44.     str = NULL
  45.     return 0; 

随机数的生成可参考《随机数生成的方法》。我们主要关注下面的部分:

  1. for(i = 0;i < strlen(str) ;i++) 
  2.     str[i]  = toupper( str[i] ); 

很简单,对不对?

运行看看时间:

  1. $ gcc - -o loop loop.c 
  2. $ ./loop 
  3. cost time: 42103 ms 

总共花了42秒多!(机器处理能力不同运行结果将会有较大差异)

消除低效循环

终于来到了我们的优化环节,我们观察代码其实很容易发现,每次循环的时候都会执行一次strlen计算字符串的长度,而这个计算具有以下特点

每次结果一致,属于重复计算

strlen时间复杂度为O(N),也就是说,字符串越长,它需要的时间也就越多

一般情况下的使用是没有太大问题的,但是问题在于,如果是在一个多次循环中,它能极大的影响效率。

到这里,优化方法想必你也清楚了,那就是将计算结果不会改变的计算移到循环外。代码如下:

  1. unsigned int len = strlen(str); 
  2. for(i = 0;i < len ;i++) 
  3.     str[i]  = toupper( str[i] ); 

那么再次运行的结果如何呢?

  1. $ gcc -O0 -o loop loop.c 
  2. $ ./loop 
  3. cost time: 4 ms 

看到没有,4ms,将近一万的性能提升!而这个数值将会随着字符串长度的增长进一步扩大。惊不惊喜,意不意外?

总结

实际上,本文的例子是比较极端的,然后实际中就可能隐藏着很多类似的代码:

  • 在循环中计算,但是每次结果都一样
  • 并且该计算的复杂度不是O(1)

对于这类代码,在不绝对影响可读性的情况下,完全可以将其移到循环外。

思考

如果是C++的string,循环时通过str.length()获取长度,会如此影响性能吗?为什么?

 

责任编辑:赵宁宁 来源: 编程珠玑
相关推荐

2020-11-18 13:54:27

IDEAJava开发

2020-05-06 16:32:18

for循环Python迭代

2021-01-04 15:11:57

开发 IDEA代码

2023-03-01 23:59:23

Java开发

2023-09-26 12:02:34

C++循环

2015-09-16 14:47:14

Android性能优化代码

2019-07-18 12:40:49

Java编程语言性能优化

2021-11-18 08:20:22

接口索引SQL

2020-09-29 07:54:05

Express 飞起

2011-04-13 10:51:58

MATLAB

2017-03-15 09:42:43

软件开发云携手

2022-02-21 13:27:11

接口性能优化索引命令

2011-05-20 11:12:01

数据库DB2优化

2009-10-27 09:09:06

Eclipse技巧

2024-01-08 17:09:07

Python解释器CPython

2023-09-25 13:15:50

SQL数据库

2022-01-18 10:15:18

Vue性能优化前端

2019-08-21 10:53:29

.NET性能优化

2023-12-06 13:43:00

python代码
点赞
收藏

51CTO技术栈公众号