社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 速度慢,试试这个方法提高 1000 倍

Python编程 • 1 年前 • 186 次点击  

本文经授权转自公众号CSDN(ID:CSDNnews)

作者 | Andrew Zhu
译者 | 苏本如

1、人们一直诟病 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秒(它到底做了些什么)!(注:在本文的最后,我附上了所有语言的测试代码)。

2、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 程序的性能呢?

3、答案是:使用 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 运算(操作)符。

4、总结

对于那些使用 Python 的人来说,使用 Python 处理数据和数字的可能性很大。这些数据可以存储在 NumPy 或 Pandas DataFrame中,因为DataFrame 是基于 NumPy 实现的。所以 Ufunc 也可以使用。

UFunc 使我们能够以超越几个数量级的更快速度在 Python 中执行重复操作。最慢的 Python 甚至可以跑得 C 语言更快。这一点太让人激动了。

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

C 语言:

#include #include #include 
int main(){ struct timeval stop, start; int length = 1000000; int rand_array[length]; float output_array[length]; for (int i = 0; i rand_array[i] = rand(); } gettimeofday(&start, NULL); for(int i = 0; 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                rand_array[i] = rand.Next();                //Console.WriteLine(rand_array[i]);            }            long start = DateTimeOffset.Now.ToUnixTimeMilliseconds();            for(int i =0;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 rand_array[i] = rand.nextLong(); } long start = System.currentTimeMillis(); for(int i = 0;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    rand_array[i] = Math.floor(Math.random()*10000000);}let start = (new Date()).getMilliseconds();for(var i=0;i    output[i] = 1.0/rand_array[i];}let end = (new Date()).getMilliseconds();console.log(end - start);

原文链接:https://python.plainenglish.io/a-solution-to-boost-python-speed-1000x-times-c9e7d5be2f40

本文转自公众号“CSDN”,ID:CSDNnews

---END---


推荐↓↓↓

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/152865
 
186 次点击