1.1 学习性能所必需的知识 在最开始,我们先来介绍一下有关性能的基础理论。 性能变差的原因示例 曾经有客户过来咨询:“处理的数据条数变多时,数据库(DataBase,DB)的处理速度就会变得很慢,这让人很头疼。”听到这样的问题,一般就会马上想到“是不是DB 的这个SQL 语句不太好啊?是不是磁盘的I/O 不太好啊”等,即从一个方面去判断问题的原因。由于当时笔者有一定的知识储备,立即就推断出DB 进行单个处理的速度并没有变慢。但是据客户说,DB 单次处理的数据量分别为1000 条和100 万条时,花费的时间会有几十倍甚至几百倍的差别。由于找不到头绪,就从客户那里要来了应用程序的代码。看了一下源代码,就找出原因了。实际情况如下所示。 第1 条数据的处理过程是:从文件中读取1 条数据,并把它放在内存中,接着在DB 中放入1 条数据,完成。第2 条数据的处理过程是:从文件中读取1 条数据,放在刚才的内存位置的后面,接着在DB 中放入1 条数据,完成。第3 条数据的处理过程是:同样从文件中读取1 条数据,然后遍历内存,在其后面的位置放入1 条数据,再在DB 中放入1 条数据,完成。 那么,知道这个处理的问题出在哪里了吗?没错,在已经放入100万条数据的时候,为了再放入1 条数据,就需要遍历100 万次内存(图1.1)。 编写这个程序的客户可能觉得遍历内存这种处理一下子就能完成了。但是,要知道“积土成山”。本身DB 的单次处理速度就不快,如果要遍历100 万次内存的话,速度会更慢。在笔者指出以上问题后,客户显得很不好意思。要解决这个问题,只需增加1 个变量,来标明内存的最后位置。只需如此,性能就有了几百倍的提升(图1.2)。 图1.1 数据放得越多,处理速度变得越慢的例子 图1.2 放入很多数据后处理速度也不会变慢的例子在这个例子中,应用程序的设计是导致性能变差的原因。但是,如果没有这方面的知识,就不会意识到这个原因。在本章中,我们就来说明一下有关性能的基础知识——“算法”。 1.2 算法的优缺点与学习方法 1.2.1 什么是算法 首先,请想象一下连成一排的箱子的长队列,这些箱子中放着物品。从这些箱子中找出目标物品所花费的时间长短就代表性能的优劣。如果时间很短,就可以说性能很好。相反,如果时间很长,就称性能很差。 想象一下,从队列的一头依次打开箱子。假设有1000 个箱子,平均打开500 个就能找到需要的物品。也就是说,我们需要从一头开始依次打开500 个箱子。那么有没有效率更高的方法呢?如果把箱子里的物品标上序号排好的话,会更方便查找(图1.3 上)。比如说,我们要找标着数字“700”的物品。先打开最中间那个箱子,假设出来的是标着数字“500”的物品。因为要查找的数字比它更大,所以我们把目标转向这个箱子的右边部分。在右边的那一部分箱子之中,打开位于正中间的箱子,假设第2 次找到的是标着“750”的物品。由于需要查找的数字比它更小,因此接下来就要查找这个箱子的左边。这样,通过有效地缩小查找范围,只需要很少的次数就能快速找到需要的数字(图1.3 下)。 这样的策略或方法就称为“算法”。算法的好坏对性能有很大的影响。 此外,学习算法的好处,不仅仅局限于提升性能。熟练掌握IT 技能的人也都熟知主要的算法,当新的技术出现的时候,他们可以马上想到“就是那个算法啊”,立刻就能理解。因为对每个算法的优缺点都了然于胸,所以很少会用错。如果只掌握了技术的表面而没有真正理解,或者是仅仅死记硬背了下来,那么或许会一些简单的使用方法,但在实际应用时会很辛苦。因此,掌握算法是IT 工程师的基本功。 图1.3 通过改进使查找起来更容易 1.2.2 算法的基础 那么,让我们回到刚才的箱子队列的例子。与现实世界不同的是,这些箱子上都写着地址,如果知道了地址,就能立即打开那个箱子。例如,如果想要看第100 号箱子的内容,不管自己在哪个位置,都能立即打开第100 号箱子看里面的内容。 还可以在箱子的里面放入地址。例如,如果将第100 号箱子的地址放入第99 号箱子中,那么“100”这个数字就放入到了第99 号箱子里(图1.4)。 “①箱子连成一排”“②地址”“③可以在箱子里面放地址”“④知道地址的话就能立即访问”,这4 点就是算法的基础,也是计算机结构的基础。对此有些了解的人可能会从“可以在箱子里面放地址”这个比喻联想到C 语言的“指针”。另外,也可能会有人从“知道地址的话就能立即访问”这个比喻联想到CPU 处理的“物理内存”。有些人可能对指针有恐惧心理,觉得很难,其实理解指针的窍门就是区分“值”和“地址”。如果感到头脑混乱,请想一下这个原则。 图1.4 地址是算法的基础