Python性能优化有两个方面:
首先绕过Python解释器,大致有以下几个方法:
不使用默认的C解释器实现,使用Pypy,Jyhton等
编写C扩展,Numba等编译器
使用非默认的解释器实现,其实获得的好处并没有失去的多,因为使用非官方的解释器实现相当于意味着围绕着官方解释器所衍生出的各种 优化手段 生态!!都没用了。
同时非官方的解释器实现并不能支持最新的Python版本,甚至可能都不算是完整的Python。
现在可以看一下Jython还停留在2.7,然而当前Python正式发布的已经到了3.10
当然他会获得实现平台的类库,同时还没有GIL的限制。
Python在执行C代码的时候是不受GIL的限制的,C语言的性能当然也不必说。
不过一个重要问题是编写C扩展是否有意义。因为就Web应用来说,大部分时候都在等待IO了,计算做的再快有什么用呢?尽快去等待IO吗?
当然本身目标就是科学计算,机器学习那么当然应该都需要使用C扩展了。
不过大多数时候C扩展都不需要我们来写,例如科学计算有Numpy,Pandas,深度学习有Tensorflow,Pytorch等。
当你实在无可奈何需要做自己编写C语言扩展,便可以使用Cython。以一种类似Python语言的方法来编写C扩展。不过缺点就是现在代码每到一个平台都需要编译了。
只需要加上一个小小的注解便能够启用jit,关闭gil(当然关闭后你不能够进行声明变量和调用函数等操作)
更多等待发现...(没怎么用过)
讲过绕过解释器的优化手段,现在应该来讲一下代码层面的优化。
不过前面似乎遗漏了一个很重要的问题,就是你怎么是知道你应该优化哪一部分代码,一般应该先profile分析,然后再进行你的操作。
在代码层面上优化,除了做好本质工作写好代码以外,剩下其实很有限了,只能够从设计上面改进了。
这里提到一个很平常的改进,也就是将同步设计改进为异步设计。
异步设计有很多方法,这里提及最常用的多进程,多线程,以及协程。
unix这一派推崇的是多进程设计比多线程设计更有伸缩性,然而多线程真的一无是处吗?
当然也不是这样,比如多线程相对于多进程之间通信就很容易,因为他们除了栈空间意外都是共享的,但是共享的内存空间似乎带来了更多问题,因为多个线程之间可能同时访问同一变量,然后该变量在他读取之后修改之前(指令)却改变了。
所以便需要对多线程程序进行协调竞争,也就是说竞争的部分势必是不能够同步的,所以多线程的处理能力势必要低于N*单个线程的处理能力,多线程的处理能够取决于程序中能够同步的部分,也就是说使更多的代码能够拆分并行,就可以得到越大的性能提升。
但是依赖手动拆分代码,管理并行,实在是麻烦并且不可靠。
这里实在不得不提函数式编程,以Java引入的Stream API举例。
public static void main(String... args) { var nums = collection(range(1, 101)); var result = nums.stream().map(num -> Math.pow(num, 2)).toArray(); // 这里只需要将stream改为parallelStream便可改为并行操作 // var result = nums.parallelStream().map(num -> Math.pow(num, 2)).toArray(); for (var r : result) { System.out.println(r); } } public static Collection<Integer> collection(Iterable<Integer> iterable) { var col = new LinkedList<Integer>(); iterable.forEach(col::add); return col; } public static Iterable<Integer> range(int start, int end) { var iterator = new Iterator<Integer>() { private int nextNum = start; @Override public boolean hasNext() { return nextNum < end; } @Override public Integer next() { if (hasNext()) return nextNum++; throw new NoSuchElementException(); } }; return () -> iterator; }
由于并行的操作之间的结果互相不受影响,所以可以放心的并行,同时只需要更改一个函数便可以轻松的实现并行操作而不需要自己手动管理,杂务都交由语言去实现,岂不是十分方便。
有人说函数调用会带来额外的开销,然而这是否太不相信编译器&虚拟机的是实现者了呢?
是否偏离了主题。这里再来比较多线程和多进程.
资源占用?多线程共享栈外的内存空间,较轻量;多进程空间独立,占用多(也不一定,如写时复制机制)
是否需要同步? 多线程需要; 多进程不需要
通信方式? 多线程不需要;多进程间通信方式大致分为两种IPC和套接字(这里不展开来讲)
协程相比线程而言更为轻量,可以理解为可以暂停并在暂停位置重新开始的一个函数。
似乎并没有解释清楚协程是什么东西呢。本人水平有限只好读者自行搜索。
似乎讲了一些Python性能提升的方法。然而却又并未说明具体应该怎么做。这是因为我觉得Python的性能优化实在是个伪命题。
我觉得当你觉得Python的性能需要优化的时候,有两种可能:
Python或许是很慢,但是绝大多数的慢都不是由语言导致的,如果真的是由语言导致的慢,换个语言就好了