Python 代码比 Java 慢60倍

3

这段代码执行Haversine距离计算,是一个更大项目的一部分。

Java实现似乎比Python快60倍。它们的实现几乎完全相同。

这个性能测试是在同一台机器和操作系统上进行的。我尝试了各种组合:

  1. 操作系统:Linux和Windows
  2. 使用不同CPU的机器(来自第四代和第六代的i5)

但执行速度的差异保持一致。

Python实现:

from math import radians, sin, cos, sqrt, asin
import time 

def haversine(lat1, lon1, lat2, lon2):

  R = 6372.8 # Earth radius in kilometers

  dLat = radians(lat2 - lat1)
  dLon = radians(lon2 - lon1)
  lat1 = radians(lat1)
  lat2 = radians(lat2)

  a = sin(dLat/2)**2 + cos(lat1)*cos(lat2)*sin(dLon/2)**2
  c = 2*asin(sqrt(a))

  return R * c

start_time = time.time()
#START: Performance critical part
for x in range(1, 1*1000*1000*10): 
    haversine(36.12, -86.67, 33.94, -118.40)
#END: Performance critical part
elapsed_time = time.time() - start_time
print("Python elapsed time = " + str(elapsed_time)+" seconds")

Java实现:

import java.util.Date;

public class HaversineTest {
    public static final double R = 6372.8; // In kilometers
    public static double haversine(double lat1, double lon1, double lat2, double lon2) {
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        lat1 = Math.toRadians(lat1);
        lat2 = Math.toRadians(lat2);

        double a = Math.pow(Math.sin(dLat / 2),2) + Math.pow(Math.sin(dLon / 2),2) * Math.cos(lat1) * Math.cos(lat2);
        double c = 2 * Math.asin(Math.sqrt(a));
        return R * c;
    }
    public static void main(String[] args) {
        int loopCount = 1*1000*1000*10;
        long start_time = new Date().getTime();
        for(int i=0;i<loopCount;i++){
            haversine(36.12, -86.67, 33.94, -118.40);
        }
        long end_time = new Date().getTime();
        long elapsed_time=end_time-start_time;
        System.out.println("Java elapsed time = "+elapsed_time+" ms");
    }
}

版本:

Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC v.1900 64 bit (AMD64)] on win32

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

输出:

Java 耗时 = 229 毫秒

Python 耗时 = 15.028019428253174 秒

我能提高 Python 性能吗?


3
Java会被编译成字节码,而Python是解释型语言。 - Yoav Sternberg
3
如果你想用Python更快地完成某些任务,你应该考虑使用numpy库并向量化计算。请参阅Fast Haversine Approximation (Python/Pandas)。希望纯Python能与任何编译语言竞争是徒劳的,这个展示了Python在多个方面上与Java相比表现不佳。它们各自擅长不同的领域。 - roganjosh
6
我正在投票关闭此问题,因为寻求提高代码速度或效率的具有可运行代码的问题应该发表在 CodeReview.StackExchange 上。Stack Overflow 用于涉及代码在某些方面不起作用的问题。 - TylerH
2
@Prune 我在问题或评论中没有看到 OP 问为什么它更慢。我看到的唯一一个来自 OP 的问题是“有什么可以改变以提高 Python 性能吗? - TylerH
2
@TylerH:我明白了;我对总体趋势的解释有误,我相信这是我的错误。这就是我的摇摆不定;我是第四个关闭投票者。 - Prune
显示剩余14条评论
2个回答

3

有两个主要的瓶颈:

  • 循环
  • 函数调用(haversine 函数和 math 函数)。

如果您使用 并操作数组,您只需要调用一次函数,因为循环是在数组上向量化的 - 最终结果是代码在我的计算机上运行速度快了30倍(代码取自Prunes answer,但已更改为可与 numpy 数组一起使用):

import numpy as np

R = 6372.8 # Earth radius in kilometers

def haversine(lat1, lon1, lat2, lon2):
    dLat = np.radians(lat2 - lat1)
    dLon = np.radians(lon2 - lon1)
    lat1 = np.radians(lat1)
    lat2 = np.radians(lat2)
    sinLat = np.sin(dLat/2)
    sinLon = np.sin(dLon/2)

    a = sinLat * sinLat + np.cos(lat1) * np.cos(lat2) * sinLon * sinLon
    c = 2 * np.arcsin(np.sqrt(a))

    return R * c

haversine(np.ones(10000000) * 36.12,
          np.ones(10000000) * -86.67,
          np.ones(10000000) * 33.94,
          np.ones(10000000) * -118.40)

但这会创建许多巨大的临时数组,使用可以避免它们并仍然使用数学函数:

from math import radians, sin, cos, sqrt, asin
from numba import njit

@njit
def haversine(lat1, lon1, lat2, lon2):

    R = 6372.8 # Earth radius in kilometers

    dLat = radians(lat2 - lat1)
    dLon = radians(lon2 - lon1)
    lat1 = radians(lat1)
    lat2 = radians(lat2)

    a = sin(dLat/2)**2 + cos(lat1)*cos(lat2)*sin(dLon/2)**2
    c = 2*asin(sqrt(a))

    return R * c

@njit
def haversine_loop():
    res = np.empty(10*1000*1000, dtype=np.float_)
    for x in range(0, 10*1000*1000): 
        res[x] = haversine(36.12, -86.67, 33.94, -118.40)
    return res

haversine_loop()

这实际上比numpy代码提高了50倍(最终速度快了150倍)。但您需要检查在您的情况下是否可行采用这种方法(numba不是轻量级依赖项!)。


谢谢,我会尝试这些更改。 - Shamit Verma
30次 * 50次 = 1500次.... 所以最后的代码比TS提供的代码快了1500倍? - wmac
@wmac 哎呀 - 看起来这是我的错误。我稍后会再次检查数字。感谢您提出这个问题。 - MSeifert

1

你希望它有多快?使用一个修饰器,您可以获得6倍的加速。

如果您有大量这样的任务需要完成,那么numexpr或其他numpy方法可能仍然更快。因此,这取决于您需要它做什么。

from numba import jit

@jit(nopython=true)
def havershine ....

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接