首页 文章详情

Python 速度慢,试试这个方法提高 1000 倍(文末送书)

Python客栈 | 972 2021-06-05 11:04 0 0 0
UniSMS (合一短信)

龟兔比赛(我6岁儿子 Charles Zhu 的绘画作品)

作者 | Andrew Zhu

译者 | 苏本如  出品 | CSDN(ID:CSDNnews)


人们一直诟病 Python 程序的速度很慢,它到底有多慢呢?

在每次的编程语言速度竞赛中,Python 的名次通常都比较垫底。有人解释这是因为 Python 是一种解释型语言(代码无需编译即可执行),而所有的解释型编程语言执行速度都很慢。然而,我们知道 Java 也是一种解释型语言,它的字节码是由 JVM 解释的。而在这个基准测试速度比较页面上的结果却显示:Java 要比 Python 的速度快得多。
下面是一个可以用来演示 Python 速度慢的示例。它使用传统的 for 循环来产生一个数的倒数:
import numpy as npnp.random.seed(0)values = np.random.randint(1, 100, size=1000000)def get_reciprocal(values):output = np.empty(len(values))for i in range(len(values)):output[i] = 1.0/values[i]%timeit get_reciprocal(values)
结果显示:
每个循环平均耗时3.37秒(标准偏差±582毫秒)(共计运行了7次程序,每次一个循环)
计算 1,000,000 个倒数竟然需要 3.37 秒。使用 C 语言执行同样的运算只需要不到一眨眼的工夫:9 毫秒;C# 需要 19 毫秒;Nodejs 需要 26 毫秒;Java 仅仅需要 5 毫秒!而 Python 竟然用了让人怀疑人生的 3.37秒(它到底做了些什么)!(注:在本文的最后,我附上了所有语言的测试代码)。

Python 速度缓慢的根本原因

我们通常把 Python 称为一种动态类型编程语言。而 Python 程序中的一切变量都是以对象的形式存在,换句话说,每次 Python 代码处理数据时,都需要进行对象拆箱操作,以确定对象的具体类型。在 for 循环内部,每次循环都需要拆箱对象,检查类型并计算倒数。那3秒钟的时间都在类型检查中浪费了。
C 语言和其他传统的编程语言则不同,它们对数据的访问是直接的。但在 Python 中,大量的 CPU 时间都用在了类型检查上。
即使是一个简单的赋值操作也会花费很长的时间。如:
a = 1
这个简单的赋值操作,它需要如下两个步骤:
  • 步骤 1:将 a->PyObject_HEAD->typecode 设置为 Integer 类型.
  • 步骤 2. 将值 1 赋值 a (a->val =1).
关于 Python 为什么速度慢的更多信息,Jake 写的这篇精彩文章值得一读:Why Python is Slow: Looking Under the Hood
那么,有没有一种方法可以绕过类型检查,从而提高 Python 程序的性能呢?

答案是:使用 NumPy 通用函数

与 Python 列表(list)不同,NumPy 数组是围绕 C 数组构建的对象。NumPy 数组访问项不需要任何步骤来检查类型。这给我们找到解决方案指明了方向:使用 NumPy 通用函数(亦即UFunc)。
简而言之,UFunc 是一种可以直接对整个数组进行算术运算的方法。下面我们将前面那个慢速的 Python 示例改写为 UFunc 版本,它就像下面这样:
import numpy as npnp.random.seed(0)values = np.random.randint(1, 100, size=1000000)%timeit result = 1.0/values
改写后的代码不仅提高了速度,而且代码变得更短。猜猜现在这个程序执行要花多少时间?它比我上面提到的最快的语言快了2.7毫秒
每个循环平均耗时2.71毫秒(标准偏差±50.8微秒)(共运行了7次程序,每次循环100个)
返回代码,关键是 1.0/values 这一行。这里的 values 不是一个数字,而是一个 NumPy 数组。和除法运算符一样,Numpy 还有许多其他运算符(如下图示)。
点击这里可以找到所有 Ufunc 运算(操作)符。

总结

对于那些使用 Python 的人来说,使用 Python 处理数据和数字的可能性很大。这些数据可以存储在 NumPy 或 Pandas DataFrame中,因为DataFrame 是基于 NumPy 实现的。所以 Ufunc 也可以使用。
UFunc 使我们能够以超越几个数量级的更快速度在 Python 中执行重复操作。最慢的 Python 甚至可以跑得 C 语言更快。这一点太让人激动了。


附录— C,C#,Java 和 NodeJS 的测试代码

C 语言:
#include <stdio.h>#include <stdlib.h>#include <sys/time.h>
int main(){struct timeval stop, start;int length = 1000000;int rand_array[length];float output_array[length];for(int i = 0; i<length; i++){ rand_array[i] = rand(); } gettimeofday(&start, NULL);for(int i = 0; i<length; i++){ output_array[i] = 1.0/(rand_array[i]*1.0); } gettimeofday(&stop, NULL);printf("took %lu us\n", (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec); printf("done\n");return 0;}
C#(.net 5.0):
using System;namespace speed_test{class Program{static void Main(string[] args){int length = 1000000;double[] rand_array =new double[length];double[] output = new double[length];var rand = new Random();for(int i =0; i<length;i++){ rand_array[i] = rand.Next();//Console.WriteLine(rand_array[i]); }long start = DateTimeOffset.Now.ToUnixTimeMilliseconds();for(int i =0;i<length;i++){ output[i] = 1.0/rand_array[i]; }long end = DateTimeOffset.Now.ToUnixTimeMilliseconds(); Console.WriteLine(end - start); } }}
Java:
import java.util.Random;
public class speed_test {public static void main(String[] args){int length = 1000000;long[] rand_array = new long[length];double[] output = new double[length]; Random rand = new Random ();for(int i =0; i<length; i++){ rand_array[i] = rand.nextLong(); }long start = System.currentTimeMillis();for(int i = 0;i<length; i++){ output[i] = 1.0/rand_array[i]; }long end = System.currentTimeMillis(); System.out.println(end - start); }}
NodeJS:
let length = 1000000;let rand_array = [];let output = [];for(var i=0;i<length;i++){    rand_array[i] = Math.floor(Math.random()*10000000);}let start = (new Date()).getMilliseconds();for(var i=0;i<length;i++){    output[i] = 1.0/rand_array[i];}let end = (new Date()).getMilliseconds();console.log(end - start);


············END············

留言送书

 

推荐理由:


本书介绍了如何使用Python来编写网络爬虫程序,内容包括网络爬虫简介、发送请求、提取数据、使用多个线程和进程进行并发抓取、抓取动态页面中的内容、与表单进行交互、处理页面中的验证码问题及使用Scrapy和分布式进行数据抓取,并在最后介绍了使用本书讲解的数据抓取技术对几个真实的网站进行抓取的实例,旨在帮助读者活学活用书中介绍的技术。


推荐理由:


全新:本书理论、技术与案例基于全新的Python 3.x。全面:涵盖Python全栈开发全过程,知识点涉及数据分析、数据存储、数据可视化、系统网络运维、Web 项目和量化交易等多领域。实战:海量示例文件展示开发过程中的重点、疑点、难点。基础:零基础也能入门,拆解式讲解全栈开发过程中的知识和技术。丰富:本书配套的源代码全部赠送,资源丰富,可轻松复制所需项目内容


推荐理由:


本书专为没有任何 Python基础的初学者和爱好者打造,无论你是否从事计算机相关专业,是否有过 Python 项目经验,或是否想要转行从事计算机相关专业,均可通过本书快速掌握Python 的基本知识和开发技巧。



活动规则


活动截止时我们将从精选留言中选出 六条 走心留言赠送以上书籍,先到先选,免费包邮送到家~(每人当月仅限一次中奖机会



截止时间: 2021 年 6 月 4 日 16:00 整





往期推荐


1、鸿蒙手机来了!

2、5 月份最热 GitHub 项目

3、前端老弟第一次写后端,崩了!

4、揭秘:一个月不摸鱼能写多少代码?

5、又一程序员删库跑路被判刑,原因是离职后讨薪?

6、用 Python 写的批量翻译工具,效果竟超越付费软件?




今天因为您的点赞和在看,让我元气满满!
good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter