为什么x**4.0比x**4快?我正在使用CPython 3.5.2。
1 2 3 4 5
| $ 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的幂有关,但我不知道。
我注意到,在这两种情况下,2的能力都更快,我想,因为这些计算对于口译员/计算机来说更为原始/简单。不过,有了漂浮物,它几乎不动了。2.0 => 24.1~4 & 128.0 => 24.1~4,2 => 29 & 128 => 62。
Tigerhawkt3指出它不会发生在循环之外。我检查过了,情况只在基地被提升时发生(从我所见)。你知道吗?
- 值得一提的是:对于我来说,python 2.7.13是一个更快的系数2~3,并且显示了相反的行为:整数指数比浮点指数更快。
- @每次都是这样,我得到14个用于x**4.0和3.9用于x**4。
Why is x**4.0 faster than x**4 in Python 3*?
号
python 3 int对象是一个完全成熟的对象,设计用于支持任意大小;因此,它们在C级别上是这样处理的(参见如何在long_pow中将所有变量声明为PyLongObject *类型)。这也使得它们的求幂更加复杂和乏味,因为您需要使用它用来表示其执行值的ob_digit数组。(勇敢者的来源。--有关PyLongObjects)的更多信息,请参见:了解python中大整数的内存分配。)
相反,python float对象可以转换为c double类型(使用PyFloat_AsDouble类型),并且可以使用这些本地类型执行操作。这很好,因为在检查了相关的边缘情况后,它允许python使用平台的pow(c的pow来处理实际的求幂:
1 2 3 4 5 6 7
| /* 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); |
。
其中,iv和iw是我们原来的PyFloatObjects,即c doubles。
For what it's worth: Python 2.7.13 for me is a factor 2~3 faster, and shows the inverse behaviour.
号
前面的事实也解释了python 2和3之间的差异,所以,我想我也会讨论这个注释,因为它很有趣。
在python 2中,您使用的是与python 3中的int对象不同的旧int对象(3.x中的所有int对象都是PyLongObject类型)。在python 2中,有一个区别取决于对象的值(或者,如果使用后缀L/l):
1 2 3
| # Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'> |
你在这里看到的和float做的一样,当对它执行求幂时,它可以安全地转换为c long(int_pow还提示编译器如果可以的话,将它们放入寄存器中,这样做可能会有所不同):
1 2 3 4 5
| static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */ |
。
这样可以获得良好的速度增益。
为了了解与s相比,s有多慢,如果在python 2中用long调用(本质上强制它使用long_pow与python 3中一样)包装x名称,则速度增益将消失:
1 2 3 4 5 6
| # <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)一起计时报表)。
[...] it doesn't happen outside of the loop. [...] Any idea about that?
号
这是CPython的窥视孔优化器,为您折叠常数。无论哪种情况,您都会得到相同的精确计时,因为没有实际计算来查找求幂结果,只加载值:
1 2 3 4 5
| 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加载的是float 256.0,而不是int 256:
1 2 3 4 5
| dis.dis(compile('4 ** 4.', '', 'exec'))
1 0 LOAD_CONST 3 (256.0)
2 POP_TOP
4 LOAD_CONST 2 (None)
6 RETURN_VALUE |
。
所以时间是相同的。
*以上所有内容都只适用于Python的参考实现cpython。其他实现的执行方式可能不同。
- 不管是什么,它与range上的环流有关,因为时机仅仅是EDOCX1〕〔8〕操作本身的产量没有区别。
- 不同之处在于看着一个变量(EDOCX1〕〔3〕就像EDOCX1〕〔4〕一样迅速,而这个答案并没有影响到这一点。
- 但是,君士坦丁斯会被折叠@tigerhawkt3(EDOCX1&11),所以时间应该完全一样。
- 你最后的时机似乎不想让我知道你在说什么。通过一个4-5因子,仍然比long(x)**2快。(Not one of the downvoters,though)
- @Tigerhawkt3@Jimfasarakis-Hilliard The question began on the simple question,without noticing that the effect is only happening in a loop(and when the index is involved in the pow operation-i**2or 2**i但现在,虽然在《底层行为守则》中有一个不同之处,但它似乎并没有对《底层行为守则》中的一项指数产生任何影响。那么我们怎么从这里继续?问题改变了,答案是好的,但对开始的问题来说是好的。我想听听你关于如何继续处理这个问题的建议,看看这个社区的标准。
- @Grapher,但这是我的观点。在Python2中,显示的速度和它们在Python 3中所做的一样,因为它们的方式相同。在Python2(在Python3中不存在)的另一只手臂上,的物体像以前被叫做"快车道"的人一样。
- @Arieljannai outside a loop the values get folded by the interpreter so the operations are exactly the same,there's no additional computation required and no speed difference displayed.你在索引上跑步是什么意思?如果你谈论的是除pow之外的其他操作,那么,从他们实施不同的操作以来,这些操作将显示其他行为。你能用这个答案来描述你仍然有什么问题吗?
- @Jimfasarakis-Hilliard哦!我想念的那一部分是那个翻译在处理它。如果我只是运行EDOCX1〕〔22〕或2**17.0的话,我的指数是什么意思?如果我使用的是环形变量(用于i〔1〕〔25〕将比i**17.0慢得多。但我相信这仍然是同一个原因,因为它是一个常数的计算,解释者的行为,就像它是在环路之外,并称之为许多时候。
- 所以,当它是一个常量计算时,它们会被解释器折叠(不管是否在循环中),但是当涉及到一个正在运行的索引时——它会在循环中被计算并转换为您所显示的内容。谢谢!
- 那么,如果python 3具有负的速度影响,并且不能再使用本机类型进行整数操作,那么为什么要进行这种更改呢?
- @mbomb007在python 3中消除类型的原因可能是为简化语言所做的努力。如果您可以有一个类型来表示整数,那么它比两个更易于管理(并且在必要时担心从一个类型转换到另一个类型,用户会感到困惑等等)。速度增益是次要的。政治公众人物237的基本原理部分也提供了更多的见解。
- 我想说的是,测试long(x) ** n和x ** n的python 2速度有点离谱,因为您显式地将intx转换为一个长的。我很想看看4L ** n和4 ** n的速度比较。
- @Pydsigner抓到了!你的确是对的,我没有想到。我不想偏离他问题中使用的测试,所以我将继续说明long(int_object)的速度有多快。
如果我们看字节码,我们可以看到表达式是完全相同的。唯一的区别是常量的类型,它将是BINARY_POWER的参数。因此,最明显的原因是int被转换成了一个下线的浮点数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| >>> def func(n):
... return n**4
...
>>> def func1(n):
... return n**4.0
...
>>> from dis import dis
>>> dis(func)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (4)
6 BINARY_POWER
7 RETURN_VALUE
>>> dis(func1)
2 0 LOAD_FAST 0 (n)
3 LOAD_CONST 1 (4.0)
6 BINARY_POWER
7 RETURN_VALUE |
更新:让我们来看一下cpython源代码中的objects/abstract.c:
1 2 3 4 5
| PyObject *
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
{
return ternary_op(v, w, z, NB_SLOT(nb_power),"** or pow()");
} |
号
PyNumber_Power调用ternary_op,它太长了,无法粘贴到这里,所以这里是链接。
它称为x的nb_power槽,把y作为论据。
最后,在objects/floatobject.c第686行的float_pow()中,我们看到参数在实际操作之前转换为c double:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
double iv, iw, ix;
int negate_result = 0;
if ((PyObject *)z != Py_None) {
PyErr_SetString(PyExc_TypeError,"pow() 3rd argument not"
"allowed unless all arguments are integers");
return NULL;
}
CONVERT_TO_DOUBLE(v, iv);
CONVERT_TO_DOUBLE(w, iw);
... |
- 为什么下载?转换/变量式检查似乎是这里的问题。对本案而言,文献与12.0**40.0和12**40之间没有速率上的差异。
- @Jean-Fran?an&231;oisfabre,我相信这是因为不断的折磨。
- 我认为,这意味着有一种转换,而他们却没有把握"最确切地"线下方的不同,这是一个没有任何来源的伸缩比特。
- 我也认为这是一个方法,但却找不到任何线索
- 从关于二进制 power的文献来看,这也不是一个很好的提示性主体,而是从堆栈和堆栈中提取的。
- @Mitch-Specially since,in this particular code,there's no difference in the execution time for those two operations.不同之处在于作战圈。这是一个很难得出结论的答案。
- 你为什么只看float_pow〔When that doesn't even run for the slow case?
- 不同之处在于看着一个变量(EDOCX1〕〔3〕就像EDOCX1〕〔4〕一样迅速,而这个答案并没有影响到这一点。
- 【参考译文】Get constant-folded这是完全分离的效果。
- 你能不能换一条线"它最确切的原因是一个内部转换到一个浮点下面的线路"?Would you change the line"it's most certainly because of an int converted to a floating point down the line"?尽管最初的警告是有效的,但这并不是根源(一般来说,改革你的答案,包括最新的优惠。)
因为一个是正确的,另一个是近似值。
1 2 3 4 5 6
| >>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625 |
- 投反对票的人,想解释一下为什么吗?
- 我不知道为什么那个投反对票的人投反对票,但我投了,因为这个答案不能回答问题。仅仅因为某件事是正确的,并不意味着它更快或更慢。一个比另一个慢,因为一个可以使用C类型,而另一个必须使用Python对象。
- 谢谢你的解释。好吧,我真的认为很明显,仅仅计算一个数字到12位左右的近似值,比精确计算所有这些数字要快。毕竟,我们使用近似的唯一原因是它们的计算速度更快,对吧?