CAD程序性能优化之一般优化方法

       建模效率是CAD软件至关重要的指标,会极大地影响应用中的设计效率,进而决定工业产品的研发周期。鉴于CAD程序的复杂性,在编写完成之前,几乎很难准确判断这段程序是否符合预期的性能要求,往往需要通过实际测试来确认。

       图1是提高程序性能的一般流程。第一步是测试总体性能,如果满足要求,则结束,否则,需要分析和定位问题,然后修改相应代码,再回到第一步重新测试,直到性能满足要求。当然,也可能存在一种情况,在现有方案下无法优化到目标性能,而面临推倒重来,这不是本文的关注点。在上述流程中,如何分析和定位问题、怎样修改代码是本文要讨论的重点。

在这里插入图片描述

图1. 提高程序性能的一般流程
 

1. 分析方法

       不管是哪种分析方法,基本原则是不变的,那就是查找运行热点,或者说是定位到耗时最久的部分。这个原则是非常关键的,因为优化非热点代码的性价比可能非常低。比如,有一段代码明显有问题,但只占到总时间的10%,即使将这段代码的运行效率提高100%,也仅仅能提高总效率的5%。而如果能将占总时间80%的代码提高100%,那么就能使总效率提高40%。

       分析热点的方法一般可以分成4种:

  • 根据经验判断。这只适用于小段程序,且非常依赖工程师的经验。
  • 查看汇编代码。由于汇编语言阅读性较差,这种方式一般只适用于深度优化。
  • 编写测试程序,分解运行时间。这种方法直观且灵活度高,使用的最多。
  • 运用统计和分析工具。本质上与上一种方法相同,可以减少甚至不写代码,表现形式一般也更加友好。
1.1. 计时工具

       首先,我们先建立一下对计算机运行速度的大概印象,以计算两点之间距离为例,计算1,000,000次,需要多少时间呢?笔者所用电脑的表现是3ms左右。因此,如果想准确查找到热点,需要有高精度的计时工具。

       以Windows系统为例,提供了多个计时器,比如clock_t。但是,clock_t的计时精度只有ms级别,通过上面的例子不难看出,如果距离的计算次数小于100,000次,clock_t就无法统计到了,因此无法满足优化性能的需求。Windows提供了高精度的性能计数器,通过QueryPerformanceCounter()和QueryPerformanceFrequency()两个函数实现,QueryPerformanceCounter()用来获取计数器当前值,QueryPerformanceFrequency()用来获取计数器的频率。实际使用中,在被测试程序之前和之后,分别调用QueryPerformanceCounter(),然后获得计数差值,除以计数器频率就是被测试程序的运行时间了。QueryPerformanceCounter()的分辨率小于1 us,计时精度可以到us级别。

       实际应用中需要注意的是:

  • 不要直接使用计数差值dif除以频率freq,这样得到的是以s为单位的整数值,很容易就得到0(运行时间小于1s)。如果想得到us时间,应该dif * 1000 * 1000 / freq。
  • 即使计时精度为us级别,但依然会存在无法统计到的项,比如在for循环中统计点距离的时间,并累加起来,得到的数值也可能是0。
1.2. 时间分解

       分析热点的核心思路就是将运行时间不断分解,一般是按照类或函数为单位模块。图2是对模块A运行时间的分解,首先,将A分解成3个子模块,分别统计运行时间,D的运行时间占比很低,而B、C的运行时间占比较多,并且相差不大。因此,需要B、C这2个子模块再次进行拆解,第三层的时间统计特点是:虽然E单次运行的时间占总时间不是很高,但是被不同的2个上级模块调用了,调用的总时间让E成为第三层的热点。继续分解E,可以看出R的时间占比很大。因此,子模块R是整个模块A的热点,是应该首先考虑优化的程序模块。

在这里插入图片描述

图2. 时间分解示例
 

       上面的分解思路是理想化的,实际过程中可能比较复杂。正如下面的代码,假设模块R中是一个for循环,其中调用了2个函数,一般的分解思路是:分别统计Fun1()和Fun2(),然后将单次调用时间累加。但是,如果2个函数单次运行的时间都在1us以下呢?累加的结果都是0。一种可行的方式是:首先,注释掉Fun2(),在for循环之外加计时代码,这样就可以统计出Fun1()的运行时间;然后,取消注释Fun2(),统计出两者的总运行时间,减去Fun1()的,就是Fun2()的运行时间。实际代码可能存在更加复杂的嵌套关系,可能需要开发人员设计更好的测试代码。

for (int i = 0; i < n; i++)
{
   
    //计时开始
    Fun1();
    //计时结束
    //累加时间

    //计时开始
    Fun2();
    //计时结束
    //累加时间
}

       其它需要注意的是:

  • 运行时间的统计和分解是一个易出错的工作,需要验证分解的正确性,确保所有子模块的时间之和近似等于父模块的时间。
  • 鉴于CAD程序的复杂性,不要盲目自信,主观臆断一段程序是不耗时的,可能会错失优化性能的机会。
  • 若有需要,可以在Debug模式下统计运行时间,但是,最终结果还是需要在Release模式下获取和验证,两种模式下的效率差异巨大,不同模式下得到的热点也可能截然不同。
1.3. 实用工具

       上面提到的时间分解方法需要编写测试代码,这是一项比较繁琐和易出错的工作。现在有一些好用的工具,可以用来测试程序性能,比如Intel推出的VTune Profiler 和 Visual Studio中内置的性能探查器。VTune Profiler可以提供采集并分析丰富的、多层次的性能数据,对运行在Intel平台上的程序非常友好。Visual Studio内置的性能探查器,可以对内存、CPU、GPU、IO等进行数据采样,针对图2中的程序,图3和图4分别是性能探查器给出的火焰图和函数运行时间排序,从这两张图中很容易发现,程序运行的热点是函数R。

在这里插入图片描述

图3. Visual Studio内置的程序运行火焰图
 

在这里插入图片描述

图4. Visual Studio内置的函数运行时间排序 </
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值