为什么计算时间差会如此之大?

4

我需要将许多UNIX时间戳舍入到它们各自的分钟数(再次表示为时间戳)。

出于好奇,我计时了两种方法:

%timeit (127/60)*60
10000000 loops, best of 3: 76.2 ns per loop

%timeit 127 - 127%60
10000000 loops, best of 3: 34.1 ns per loop

我多次运行过这两种方法,第二种方法的速度始终是第一种方法的两倍左右。为什么差距这么大?


除法比乘法、减法和取模运算要昂贵得多。 - Dan Kruchinin
1
哪个版本的Python?我想知道这里的“/”是整数除法还是浮点除法。 - Tim Peters
@TimPeters 如果两个参数都是整数,则 / 为整数除法。 - Dan Kruchinin
@TimPeters:2.7,x64,Linux - LetMeSOThat4U
3
在Python3中,即使两个操作数都是整数,“/”仍然表示浮点除法。“//”表示整数除法。@DanKruchinin的说法不正确。 - Tim Peters
@TimPeters 不知道那个,谢谢。 - Dan Kruchinin
3个回答

8
>>> import dis
>>> method1 = lambda: (127 / 60) * 60
>>> method2 = lambda: 127 - 127 % 60
>>> dis.dis(method1)
  1           0 LOAD_CONST               1 (127)
              3 LOAD_CONST               2 (60)
              6 BINARY_DIVIDE       
              7 LOAD_CONST               2 (60)
             10 BINARY_MULTIPLY     
             11 RETURN_VALUE        
>>> dis.dis(method2)
  1           0 LOAD_CONST               1 (127)
              3 LOAD_CONST               3 (7)
              6 BINARY_SUBTRACT     
              7 RETURN_VALUE        

在第二种情况下,取模运算被简单地优化掉了。

哈哈,不错 :) 我认为一个聪明的编译器会优化整个表达式,无论它是如何编写的... - Michael
@Vasiliy:啪地一声,拍了下额头。 - LetMeSOThat4U
在现实生活中,我认为值127将作为变量而不是常量传递,这种情况下m2中的优化就不存在了。 - jwygralak67

3

根据以下timeit结果,除法是与其他操作(+, -, *, %) 相比较重的操作:

In [9]: timeit 127 + 12
100000000 loops, best of 3: 14.8 ns per loop

In [10]: timeit 127 - 12
100000000 loops, best of 3: 14.8 ns per loop

In [11]: timeit 127 * 12
100000000 loops, best of 3: 14.9 ns per loop

In [12]: timeit 127 / 12
10000000 loops, best of 3: 40 ns per loop

In [13]: timeit 127 % 12
100000000 loops, best of 3: 14.7 ns per loop

更新

我错了。正如Tim Peters所评论的那样,使用变量会显示不同的结果。

In [1]: a, b = 127, 12

In [2]: timeit a + b
10000000 loops, best of 3: 37.6 ns per loop

In [3]: timeit a - b
10000000 loops, best of 3: 37.9 ns per loop

In [4]: timeit a * b
10000000 loops, best of 3: 52.7 ns per loop

In [5]: timeit a / b
10000000 loops, best of 3: 54 ns per loop

In [6]: timeit a % b
10000000 loops, best of 3: 56.5 ns per loop

2
最好使用变量,因为编译器可能会优化掉对字面量的操作。 - Tim Peters
@TimPeters,感谢您的评论。我更新了答案以包括变量版本。 - falsetru
@falsetru:不过还是有区别的,假设a=127,p=60,运行%timeit a - a%p%timeit (a/p)*p。所以Tim是对的,你也是对的。 :-) - LetMeSOThat4U
@JohnDoe,我更新了答案以包括变量版本。对不起。 - falsetru
1
听起来很正确 - 这是 intlong 在 Py3 中变成相同类型(Py2 称为 long,Py3 称为 int)的结果。而这个结果的一个后果是,Py3 的 eval 循环不再特别处理(曾经是)int。有得有失;-) - Tim Peters
显示剩余2条评论

2
这实际上是关于您特定的计算机硬件的问题,可能与流水线和ALU细节有关,以及这些循环实际上是如何本地执行的,因此我不能给出明确的答案。但我的猜测是,在这个代码块中,乘法、除法和模运算大致需要相同的时间,而且这个时间比加法或减法要长。因此,在示例1中,您有两个较慢的操作,但在示例2中只有一个。
编辑:正如falsetru在他的答案中所示,可能是除法与其他操作之间的问题。但是,再次强调,没有更多的细节,无法确定。

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