当前位置: 首页 > 知识库问答 >
问题:

为什么在Python 3中X**4.0比X**4快?

吕承福
2023-03-14

为什么X**4.0X**4快?我使用的是CPython 3.5.2。

$ python -m timeit "for x in range(100):" " x**4.0"
  10000 loops, best of 3: 24.2 usec per loop

$ python -m timeit "for x in range(100):" " x**4"
  10000 loops, best of 3: 30.6 usec per loop

我试着改变我提升的幂,看看它是怎么做的,例如,如果我提升x的10或16的幂,它会从30跳到35,但如果我提升10.0作为浮动,它只是在24.1~4左右移动。

我想这和浮点转换和2次方有关,但我真的不知道。

共有1个答案

钱嘉致
2023-03-14

为什么在Python 3*X**4.0X**4快?

Python 3int对象是一个完整的对象,旨在支持任意大小;由于这一事实,它们在C级上是这样处理的(请参见如何在long_pow中将所有变量声明为PylongObject*类型)。这也使得它们的幂运算变得更加棘手和乏味,因为您需要使用它用来表示其值的ob_digit数组来执行它。(勇敢者的源代码。--有关PylongObject的更多信息,请参见:了解Python中大整数的内存分配。)

相反,Pythonfloat对象可以转换为Cdouble类型(通过使用pyfloat_asdouble),并且可以使用这些本机类型执行操作。这很棒,因为在检查相关的边缘情况之后,它允许Python使用平台的POW(即C的POW)来处理实际的幂运算:

/* Now iv and iw are finite, iw is nonzero, and iv is
 * positive and not equal to 1.0.  We finally allow
 * the platform pow to step in and do the rest.
 */
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw); 

其中IVIW是我们的原始PyFloatObject作为CDouble

值得注意的是:Python2.7.13对我来说是一个因素2~3更快,并且显示了相反的行为。

前面的事实也解释了Python 2和3之间的差异,所以我想我也要解决这个注释,因为它很有趣。

# Python 2
type(30)  # <type 'int'>
type(30L) # <type 'long'>
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
    register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */    

这允许一个很好的速度增益。

为了查看 相比有多慢,如果您在Python 2中的long调用中包装x名称(实际上强制它使用long_pow,就像Python 3中的那样),速度增益将消失:

# <type 'int'>
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"       
10000 loops, best of 3: 116 usec per loop
# <type 'long'> 
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop

请注意,尽管其中一个代码段将int转换为long,而另一个代码段不转换(正如@pydsinger指出的那样),但这种强制转换并不是导致减速的原因。long_pow的实现是。(仅用long(x)对语句计时以查看)。

[...]它不会发生在循环之外。[...]对此有什么想法吗?

这是cpython的窥视孔优化器为您折叠常量。在这两种情况下,您都得到了相同的精确时间,因为没有实际的计算来找到幂的结果,只有加载值:

dis.dis(compile('4 ** 4', '', 'exec'))
  1           0 LOAD_CONST               2 (256)
              3 POP_TOP
              4 LOAD_CONST               1 (None)
              7 RETURN_VALUE

'4**4'生成相同的字节码。,唯一的区别是load_const加载float256.0而不是int256:

dis.dis(compile('4 ** 4.', '', 'exec'))
  1           0 LOAD_CONST               3 (256.0)
              2 POP_TOP
              4 LOAD_CONST               2 (None)
              6 RETURN_VALUE
 类似资料:
  • 以下Python3.x整数乘法的平均运算时间在1.66s到1.77s之间: 如果将替换为,则需要在和之间。怎么会呢? 另一方面,在Java中则相反:在Java中更快。Java测试链接:为什么在Java中2*(i*i)比2*i*i快? 我运行每个版本的程序10次,以下是结果。

  • 基于此,我认为我应该完全开始在中使用

  • 问题内容: 我在计算机上得到以下结果: 我认为这可能与int / long转换有关,但在2.7中并没有更快的速度。 问题答案: Python 2使用朴素的阶乘算法: Python 3使用分治法阶乘算法: 有关讨论,请参见Python Bugtracker问题。感谢DSM指出这一点。

  • 问题内容: 考虑以下示例: 我不确定Java语言规范中是否有一项规定要加载变量的先前值以便与右侧()进行比较,该变量应按照方括号内的顺序进行计算。 为什么第一个表达式求值,而第二个表达式求值?我本来希望先被评估,然后再与自身()比较并返回。 这个问题与Java表达式中子表达式的求值顺序不同,因为这里绝对不是“子表达式”。需要 加载 它以进行比较,而不是对其进行“评估”。这个问题是特定于Java的,

  • 这个问题与Java表达式中子表达式的求值顺序不同,因为在这里肯定不是“子表达式”。需要加载它进行比较,而不是“求值”。这个问题是特定于Java的,表达式来自一个真实的项目,而不是通常为棘手的面试问题而设计的牵强附会的不切实际的构造。它应该是比较和替换习语的一行替换 它比x86 CMPXCHG指令还要简单,因此在Java中应该使用更短的表达式。

  • 问题内容: 今天,我正在为即将参加的Java考试而学习,但遇到了这个问题: 设一个定义如下的类: 该指令产生的输出是什么? 答案似乎是,但我不明白为什么。有人可以给我适当的解释吗? 问题答案: 与具有可变参数列表的函数相比,重载解析始终偏向于使用具有明显数量的参数的函数,即使这意味着该函数是自动装箱的。 更详细地说,根据JLS 15.12.2 选择具有此优先级的函数: 类型加宽 自动装箱 可变参数