一种将Python速度提高1000倍的解决方案

人们说Python很慢,可能会很慢

每当出现编程速度竞赛时,Python通常都会走到最底层。有人说这是因为Python是一种解释语言。所有的解释语言都很慢。但是我们知道Java也是一种语言,它的字节码由JVM解释。

https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/python3-java.html

如本基准测试所示,Java比Python快得多。

这是一个可以演示Python慢度的示例。使用传统的for循环产生倒数:

import numpy as np
np.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 s±582毫秒(平均±标准偏差,共7次运行,每个循环1次)

神圣的xxx,计算1,000,000个倒数需要3.37s。C语言中的相同逻辑只需要眨一下就可以了:9ms ; C#需要19毫秒; Nodejs花费26ms ; Java需要5毫秒!而Python则采用了自我怀疑的3.37秒。(我在最后附加了所有测试代码)。

缓慢的根本原因

我们通常将Python称为动态类型编程语言。而且Python程序中的所有内容都是object,换句话说,每次Python代码处理数据时,都需要将对象包装拆箱。在for循环内部,每次迭代都需要拆箱对象,检查类型并计算倒数。那3秒钟都在类型检查中浪费了

与C之类的传统语言不同,对数据的访问是直接的,而在Python中,大量的CPU周期用于检查类型。

e129ce7e3e7d9971ea57345ea69e4beb.png

即使是简单的数字分配也将花费很长时间。

a = 1
步骤1.设置a->PyObject_HEAD->typecode为整数
步骤2.设置a->val =1
那么,有没有一种方法可以解决类型检查,从而提高性能呢?

解决方案:NumPy通用函数

与Python列表不同,NumPy数组是围绕C数组构建的对象。NumPy中的访问项无需任何步骤即可检查类型。这使我们了解了解决方案,它是NumPy通用函数(又称UFunc)

cb3289a98f74ded150b18fb0f24bd640.png

简而言之,UFunc是一种我们可以直接对整个数组进行算术运算的方法。将第一个慢速Python示例转换为UFunc版本,它将像这样:

import numpy as np
np.random.seed(0)
values = np.random.randint(1, 100, size=1000000)
%timeit result = 1.0/values

此代码不仅可以提高速度,还可以缩短代码长度。猜猜现在需要多少时间?比我上面提到的任何其他语言快2.7ms

每个循环2.71 ms±50.8 µs(平均±标准偏差,共运行7次,每个循环100个)
返回代码,关键是1.0/values。值这里是不是一个数字,它是一个NumPy的阵列。像除法运算符一样,还有很多其他运算符。

009f7e6cb5173931ab6ea306b21bc81e.png

检查这里的所有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;
    gettimeofday(&start, NULL);
    int length = 1000000;
    int rand_array[length];
    float output_array[length];
    for(int i = 0; i<length; i++){
        rand_array[i] = rand();
    }
    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#(dotnet 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);

往期推荐

渗透测试员必备!Top 10免费黑客工具

手把手教你用Scrapy+Gerapy部署网络爬虫

这是我见过最有用的Mysql面试题,面试了无数公司总结的(内附答案)

面试稳了!集齐几千名程序员精选的 100 道性能面试题!

刷完500道高频面试题,我能去面试大厂了吗?(持续更新)

SeleniumWebDriver运行数据库测试?

Selenium Webdriver上传文件,别傻傻的分不清得3种方法

如何将功能测试用例转为自动化脚本?

微软开源最强自动化工具-Playwright

如何使用Selenium WebDriver查找错误的链接?

0aed7111d5d86fcb2ff2d64125a3c8bb.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件测试test

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值