Python与Julia速度比较

13
我尝试比较这两段代码,并查看每秒钟可以执行多少次迭代。结果发现Julia执行了250万次迭代,而Python则执行了400万次迭代。难道Julia不应该更快吗?或者这两个片段不等价吗?

Python:

t1 = time.time()
i = 0
while True:
    i += 1
    if time.time() - t1 >= 1:
        break

朱莉娅:

function f()
    i = 0
    t1 = now()
    while true
        i += 1
        if now() - t1 >= Base.Dates.Millisecond(1000)
            break
        end
    end
    return i
end

4
我不确定Julia是如何工作的,但看起来每次比较都需要构建一个新对象,而Python则进行简单的整数比较。 - chepner
1
请注意,这只是某种程度上的速度比较,不是吗?现在,只要有足够的动力(双方都有),你就可以让Python和Julia以大致相同的速度运行。如果您正在学习其中一种语言,请考虑哪种语言更容易让您思考。当您真正需要时,可以稍后进行优化。 - norok2
@norok2 对于某些代码来说是正确的,但对于其他代码则不然。如果你能将Python代码转换为调用用快速语言编写的库函数,或者如果它被numba或类似的东西支持,那么也许可以,但否则Python会显著变慢。 - DNF
@DNF 在某些情况下,Python更快,而在其他情况下,Julia更快。我相信你可以找到两者的例子。仅仅因为显式循环和函数调用相对昂贵就说Python“明显”(无论这意味着什么)较慢是过于忽略整体情况了。当然,如果这是你的主要工具,那么也许你最好使用Julia。然而,如果你使用正确的工具,你可以在Python中同样快。学习这些工具值得吗?还是学习另一种语言更好呢?很难说。 - norok2
1
我同时使用这两种语言,虽然它们都有一些更快的例子,但平衡点明显偏向于一方。这只是因为Python是解释性的,并且几乎没有关注性能,而Julia则专注于性能。这实际上很像说Python和C一样快。如果没有明显的差异,那将非常奇怪,并且会削弱Julia的许多目的。 - DNF
显示剩余6条评论
3个回答

13
这种性能比较有点奇怪,因为通常人们测量计算实质内容所需的时间,而不是看在一定时间内可以执行多少次微不足道的迭代。我尝试运行您提供的Python和Julia代码时遇到了问题,所以我修改了Julia代码并使其正常工作,但没有运行Python代码。正如@chepner在评论中指出的那样,使用now()并与DateTime对象进行时间比较相当昂贵。Python的time.time()函数只返回一个浮点值。事实证明,Julia还有一个名为time()的函数,执行完全相同的操作:
julia> time()
1.587648091474481e9

这是您原始的f()函数(已修改以使其正常工作)在我的系统上的时间安排:

julia> using Dates

julia> function f()
           i = 0
           t1 = now()
           while true
               i += 1
               if now() - t1 >= Millisecond(1000)
                   break
               end
           end
           return i
       end
f (generic function with 1 method)

julia> f()
4943739

时间结束前,它进行了近500万次迭代。正如我所说,我没有办法让您的Python代码在我的系统上运行,除非进行大量调整(但我并没有费心去做)。但是这里有一个使用time()f()版本,我将其想象地称为g()

julia> function g()
           i = 0
           t1 = time()
           while true
               i += 1
               if time() - t1 >= 1
                   break
               end
           end
           return i
       end
g (generic function with 1 method)

julia> g()
36087637

这个版本循环了3600万次。所以我猜Julia在循环方面更快?耶!其实,这个循环中的主要工作是调用time(),因此...... Julia在生成大量time()调用方面更快!为什么计时奇怪?正如我所说,这里的大部分实际工作都是调用time()。循环的其余部分实际上没有做任何事情。在优化编译语言中,如果编译器看到一个不做任何事情的循环,它将完全消除它。例如:
julia> function h()
           t = 0
           for i = 1:100_000_000
               t += i
           end
           return t
       end
h (generic function with 1 method)

julia> h()
5000000050000000

julia> @time h()
  0.000000 seconds
5000000050000000    

哇,零秒!这怎么可能?好的,让我们看看LLVM代码(类似于机器码,但是用于一台虚拟机的中间表示)转换为:

julia> @code_llvm h()

;  @ REPL[16]:1 within `h'
define i64 @julia_h_293() {
top:
;  @ REPL[16]:6 within `h'
  ret i64 5000000050000000
}

编译器看到循环,发现每次结果都相同,就直接返回那个常量值,而不是真正执行循环。当然,这需要零时间。

这是编程语言的BogoMips - norok2
1
对,但 bogomips 用于衡量 CPU,而不是编程语言。但当然,这是可以衡量的事情。 - StefanKarpinski

5

您可能希望在Julia中使用time_ns函数:

function f()
    i = 0
    t1 = time_ns()
    while true
        i += 1
        if time_ns() - t1 >= 10^9
            break
        end
    end
    return i
end

在我的电脑上,这个程序运行的速度比Python快10倍。


4

嗯,在我的系统上观察到的情况与此不同:

Python 3.7.7

Python 3.7.7 (default, Mar 26 2020, 15:48:22) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import time                                                                                                                                                       

In [2]: def f(): 
   ...:     t1 = time.time() 
   ...:     i = 0 
   ...:     while True: 
   ...:         i += 1 
   ...:         if time.time() - t1 >= 1: 
   ...:             return i 
   ...:                                                                                                                                                                   

In [3]: f()                                                                                                                                                               
Out[3]: 4676268

Julia 1.4.0:

朱莉娅 1.4.0:
julia> using Dates

julia> function f()
           i = 0
           t1 = now()
           while true
               i += 1
               if now() - t1 >= Dates.Millisecond(1000)
                   break
               end
           end
           return i
       end
f (generic function with 1 method)

julia> f()
6339528

但请注意,仅使用time(即比较普通数字)仍然更快:

julia> function f()
           i = 0
           t1 = time()
           while true
               i += 1
               if time() - t1 >= 1
                   break
               end
           end
           return i
       end
f (generic function with 1 method)

julia> f()
24742703


在Python中,难道你不应该使用time.perf_counter_ns()吗? - norok2
使用time.perf_counter_ns对于这个基准测试没有改变任何东西(至少在我的系统上是这样)。我猜测,当测量时间差在1秒左右时,时间测量的精度并不重要。在这里只有获取测量和比较结果对象所需的时间(以及循环本身的效率)才是重要的。 - François Févotte
在 Julia 中,测量时间很重要 - 这就是为什么在我的代码中我使用了 time_ns 而不是 time,因为它比后者快约30%。 - Bogumił Kamiński

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