理解高性能Python

Understand High Performance Python

Posted by Rancho on 2018-06-24 Word Count 1.1k, 3 min to read

前言

计算机编程可以认为是对输入数据进行特定的移动转换后得到某种结果。通常,在对数据进行这些操作时,会不可避免地产生时间上的开销。因此,高性能编程可以认为通过某些手段来让这些操作的代价最小化,例如:

  • 降低运行开销(撰写更加高效的代码)
  • 改变操作方式(寻找更加高效的算法)

数据的移动发生在实际的硬件上,通常高级语言在背后做了很多工作,将我们对硬件的直接操作进行抽象和封装。理解数据在硬件层面的移动方式以及Python在抽象层面管理数据的方式,可以帮助我们撰写高性能Python代码。

基本的计算机系统

一台计算机的底层组件可以分为三大基本部分:计算单元存储单元,以及两者之间的连接方式。对于计算单元(CPU),我们通常关心它每秒能进行多少次计算,而对于存储单元,我们则关心它能存放多少数据以及它的读写速度是多少。

计算单元

一台计算机的计算单元是其中央部件,被大众熟知的CPU就是一种常见的计算单元。值得一提的是,最初被设计于加速图像处理的GPU因其自身的并行计算模式,现在更加适用于数值计算了。无论计算单元是如何架构的,一个计算单元总是会接收一系列的比特,并输出另外一堆比特。

计算单元的主要属性是其每个周期能进行的操作数量以及每秒能完成多少个周期。衡量第一个属性的是每周期完成的指令数(IPC),而第二个属性则通过时钟速度衡量。这两个参数总是互相竞争。比如Intel的Core系列具有非常高的IPC但时钟速度较低,而Pentium4的芯片则恰好相反。

当提高IPC时,计算单元的矢量计算能力随之提高,而当时钟速度提高时,能够立即提高该计算单元上所有程序的运行速度,因为这意味着每秒能够进行更多的运算。可能普遍会认为计算机硬件世界始终受摩尔定律影响,但实际上在过去十年,IPC和时钟速度的提升基本处于停滞,因为晶体管已经接近物理上的极限。其结果就是芯片制造商开始依靠其他手段来获得更高的速度,包括超线程技术,乱序执行和多核架构。

超线程技术为主机的操作系统虚拟了第二个CPU,而聪明的硬件逻辑则试图将两个指令线程交错地插入单个CPU的执行单元。如果插入成功,会比单线程模式提高30%。

乱序执行允许编译器检测出一个线性程序中不依赖于上下文的部分,可就是说两个部分能够同时进行,而不会互相影响。这使得当一些指令被阻塞时,另一些指令得以执行,以此来提升资源的利用率。

多核架构在同一个计算单元中包含了多个CPU,提高了总体计算能力。但一味地增加更多核心不一定能提升程序的运行速度。这是由阿姆达尔定律决定的。简单地说,任何并行计算的瓶颈最终都会落到其顺序执行的那部分任务上。

存储单元

在计算机存储中,通常存储系统的读写速度与其容量成反比————当我们试图加快读写速度时,存储容量就下降了。因此大部分系统都实现了分层存储。

通信层

基本单元之间的通信模式有很多种,但它们都是同一样东西的变种:总线

一条总线的主要属性是它的速度:在给定时间内能够传输多少数据。这由两个因素决定:每次传输的数据量(总线带宽)和每秒传输多少次(总线频率)。

Python虚拟机

  • 适量操作不再是直接可用的
  • 内存布局的变化
  • GIL影响并行性能