c++应用程序优化之二关注热点

c++程序的优化的原理和其它应用程序没有本质的不同,在优化程序的时候,一定要注意优化的位置,举一个例子:如果优化十处,可以提高0.1秒,但是只要优化另外一处就可以提高十秒,那么重点应该关注哪里,当然是十秒的地方,也就是标题提到的热点。

一、循环热点

有很多的工具和方法用来查找热点位置,热点包括CPU占用,内存占用,IO占用等等。下面举一个CPU热点的例子:

void Test(const char *s)
{

  //int len = strlen(s);
  for (int num =0;num < strlen(s);num++)
  {
    ...
  }

}

这里面就有一个问题,每次for循环,都要重新计算一下strlen(s),如果能够把这个数字提前计算出来,就可以节省很多时间。其它类似的有好的这种情况都可以这样处理。另外一个就是在循环语句中有大量的常量代码:

#define N 100000
void Test()
{
  int d = 0;
  for (int num = 0;num < N;num++)
  {
    int i =10;
    int j =100;

    d = i * j +num;
  }
}

这里完全可以把i,j的定义挪到循环外面。另外还有一种在多线程的情况下经常出现的:

//此处在线程内调用这个Test
void Test()
{
  for (int num = 0;num < N;num++)
  {
    ....
    //wait();
    //Sleep(0);
  }
}

新手很容易写成在一个线程里调用一个循环然后把工作完成,这样本身没有什么过错,但是如果是在一个大型程序中,这样写的话,用CPU监控器可以发现,一个CPU的核心完全被占满了。其实,工作未必会这样复杂。好的办法,是要Sleep(0)一下,目的不是为了睡眠,目的是让出CPU,或者使用等待机制,都可以解决这个问题。

二、函数热点

函数使用中的热点这里介绍两类:

1、多层调用堆栈

在c语言中,函数是基本的构件,为了完成功能,一定会写N多的函数,函数多无所谓,主要是不要不断的嵌套调用函数(包括谨慎的使用递归调用),看下面的代码:

void Test(const int d)
{
  int a[30]={0};
  ......
}
void Get(const int v)
{
    double b[10]={0.0};
    Test(v);
}
void GetOnce(const int c)
{
  Get(c);
}

函数调用时,会发生上下文的保存,恢复时又要进行逆向的操作,当你的函数中存在大量的变量或者需要保存的数据时,这个代价是不容小觑的,所以一定要谨慎的处理函数的调用包括递归。

2、不必要的虚函数

虚函数的调用其实还是比较好理解的,迟后联编,动态加载,这肯定是需要牺牲性能的。如果在设计一个模块时,可以不使用虚拟函数来完成目标,那么尽量还是放弃它(这里仅用优化的角度谈这个问题,实际情况需要从整体权衡考虑)。

除了上面的两点,其实还可以使用静态多态来提高效率,比如使用模板,把动态加载提前到编译期。当然,这也提高了编程的难度和复杂度。还有减少动态库的调用等。

三、内存热点

如果需要不断的分配和销毁内存,这就会出现一个问题,在持续一段时间后,可能查看内存还有不少,但是内存的分配却无法成功了,反复的不断分配不同大小 的内存,生成了大量的内存的碎片,导致后期内存的分配不堪使用:

void Test(int num)
{
  int * buf = new int[num];
  .....
  delete []buf;
}
int random()
{
  //返回一个随机值,省略了计算
  return n;
}
void CallTest()
{
  int n = random();
  Test(n);
}

类似上面的代码的情况很多,比如在读写Socket数据时就会有这种情况,那么如何解决呢?一般使用内存池的方式,把内存管理起来,控制分配,一次释放。但是陈硕在网上说“现在一些人动不动就要挽起袖子自己写内存池,号称能提高性能,真当 Ulrich Drepper 是水货?”,参看这篇文章:
https://akkadia.org/drepper/cpumemory.pdf

所以还是要深入钻研,根据实地情况来搞,技术的进步,会导致原来的一些方法失效甚至根本起了反作用,要紧跟技术前进的步伐。

四、IO热点

这个比较好理解,毕竟硬件IO的速度比之CPU和内存要慢好几个数量级,产生热点的可能性会倍增。这里好的方法只有一个,使用好的库,更优秀的语言,并其中进行权衡。当然,在读写IO时,尽量不要使用画蛇添足的方式来操作,比如在不必要的情况下非要显示的每次都Flush。

五、总结

热点其实是解决优化的重点,只要找到了热点,基本上可以说,优化的问题解决了大半,这也是一种抓大放小,哲学上的抓住事物的主要一面。
在这里插入图片描述

发布了104 篇原创文章 · 获赞 12 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/fpcc/article/details/88817300