python实现连续数列相加_全方位对比:Python、Julia、MATLAB、IDL 和 Java (2019 版)...

f109108ab84889a3aeba703540ba2e79.gif

引言

我们使用简单的测试用例来对各种高级编程语言进行比较。我们是从新手程序员的角度来实现测试用例,假设这个新手程序员不熟悉语言中可用的优化技术。我们的目的是突出每一种语言的优缺点,而不是宣称一种语言比其他语言更优越。计时结果以秒为单位,精确到四位数,任何小于 0.0001 的值将被视为 0 秒。

本文提供的测试是在 Intel Xeon Haswell 处理器节点上进行的,每个节点有 28 核(每核 2.6GHz)和 128GB 的可用内存。Python、Java 和 Scala 测试运行在一台 Mac 计算上,该计算机配备了 Intel i7-7700HQ(4 核,每核 2.8GHz),16GB 可用内存,以便与 Xeon 节点进行比较。我们考虑这些语言使用如下版本:

语言版本号是否开源
Python3.7
Julia0.6.2
Java10.0.2
Scala2.13.0
IDL8.5
R3.6.1
MatlabR2017b
GNU Compilers9.1
Intel Compilers18.0.5.274

GNU 和 Intel 编译器用于 C 和 Fortran。包含这些语言是为了作为基准,这就是为什么它们的测试也带有优化版本(-O3、-Ofast)的原因。

测试用例分为四类:

  • 循环和向量化

  • 字符串操作

  • 数值计算

  • 输入 / 输出

每个测试都足够“简单”,可以用任何一种语言快速编写,旨在解决以下问题:

  • 非连续内存位置的访问

  • 递归函数的使用

  • 循环或向量化的利用

  • 海量文件的打开

  • 任意长度的字符串的操作

  • 矩阵的乘积

  • 迭代求解的使用

  • 等等

源文件包含在以下目录中:

复制代码

C\ Fortran\ IDL\ Java\ Julia\ Matlab\ Python\ R\ Scala\

还有一个目录:

复制代码

Data\

它包含一个 Python 脚本,该脚本在读取大量文件时生成测试用例所需的 NetCDF4 文件。它还有用于“计算文件中唯一单词”测试用例的示例文本文件。

备注:在下面显示的结果中,我们使用了较旧版本的 Julia,因为在 Xeon Haswell 节点上安装最新版本的 Julia(1.1.1) 时我们遇到了困难。此外,Python 实验并不包括 Numba,因为我们有权访问的 Haswell 节点使用的是较旧版本的操作系统,妨碍了 Numba 的正确安装。

循环与向量化

  • 复制多维数组

给定任意 n x n x 3 矩阵 A,我们将执行以下操作:

复制代码

A(i, j, 1) = A(i, j, 2)

A(i, j, 3) = A(i, j, 1)

A(i, j, 2) = A(i, j, 3)

循环和向量化的使用。该测试用例旨在测量语言访问连续内存位置的速度,并查看每种语言如何处理循环和向量化。

表 CPA-1.0:在 Xeon 节点上使用循环复制矩阵元素所用的时间。

语言选项n=5000n=7000n=9000
Python16.216431.786752.5485
Julia0.07220.14450.2359
Java0.18100.32300.5390
Scala0.27500.48100.7320
IDL6.466111.906819.4499
R22.951044.976074.3480
Matlab0.28490.52030.8461
Fortrangfortran0.17600.34800.5720
gfortran -O30.06800.17200.2240
ifort0.06800.13600.2240
ifort -O30.06800.13600.2800
Cgcc0.17000.34000.5600
gcc -Ofast0.09000.18000.3100
icc0.10000.18000.3000
icc -Ofast0.10000.18000.3000

表 CPA-1.1:在 i7 Mac 上使用循环复制矩阵元素所用的时间。

语言n=5000n=7000n=9000
Python18.667536.404660.2338
Python (Numba)0.33980.30600.3693
Java0.12600.24200.4190
Scala0.20400.34500.5150

表 CPA-2.0:在 Xeon 节点上使用向量化复制矩阵元素所用的时间。

语言选项n=5000n=7000n=9000
Python0.49560.97391.6078
Julia0.31730.55750.9191
IDL0.39000.76411.2643
R3.52906.935011.4400
Matlab0.28620.55910.9188
Fortrangfortran0.09600.25200.3240
gfortran -O30.09600.24400.3120
ifort0.14000.22800.3840
ifort -O30.12000.23600.4560

表 CPA-2.1:在 i7 Mac 上使用向量化复制矩阵元素所用的时间。

语言n=5000n=7000n=9000
Python0.56021.08321.8077
Python (Numba)0.85071.36502.0739

字符串操作

  • 外观数列

外观数列(Look and Say Sequence)读取一个整数。在后续的每个项中,前一个项中每个整数出现的次数连接到该整数的前面。如,一个项 1223,接下来将会是 112213 ,或“一个 1,两个 2,一个 3”。这里,我们从数字开始:1223334444 ,并确定 n 项(随 n 不同)的外观数列,这个测试用例突出显示了语言如何操作操纵任意长度的字符串。

表 LKS-1.0:在 Xeon 节点上查找 n 项的外观数列所用的时间。

语言选项n=40n=45n=48
Python2.089044.4155251.1905
Java0.06940.08990.1211
Scala0.04700.12700.2170
IDL20.2926304.50491612.4277
Matlab423.22416292.7255exceeded time limit
Fortrangfortran0.00800.01200.0120
gfortran -O30.00800.01200.0120
ifort0.00400.01600.0120
ifort -O30.00800.00400.0080
Cgcc0.06000.19000.4300
gcc -Ofast0.04000.18000.4000
icc0.06000.19000.4100
icc -Ofast0.05000.19000.4100

表 LKS-1.1:在 i7 Mac 上查找 n 项的外观数列所用的时间。

语言n=40n=45n=48
Python1.733122.3870126.0252
Java0.06650.09120.1543
Scala0.04900.09700.2040
  • 文件中的唯一单词

我们打开一个任意文件,并计算其中唯一单词的数量,假设单词如下:

复制代码

ab Ab aB a&*(-b: 17;A#~!b

数量是相同的(在这样的情况下,大小写、特殊字符和数字将被忽略)。在我们的测试中,使用了四个文件:

复制代码

world192.txt、plrabn12.txt、bible.txt、book1.txt

这些文件取自 Canterbury 语料库。

表 UQW-1.0:在 Xeon 节点上计算文件中的唯一单词所用的时间。

语言world192.txt(19626 个单词)plrabn12.txt(9408 个单词)bible.txt(12605 个单词)book1.txt(12427 个单词)
Python (dictionary method)0.50020.10900.88690.1850
Python (set method)0.38140.08730.75480.1458
Julia0.21900.03540.32390.0615
Java0.56240.22991.01350.2901
Scala0.46000.21500.69300.2190
R104.58208.644033.821017.6720
Matlab3.02700.96576.03481.0390

表 UQW-1.1:在 i7 Mac 上计算文件中唯一单词所用的时间。

语言world192.txt(19626 个单词)plrabn12.txt(9408 个单词)bible.txt(12605 个单词)book1.txt(12427 个单词)
Python (dictionary method)0.35410.08660.73460.1448
Python (set method)0.36850.08200.71970.1417
Java0.51290.25300.91830.3220
Scala0.58100.15400.66500.2330

数值计算

  • 斐波那契数列

斐波那契数列是一个数字序列,其中每个连续的数字是它前面两个数字的和:

eed57c40c1d6562702cc4fc60cac3b5f.png

它的第一项是:

040b0d9177fd44535662928573e208d7.png

斐波那契数列在经济学、计算机科学、生物学、组合学等领域都有广泛的应用。我们在计算第 n 个斐波那契数列时测量所用的时间。迭代计算和递归计算都需要计算时间。

表 FBC-1.0:在 Xeon 节点上迭代查找斐波那契数列所用的时间。

语言选项n=25n=35n=45
Python000
Julia000
Java000
Scala000
IDL000
R0.03300.03200.0320
Matlab0.00260.00340.0038
Fortrangfortran000
gfortran -O3000
ifort000
ifort -O3000
Cgcc000
gcc -Ofast000
icc000
icc -Ofast000

表 FBC-1.1:在 i7 Mac 上迭代查找斐波那契数列所用的时间。

语言n=25n=35n=45
Python000
Python (Numba)0.11000.10950.1099
Java000
Scala000

表 FBC-2.0:在 Xeon 节点上递归查找斐波那契数列所用的时间。

语言选项n=25n=35n=45
Python0.05937.0291847.9716
Julia0.00030.03083.787
Java0.00110.04104.8192
Scala0.00100.05605.1400
IDL0.02382.5692304.2198
R0.00900.01000.0100
Matlab0.01421.2631149.9634
Fortrangfortran00.084010.4327
gfortran -O3000
ifort000
ifort -O3000
Cgcc00.04005.0600
gcc -Ofast00.02002.2000
icc00.03003.1400
icc -Ofast00.02003.2800

表 FBC-2.1:在 i7 Mac 上递归查找斐波那契数列所用的时间。

语言n=25n=35n=45
Python0.05196.4022800.0381
Python (Numba)0.417243.76045951.6544
Java0.00300.04425.0130
Scala0.00100.04705.7720
  • 矩阵乘法

将两个随机生成的 n x n 矩阵 A 和 B 相乘。测量执行乘法的时间。这个问题说明了利用每种语言中可用的内置库的重要性。

表 MXM-1.0:在 Xeon 节点上进行矩阵相乘所用的时间。

语言选项n=1500n=1750n=2000
Pythonintrinsic0.15600.24300.3457
Juliaintrinsic0.14970.23980.3507
Javaloop13.861017.860032.3370
Scalaloop9.838019.145032.1310
Rintrinsic0.16000.24600.3620
Matlabintrinsic1.36721.39510.4917
IDLintrinsic0.18940.23090.3258
Fortrangfortran (loop)17.437131.466062.1079
gfortran -O3 (loop)3.32825.300312.1648
gfortran (matmul)0.38400.61600.9241
gfortran -O3 (matmul)0.38800.61600.9161
ifort (loop)1.14011.81612.9282
ifort -O3 (loop)1.14811.80812.9802
ifort (matmul)1.14411.81212.9242
ifort -O3 (matmul)0.51600.82811.2441
ifort (DGEMM)0.21600.23600.3320
Cgcc (loop)13.200020.980031.4400
gcc -Ofast (loop)1.45002.36004.0400
icc (loop)1.23002.15004.0500
icc -Ofast (loop)1.15001.75002.5900

表 MXM-1.1:在 i7 Mac 上进行矩阵相乘所用的时间。

语言选项n=1500n=1750n=2000
Pythonintrinsic0.09060.11040.1611
Numba (loop)9.259520.201235.3174
Javaloop32.508047.768082.2810
Scalaloop23.054038.911060.3180
  • 置信传播算法

置信传播是一种用于推理的算法,通常用于人工智能、语音识别、计算机视觉、图像处理、医学诊断、奇偶校验码等领域。我们用 5000x5000 元素矩阵来测量算法进行 n 次迭代所用的时间。在 Justin Domke 的博客( Domke 2012 )中展示了 MATLAB、C 和 Julia 的代码,该博客指出,这个算法是“矩阵乘法的重复序列,然后进行归一化”。

表 BFP-1.0:在 Xeon 节点上执行置信传播算法所用的时间。

语言选项n=250n=500n=1000
Python3.70767.082413.8950
Julia4.02807.822015.1210
Java63.9240123.3840246.5820
Scala53.5170106.4950212.3550
IDL16.960933.208665.7071
R23.415045.416089.7680
Matlab1.97603.80877.4036
Fortrangfortran21.001341.010687.6815
gfortran -O34.49238.256517.5731
ifort4.73639.108617.8651
ifort -O34.73639.108621.1973
Cgcc2.64005.290010.5800
gcc -Ofast2.42004.85009.7100
icc2.16004.32008.6500
icc -Ofast2.18004.34008.7100

表 BFP-1.1:在 i7 Mac 上执行置信传播算法所用的时间。

语言n=250n=500n=1000
Python2.41214.54228.7730
Java55.3400107.7890214.7900
Scala47.956095.3040189.8340
  • 梅特罗波利斯 - 黑斯廷斯(Metropolis-Hastings)算法

梅特罗波利斯 - 黑斯廷斯算法是一种用于从概率分布中提取随机样本的算法。该实现使用二维分布(Domke 2012 ),并测量迭代 n 次所用的时间。

表 MTH-1.0:在 Xeon 节点上执行梅特罗波利斯 - 黑斯廷斯算法所用的时间。

语言选项n=5000n=10000n=15000
Python0.04040.08050.1195
Julia0.00020.00040.0006
Java0.00400.00500.0060
Scala0.00800.00900.0100
IDL0.01340.01050.0157
R0.07600.15000.2230
Matlab0.01830.02110.0263
Fortrangfortran000
gfortran -O3000
ifort0.004000
ifort -O30.00400.00400
Cgcc000
gcc -Ofast000
icc000
icc -Ofast000

表 MTH-1.1:在 i7 Mac 上执行梅特罗波利斯 - 黑斯廷斯算法所用的时间。

语言n=5000n=10000n=15000
Python0.03460.06380.0989
Java0.00600.00400.0060
Scala0.00900.01000.0130
  • 快速傅里叶变换

我们创建一个 n x n 矩阵 M ,其中包含随机复值。我们计算了 M 的快速傅里叶变换和结果的绝对值。快速傅里叶变换算法广泛用于各种科学和工程领域的信号处理和图像处理。

表 FFT-1.0:在 Xeon 节点上计算快速傅里叶变换所用的时间。

语言选项n=10000n=15000n=20000
Pythonintrinsic8.079719.635734.7400
Juliaintrinsic3.97911.49020.751
IDLintrinsic16.669938.985770.8142
Rintrinsic58.2550150.1260261.5460
Matlabintrinsic2.62436.001010.66232

表 FFT-1.1:在 i7 Mac 上计算快速傅里叶变换所用的时间。

语言选项n=10000n=15000n=20000
Pythonintrinsic7.953821.535555.9375
  • 迭代求解器

我们使用雅克比法迭代求解器(Jacobi iterative solver)数值逼近二维拉布拉斯方程(Laplace equation)的解,该解用四阶紧致格式离散(Gupta,1984)。随着网络点数量的变化,我们记录所用的时间。

表 ITS-1.0:在 Xeon 节点上迭代计算近似解所用的时间。

语言选项n=100n=150n=200
Python158.2056786.34252437.8560
Julia1.03085.187016.1651
Java0.41301.89505.2220
Scala0.5402.10305.7380
IDL73.2353364.13291127.1094
R157.1490774.70802414.1030
Matlab2.81635.05438.6276
Fortrangfortran0.82403.732010.7290
gfortran -O30.66803.07208.8930
ifort0.54002.47207.1560
ifort -O30.54002.46807.1560
Cgcc0.50002.42007.7200
gcc -Ofast0.22001.05003.1900
icc0.46002.23006.7800
icc -Ofast0.33001.60004.8700

表 ITS-1.2:在 i7 Mac 上迭代计算近似解所用的时间。

语言n=100n=150n=200
Python174.7663865.12032666.3496
Python (Numba)1.32265.032415.1793
Java0.46001.76904.7530
Scala0.59702.09505.2830

表 ITS-2.0:在 Xeon 节点上使用向量化计算近似解所用的时间。

语言选项n=100n=150n=200
Python2.627214.650540.2124
Julia2.458313.191841.0302
IDL1.711928.684128.0683
R25.2150121.9870340.4990
Matlab3.32917.648615.9766
Fortrangfortran0.86804.204011.5410
gfortran -O30.36001.80405.0880
ifort0.28001.53604.4560
ifort -O30.28001.56004.4160

表 ITS-2.1:在 i7 Mac 上使用向量化计算近似解所用的时间。

语言n=100n=150n=200
Python1.70517.457222.0945
Python (Numba)2.44518.509421.7833
  • 矩阵的平方根

给定 n x n 矩阵 A,我们寻找这样的矩阵 B,使得:

B * B = A

B 就是平方根。在我们的计算中,我们考虑对角线上为 6,别处为 1 的矩阵 A。

表 SQM-1.0:在 Xeon 节点上计算矩阵的平方根所用的时间。

语言n=1000n=2000n=4000
Python1.01015.237644.4574
Julia0.42072.508019.0140
R0.56503.066019.2660
Matlab0.35711.65522.6250

表 SQM-1.1:在 i7 Mac 上计算矩阵的平方根所用的时间。

语言n=1000n=2000n=4000
Python0.56533.396325.9180
  • 高斯求积

高斯求积(Gauss-Legendre Quadrature)是逼近定积分的一种数值方法。它使用被积函数的 n 个值的加权和。如果被积函数是 0 到 2 n - 1 次多项式,则结果是精确的。这里我们考虑区间 [-3, 3] 上的指数函数,并记录当 n 变化时执行积分所用的时间。

表 GLQ-1.0:在 Xeon 节点上计算积分近似值所用的时间。

语言选项n=50n=75n=100
Python0.00790.00950.0098
Julia0.00020.00040.0007
IDL0.00430.00090.0014
R0.02600.02400.0250
Matlab0.74760.07310.4982
Fortrangfortran00.00400.0080
gfortran -O300.01200.0120
ifort0.00800.00800.0080
ifort -O30.00800.00400.0080

表 GLQ-1.1:在 i7 Mac 上计算积分近似值所用的时间。

语言n=50n=75n=100
Python0.01400.00350.0077
  • 三角函数

我们在 n 元素值列表上迭代计算三角函数,然后在同一列表上计算反三角函数。当 n 发生变化时,测量完整全部操作所用的时间。

表 TRG-1.0:在 Xeon 节点上计算三角函数所用的时间。

语言选项n=80000n=90000n=100000
Python14.689116.508423.6273
Julia55.392062.949069.2560
IDL37.441341.969535.2387
R91.5250102.8720113.8600
Matlab5.27945.86496.3699
Scala357.3730401.8960446.7080
Java689.6560774.9110865.057
Fortrangfortran53.483360.031766.6921
gfortran -O349.927156.023562.1678
ifort18.641120.957323.2654
ifort -O318.645120.957323.2694
Cgcc107.4400120.7300134.0900
gcc -Ofast93.0400104.5700116.0600
icc76.260085.790095.3100
icc -Ofast48.840054.960061.0600

表 TRG-1.1:在 i7 Mac 上计算三角函数所用的时间。

语言n=80000n=90000n=100000
Python3.53996.19846.9207
  • Munchausen 数

Munchausen 数 是一个自然数,等于其自身幂次的位数之和。在以 10 为基数的情况下,有 4 个这样的数字:0、1、3435 和 438579088。我们来确定找到这些数字需要多久。

表 MCH-1.0:在 Xeon 节点上查找 Munchausen 数所用的时间。

语言选项所用时间
Python1130.6220
Julia102.7760
Java4.9008
Scala72.9170
Rexceeded time limit
IDLexceeded time limit
Matlab373.9109
Fortrangfortran39.7545
gfortran -O321.3933
ifort29.6458
ifort -O329.52184
Cgcc157.3500
gcc -Ofast126.7900
icc228.2300
icc -Ofast228.1900

表 MCH-1.1:在 i7 Mac 上查找 Munchausen 数所用的时间。

语言所用时间
Python1013.5649
Java4.7434
Scala64.1800

输入 / 输出

  • 读取大量文件

我们有一套涵盖 20 年的每日 NetCDF 文件(7305)。给定年份的文件位于一个标记为 YYYY 的子目录中(例如,Y1990、Y1991、Y1992 等)。我们希望编写一个脚本,打开每个文件,读取一个三维变量(经度 / 维度 / 级别)并对其进行操作。脚本的伪代码如下:

复制代码

Loop over the years

Obtain the list of NetCDF files

Loop over the files

Read the variable (longitude/latitude/level)

Compute the zonal mean average (new array of latitude/level)

Extract the column array at latitude 86 degree South

Append the column array to a "master" array (or matrix)

目标是能够生成三维数组(年份 / 级别 / 值)并执行等高线图。这是我们支持的典型用户面临的问题类型:需要对数千个文件进行操作以提取所需信息的集合。拥有能够从文件中快速读取数据(如 NetCDF、HDF4、HDF5、grib 等格式)的工具对我们的工作至关重要。

表 RCF-1.0:在 Xeon 节点上处理 NetCDF 文件所用的时间。

语言所用时间
Python660.8084
Julia787.4500
IDL711.2615
R1220.222
Matlab848.5086

表 RCF-1.1:在 i7 Mac 上处理 NetCDF 文件所用的时间。

语言所用时间
Python89.1922

表 RCF-2.0:在 Xeon 节点上利用多核处理器使用 Python 处理 NetCDF 文件所用的时间。

所用时间
1570.9791
2317.6108
4225.4647
8147.4527
1684.0102
2459.7646
2851.2191

表 RCF-2.1:在 i7 Mac 上利用多核处理器使用 Python 处理 NetCDF 文件所用的时间。

所用时间
184.1032
263.5322
456.6156

图表总结

在下面的图中,我们通过使用 GCC 获得的计时数字(仅在最后一列,即最大问题的大小)作为参考,总结上述计时结果。

1e013e3fac2579d238152c9379a35cbf.png

8f5b763fcff2ec9ea8c4e604a48e701e.png

研究结果

概述:

  • 没有任何一种语言在所有测试中都优于其他语言。

  • 通过仅在必要时创建变量以及“清空”不再使用的变量来减少内存占用非常重要。

  • 对于相同的任务,使用内置函数会比内联代码带来更高的性能。

  • Julia 和 R 提供了简单的基准测试工具。我们编写了一个简单的 Python 工具,允许我们随心所欲地多次运行 Python 测试用例。

循环和向量化:

  • 与使用循环相比,Python(和 NumPy)、IDL 和 R 在向量化时运行速度更快。

  • 在使用 Numba 时,只要使用 NumPy 数组,Python 就可以更快地处理循环。

  • 对于 Julia,循环比向量化代码运行得更快。

  • 在不涉及计算的情况下,使用循环与向量化相比,MATLAB 在性能上似乎没有显著变化。当进行计算时,向量化 MATLAB 代码要比迭代代码更快。

字符串操作:

  • 与其他语言相比,Java 和 Scala 在操作大型字符串时,似乎具有显著的性能。

数值计算:

  • 与其他语言相比,R 在使用递归时似乎具有显著的性能。

  • 语言在数值计算中相对于其他语言的性能表现取决于具体的任务。

  • MATLAB 的内置快速傅里叶变换函数似乎运行速度最快。

输入 / 输出:

  • 虽然有些语言运行测试的速度比其他语言快,但在本地 Mac 上而不是处理器节点上运行测试的话,可以获得最大的性能提升。因为处理器节点使用机械硬盘,而 Mac 用的是固态硬盘。这表明硬件对 I/O 性能的影响比所使用的语言更大。

参考资料

  1. Julia, Matlab and C ,Justin Domke 著,2012 年 9 月 17 日

  2. 四阶泊松求解器,《计算物理学杂志》,55(1):166-172,Murli M. Gupta 著,1984 年

原文链接:

Basic Comparison of Python, Julia, Matlab, IDL and Java (2019 Edition)

832839c5f1acee2b495528f291bf877e.png

2d140094a4bb6c0e73b66ed90826ffe0.png 3a7b207c0be9325c2aee8c8ea941315f.png bff530bf0206eca2afd2372c37f7ed35.png 1e84acef5b8065056537a8cf4a197635.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值