1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
<?xml version="1.0" encoding="utf-8"?>
<chapter>
<title>优化文档的快速指南</title>
<para>这是关于优化的一个简单介绍,以及如何做和为何这么做。每个工具和技术的细节留到未来的文档中,但是提供了一些提示和技巧。</para>
<sect1 id="optimization-intro-TBL-what-are-we-optimizing">
<title>什么需要优化?</title>
<para>优化 GNOME 程序时需要记住的第一件事情是:我们在做的不是使程序更好,而是让人们更快乐地使用计算机。</para>
<para>更好的程序可以使人们更快乐,但是一些改进将使他们比别人更快乐:反应速度、启动时间、更容易找到命令以及在多个程序同时打开时不让计算机卡住。</para>
<para>
Traditional optimization tackles concepts like CPU use, code size, the number of mouse clicks and the memory use of the program. This second list has been chosen to correlate with the first list, however there is an important difference: The person using GNOME doesn't care about the second list, but they care a lot about the first list. When optimizing GNOME programs we will reduce CPU use, memory use and all those things, but these are the means to the end, not the final goal. We are optimizing for people.
</para>
</sect1>
<sect1 id="optimization-intro-TBL-doing-optimization">
<title>进行优化</title>
<para>上面的章节忽略了一个重要的指标:优化的东西必须是可量测的。快乐是无法量测的,但是可以量测启动时间,然后讨论是否需要对其进行优化。希望快乐也能随之而来吧。</para>
<para>优化是测量、精益求精和重新测量的过程。所以,第一件必须要做的事情就是找到一种测量要优化的事情的方法。这个测量结果最好是一个数字,例如:执行一个任务所需要的时间。这是您的性能测试,这时唯一可以告诉您通过还是失败的方法。在一个程序 <emphasis>应该</emphasis> 快还是 <emphasis>已经</emphasis> 快之间的区别。</para>
<para>进行了基本的性能测试之后,您需要找出为什么代码没有如其应该的那样快。可以通过这样来尝试:检查代码找出需要改进的地方。您可能总会是错误的。使用一个性能分析器来获取详细的程序每步的工作是唯一可以保证是正确的方法。</para>
<para>程序通常可以分为很小的代码块。找出性能最差的地方并集中精力首先处理。完成之后,重新运行性能分析器然后重复。每进行一次这样的努力将获得的越来越少,您可以在一个点上认为结果已经足够好了。如果您的努力只能获取 10% 的改进,最好略过这个点,并且应该停止。</para>
<para>不要忘记大图片。例如,在尝试加速一段代码之前,先想想是否真的需要它。是否可以和其它代码块结合?前面计算的结果是否可以保存起来并重用?如果是用户根本不会注意的地方就甚至不需要优化。更糟的是,代码已经优化了,并且正在进行高负载运算,那就需要避免稍候再次进行运算。不孤立运行的代码也不需要优化过程。</para>
</sect1>
<sect1 id="optimization-intro-TBL-hints">
<title>提示</title>
<itemizedlist>
<title>基本原理</title>
<listitem>
<para>对代码进行更改之后重新运行性能测试,记录每次的更改及对性能的影响。这可以帮助您修复错误,也可以帮您避免重复犯错。</para>
</listitem>
<listitem>
<para>在进行优化之前确保代码是正确且没有缺陷的。在优化之后检查是否仍然是正确且没有缺陷的。</para>
</listitem>
<listitem>
<para>在对细节进行优化之前先在高层进行优化。</para>
</listitem>
<listitem>
<para>使用正确的算法。经典的文本排序使用的是快速排序而不是冒泡排序。还有很多其它,一些可以节省内存,一些可以节省 CPU。同样,寻找您可以走的捷径:如果愿意进行某些组合,甚至可以比快速排序更快。</para>
</listitem>
<listitem>
<para>优化就是折衷。缓存结果可以加速运行,但是会增加内存使用。将数据保存到磁盘可以节省内存,但从磁盘中加载时浪费时间。</para>
</listitem>
<listitem>
<para>
Make sure you choose a wide variety of inputs to optimize against. If you don't it is easy to end up with a piece of code carefully optimized for one file and no others.
</para>
</listitem>
<listitem>
<para>避免高消耗的操作:多次从磁盘读取很少的数据。使用很多内存会需要交换。避免任何不需要的硬盘读写。网络也是很慢的。同样避免需要 X 服务器响应的图形操作。</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>粗心造成的陷阱</title>
<listitem>
<para>注意副作用。在不同的代码部分之间经常有奇怪的交互,加速一个部分可能会使另一部分的速度降低。</para>
</listitem>
<listitem>
<para>测量代码的运行时间时,即使在安静的系统上,程序之外的事件也会对计时产生影响。多次运行取平均值。如果代码非常短,计时精度也是一个问题。这时,测量代码运行 100 甚至 1000 次的时间。如果您记录的时间长达数秒,就可以了。</para>
</listitem>
<listitem>
<para>也很容易被性能分析器误导。有人曾经优化掉了操作系统的 idle-loop,因为它耗费掉了系统所有的时间。不要优化做用户根本不关心的部分。</para>
</listitem>
<listitem>
<para>别忘记了 X 服务器上的时间。您的程序的内存使用量并没有包含在 X 服务器进程中存储的像素影射,但它们仍然使用内存。使用 xrestop 查看您的程序使用的资源。</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>底层提示</title>
<listitem>
<para>优化内存使用时,警惕高峰使用量和平均使用量的区别。总是持有申请的内存是不好的。一些仅仅是简单的申请,还是可以接受的。类似于 massif 的工具使用时空 (内存使用量和使用时间的乘积) 的概念来代替。</para>
</listitem>
<listitem>
<para>
Time simplified bits of code that do only the things you know are essential, this gives an absolute lower limit on the time your code will take. For example, when optimizing a loop time the empty loop. If that is still too long no amount of micro-optimization will help and you will have to change your design. Make sure the compiler doesn't optimize away your empty loop.
</para>
</listitem>
<listitem>
<para>
Move code out from inside loops. A slightly more complicated piece of code that is executed once is far quicker than a simple piece of code executed a thousand times. Avoid calling slow code often.
</para>
</listitem>
<listitem>
<para>给编译器尽可能多的提示。使用常量关键字。使用 <envar>G_INLINE_FUNC</envar> 作为经常调用的函数的简称。查看 <envar>G_GNUC_PURE</envar>,<envar>G_LIKELY</envar> 和其它 glib 杂项宏。使用这些宏代替 gcc 专用的关键字,以确保可移植性。</para>
</listitem>
<listitem>
<para>不要使用汇编语言。汇编语言不可移植,而且虽然可能在一个处理器上很快,但是甚至在支持同样处理器架构的处理器上也不能保证可以很快 (例如速龙和奔四的区别)。</para>
</listitem>
<listitem>
<para>不要重写已有的库函数,除非确定库函数不可接受得慢。很多 CPU 敏感的库函数都是优化过的。相反地,一些库函数也是慢的,尤其是那些使用系统调用的。</para>
</listitem>
<listitem>
<para>使用尽量少的库。链接到的库越少,程序启动越快。对于 GNOME,这很难达到。</para>
</listitem>
</itemizedlist>
<itemizedlist>
<title>高层技巧</title>
<listitem>
<para>发挥并发的优势。这并不仅仅意味着使用多处理器,也意味着减少用户执行一些计算所预期的时间。在等待从磁盘上加载数据是进行计算。充分使用多种资源,同时使用它们。</para>
</listitem>
<listitem>
<para>欺骗。用户仅仅认为计算机很快,而不管实际上是否如此。命令和结果之间的时间很重要,而无论是提前计算的、缓存的还是在稍候方便的时间进行实际计算,尽快地给出用户期望的结果。</para>
</listitem>
<listitem>
<para>在空闲循环中进行某些工作。这比多线程更容易编程但仍然在用户视线之外进行了工作。仍然需要注意,如果在空闲循环中花费太多时间,程序将变卡。所以需要经常返回主循环。</para>
</listitem>
<listitem>
<para>如果都失败了,告诉用户代码将会比较慢并且使用进度条。如果仅仅给出结果,用户并不会高兴,他们至少会知道程序没有崩溃,他们可以去喝杯咖啡。</para>
</listitem>
</itemizedlist>
</sect1>
</chapter>
|