带你读《基于CUDA的GPU并行程序开发指南》之三:改进第一个CPU并行程序
点击查看第一章点击查看第二章 第3章 改进第一个CPU并行程序我们并行化了第一个串行程序imflip.c,并在第2章中开发了它的并行版本imflipP.c。并行版本使用pthreads实现了合理的加速,如表2-1所示。当我们在具有4C/8T的i7-960 CPU上分别启动2个和3个线程时,多线程将执行时间从131 ms(串行版本)分别降低到70 ms和46 ms。然而引入更多的线程(即≥4)并没有帮助。在本章中,我们想让读者了解影响表2-1中结果数据的各种因素。我们可能无法改进它们,但我们必须能够解释为什么无法改进它们。我们不想仅仅因为运气而取得好的性能表现! 3.1 程序员对性能的影响 理解硬件和编译器可以帮助程序员编写好的代码。多年来,CPU架构师和编译器设计人员不断改进其CPU架构和编译器的优化功能。许多这些努力有助于减轻软件程序员的负担,因此,程序员在编写代码时不用担心底层的硬件细节。但是,正如我们将在本章中看到的,了解底层硬件和高效利用硬件也许会让程序员在某些情况下开发出性能提升10倍的代码。这种说法不仅对CPU来说是正确的,当硬件得到有效的利用时,潜在的GPU性能改进更加明显,因为许多GPU性能的显著提升来自软件。本章将介绍所有与性能有关的因素及其相互之间的关系:程序员、编译器、操作系统和硬件(以及某种程度上的用户)。 程序员拥有根本的智慧,应该理解其他部分的功能。没有任何软件或硬件可以与程序员所能做的相提并论,因为程序员具有最宝贵的资产:逻辑。良好的编程逻辑需要完全理解难题的所有方面。 编译器是一个庞大的软件包,它的常规功能有两个:编译和优化。编译是编译器的工作,优化是编译器在编译时必须执行的额外工作,以优化程序员可能编写的低效代码。所以编译器在进行编译时是“组织者”。编译时,时间是静止的,这意味着编译器可以仔细考虑在运行时可能发生的许多情况,并为运行时选择最好的代码。当我们运行程序时,时钟开始滴答滴答。编译器唯一无法知道的是数据,它们可能会完全改变程序的流程。只有在操作系统和CPU工作时,才能在运行时知道数据的情况。 在运行时,操作系统(OS)可以看作是硬件的“老板”或“经理”。它的工作是在运行时有效地分配和映射硬件资源。硬件资源包括虚拟CPU(即线程)、内存、硬盘、闪存驱动器(通过通用串行总线[USB]端口)、网卡、键盘、显示器、GPU(一定程度)等。好的操作系统知道它的资源以及如何很好地映射它们。为什么这很重要?因为资源本身(例如CPU)不知道该怎么做。它们只是遵循命令。操作系统是司令,线程是士兵。 硬件是CPU+内存+外围设备。操作系统接受编译器生成的二进制代码,并在运行时将它们分配给虚拟核心。虚拟核心在运行时尽可能快地执行它们。操作系统还要负责CPU与内存、磁盘、键盘、网卡等之间的数据传输。 用户是难题的最后一部分:了解用户对编写好的代码也很重要。一个程序的用户不是程序员,但程序员必须向用户提出建议,并且必须与他们沟通。这不是一件容易的事情! 本书主要关注硬件,尤其是CPU和内存(以及在后面第二部分中要讲的GPU和显存)。理解硬件是开发高性能代码的关键,无论是CPU还是GPU。在本章中,我们将发现是否有可能加速我们的第一个并行程序imflipP.c。如果可以的话,如何实现?唯一的问题是:我们不知道可以使用哪些硬件来更高效地提高性能。所以,我们会查看所有可能。 3.2 CPU对性能的影响 在2.3.3节中,我解释了当我们启动多线程代码时发生的事件序列。在2.4节中,我还列出了许多你可能会想到的如何解释表2-1的问题。让我们来回答第一类也是最明显的一类问题: 当CPU不同时,这些结果会如何变化? 取决于CPU的速度,还是核心数量,线程数? 或者是其他与CPU有关的属性?比如高速缓存? […]