另一个重要的优化是减少函数调用的次数。虽然现在许多编译器已经针对函数调用进行了重大优化,但是对于函数携带的重大开销还是稍存疑虑。为了验证这一点,我们可以编写一个小程序,它把一个只执行返回语句的函数调用了10 000次,你将开始感觉到函数调用的代价,如果函数传递参数或者返回值,那么花费的时间将会更长一些。常见的惯例是使用内联代码或者宏,把函数直接隐藏在算法中。研究编译器的库(如果可用,应该总是能得到源代码),并且你将会发现许多函数已经被实现为宏。要尽可能使用这些宏,并且要留心观察它们的副作用。如果你仍然必须调用函数并且不能获得所需的性能,可以考虑将供应商的源代码直接复制到你的算法中。这是一种危险的做法,仅当执行了所有其他的优化之后才应该考虑把它作为最后一种办法。除了版权问题之外,严格来讲,库的源代码也很少是可移植的,并且可能会处理隐藏在库内的变量。不过,可以轻松地引入许多函数或者把它们转换为宏。每种方法都会增加代码的大小,因此必须小心,不能过度使用这种技术。
另一种常见的算法技术是消除递归。递归是使函数调用它自身的行为。在C语言中,所有的函数都可以进行递归调用:即它们都能调用自身,至少在理论上如此。实际上,像main()和exit()这样的函数在进行递归调用时可能会引发问题。不过,几乎所有不会终止程序的函数都可以调用它们自身。在算法中经常会使用递归技术,因为它可以产生短小、优雅的解决方案。不过,它的代价高昂,包括时间和系统资源两方面,尤其体现在栈空间的消耗上。第5章给出了关于如何消除递归的一些示例。在可能的情况下,应尽量避免使用递归。
在MSDOS环境下,还有另外几种技术可用于减少函数调用的开销。首先,启用某些高级编译器提供的优化功能,它们允许使用寄存器而不是栈来传递函数参数(例如:Borland使用pr开关,Microsoft使用/Gr开关以及fastcall关键字,而Watcom则会默认执行优化)。如果用到了第三方程序库,在使用这些功能时就应该小心谨慎;有关更多信息,请参考相关的编译器文档。
MSDOS用户的第二个选择是使用pascal关键字。它将影响被调用的函数如何操作栈。如果使用这个关键字,可以同时节省时间和空间。与上述的编译开关一样,也必须小心使用pascal关键字。它不能用于参数数量可变的函数,并且一般不能把它与上述的编译开关结合起来使用。同样,请认真阅读编译器文档。如果必须从这两种方法中选择一种,就必须检查你的程序。如果程序通常只传递数量很少的参数(但不是零个参数),就使用第一个选项;如果程序具有多种类型的函数,有的函数不带参数,有的函数则带有两个或三个以上的参数,那么pascal选项将更高效。
最后,认真学习算法,然后钻研其中最频繁使用的部分。在这个过程中,拥有一种优秀的性能测量和跟踪工具(profiler)将给你带来巨大的好处。购买一种这样的工具并使用它;通过定期使用它可以加快代码的执行速度。在后面的各章中,在演示算法时我们强调了代码的清晰性。如果由于进行优化而使代码含义晦涩,我们将放弃这样做。只要有可能,我们就会给出关于在什么地方可以优化算法的提示和信息。
另一种常见的算法技术是消除递归。递归是使函数调用它自身的行为。在C语言中,所有的函数都可以进行递归调用:即它们都能调用自身,至少在理论上如此。实际上,像main()和exit()这样的函数在进行递归调用时可能会引发问题。不过,几乎所有不会终止程序的函数都可以调用它们自身。在算法中经常会使用递归技术,因为它可以产生短小、优雅的解决方案。不过,它的代价高昂,包括时间和系统资源两方面,尤其体现在栈空间的消耗上。第5章给出了关于如何消除递归的一些示例。在可能的情况下,应尽量避免使用递归。
在MSDOS环境下,还有另外几种技术可用于减少函数调用的开销。首先,启用某些高级编译器提供的优化功能,它们允许使用寄存器而不是栈来传递函数参数(例如:Borland使用pr开关,Microsoft使用/Gr开关以及fastcall关键字,而Watcom则会默认执行优化)。如果用到了第三方程序库,在使用这些功能时就应该小心谨慎;有关更多信息,请参考相关的编译器文档。
MSDOS用户的第二个选择是使用pascal关键字。它将影响被调用的函数如何操作栈。如果使用这个关键字,可以同时节省时间和空间。与上述的编译开关一样,也必须小心使用pascal关键字。它不能用于参数数量可变的函数,并且一般不能把它与上述的编译开关结合起来使用。同样,请认真阅读编译器文档。如果必须从这两种方法中选择一种,就必须检查你的程序。如果程序通常只传递数量很少的参数(但不是零个参数),就使用第一个选项;如果程序具有多种类型的函数,有的函数不带参数,有的函数则带有两个或三个以上的参数,那么pascal选项将更高效。
最后,认真学习算法,然后钻研其中最频繁使用的部分。在这个过程中,拥有一种优秀的性能测量和跟踪工具(profiler)将给你带来巨大的好处。购买一种这样的工具并使用它;通过定期使用它可以加快代码的执行速度。在后面的各章中,在演示算法时我们强调了代码的清晰性。如果由于进行优化而使代码含义晦涩,我们将放弃这样做。只要有可能,我们就会给出关于在什么地方可以优化算法的提示和信息。