max*_*max 47 python floating-point performance numpy
我为以下代码得到了非常奇怪的时间:
import numpy as np
s = 0
for i in range(10000000):
s += np.float64(1) # replace with np.float32 and built-in float
Run Code Online (Sandbox Code Playgroud)
为什么float64
比两倍慢float
?为什么float32
比float64慢5倍?
有没有办法避免使用的惩罚np.float64
,并有numpy
功能返回内置float
而不是float64
?
我发现使用numpy.float64
比Python的浮点慢得多,numpy.float32
甚至更慢(即使我在32位机器上).
numpy.float32
在我的32位机器上.因此,每次我使用各种numpy函数时numpy.random.uniform
,我将结果转换为float32
(以便以32位精度执行进一步的操作).
有没有办法在程序或命令行中的某处设置单个变量,并使所有numpy函数返回float32
而不是float64
?
编辑#1:
在算术计算中,numpy.float64 比浮点慢10倍.它非常糟糕,甚至在计算之前转换为浮动和返回使程序运行速度提高了3倍.为什么?我能做些什么来解决它吗?
我想强调一下,我的时间安排不是由以下任何原因引起的:
我更新了我的代码,以便更清楚地解决问题所在.使用新代码,我发现使用numpy数据类型可以看到十倍的性能:
from datetime import datetime
import numpy as np
START_TIME = datetime.now()
# one of the following lines is uncommented before execution
#s = np.float64(1)
#s = np.float32(1)
#s = 1.0
for i in range(10000000):
s = (s + 8) * s % 2399232
print(s)
print('Runtime:', datetime.now() - START_TIME)
Run Code Online (Sandbox Code Playgroud)
时间是:
只是为了它的地狱,我也尝试过:
从datetime import datetime import numpy as np
START_TIME = datetime.now()
s = np.float64(1)
for i in range(10000000):
s = float(s)
s = (s + 8) * s % 2399232
s = np.float64(s)
print(s)
print('Runtime:', datetime.now() - START_TIME)
Run Code Online (Sandbox Code Playgroud)
执行时间为13.28秒; 实际上,转换float64
为float
和返回的速度比使用它快3倍.尽管如此,转换还是要付出代价,所以总体来说它比纯蟒蛇慢了3倍多float
.
我的机器是:
编辑#2:
谢谢你的答案,他们帮助我了解如何处理这个问题.
但我还是想知道确切原因(基于源代码的可能)为什么下面的代码运行速度慢了10倍float64
比float
.
编辑#3:
我重新运行Windows 7 x64(Intel Core i7 930 @ 3.8GHz)下的代码.
同样,代码是:
from datetime import datetime
import numpy as np
START_TIME = datetime.now()
# one of the following lines is uncommented before execution
#s = np.float64(1)
#s = np.float32(1)
#s = 1.0
for i in range(10000000):
s = (s + 8) * s % 2399232
print(s)
print('Runtime:', datetime.now() - START_TIME)
Run Code Online (Sandbox Code Playgroud)
时间是:
现在两个np
浮点数(64或32)都比内置浮点慢5倍float
.仍然是一个显着的差异.我想弄清楚它来自哪里.
结束编辑
sam*_*ias 44
CPython浮点数以块的形式分配
将numpy标量分配与float
类型进行比较的关键问题是CPython总是为大小为N的块分配内存float
和int
对象.
在内部,CPython维护一个块的链接列表,每个块都足够容纳N个float
对象.当您调用float(1)
CPython检查当前块中是否有可用空间时; 如果没有,它会分配一个新块.一旦它在当前块中有空间,它只需初始化该空间并返回指向它的指针.
在我的机器上,每个块可以容纳41个float
对象,因此第一次float(1)
调用会有一些开销,但是随着内存的分配和准备,接下来的40个运行速度会快得多.
慢numpy.float32与numpy.float64
看起来numpy在创建标量类型时可以采用2条路径:快速和慢速.这取决于标量类型是否具有可以推迟参数转换的Python基类.
由于某种原因numpy.float32
,硬编码采用较慢的路径(由_WORK0
宏定义),同时numpy.float64
有机会采用更快的路径(由_WORK1
宏定义).请注意,这scalartypes.c.src
是scalartypes.c
在构建时生成的模板.
您可以在Cachegrind中将其可视化.我已经包括截屏显示更多的电话是由多少构建float32
与float64
:
float64
采取快速的道路
float32
走慢路
更新 - 慢速/快速路径采用哪种类型可能取决于操作系统是32位还是64位.在我的测试系统上,Ubuntu Lucid 64位,float64
类型比它快10倍float32
.
Ros*_*ron 22
像这样的繁重循环中的Python对象操作,无论它们是否float
,np.float32
总是很慢.NumPy对于向量和矩阵的操作非常快,因为所有操作都是由用C语言编写的库的大部分数据执行的,而不是由Python解释器执行的.在解释器中运行的代码和/或使用Python对象总是很慢,并且使用非本机类型会使它更慢.这是可以预料的.
如果您的应用程序运行缓慢且需要对其进行优化,则应尝试将代码转换为直接使用NumPy的矢量解决方案,并且速度快,或者您可以使用Cython等工具在C中创建循环的快速实现.
riz*_*iza 10
也许,这就是为什么你应该直接使用Numpy而不是使用循环.
s1 = np.ones(10000000, dtype=np.float)
s2 = np.ones(10000000, dtype=np.float32)
s3 = np.ones(10000000, dtype=np.float64)
np.sum(s1) <-- 17.3 ms
np.sum(s2) <-- 15.8 ms
np.sum(s3) <-- 17.3 ms
Run Code Online (Sandbox Code Playgroud)
答案很简单:内存分配可能是其中的一部分,但最大的问题是numpy标量的算术运算是使用"ufuncs"来完成的,这对于数百个值不仅仅是1而言是快速的.有一些开销选择正确的函数来调用和设置循环.标头不需要的开销.
将标量转换为0-d数组然后传递给相应的numpy ufunc然后为NumPy支持的许多不同标量类型中的每一个编写单独的计算方法更容易.
意图是标量数学的优化版本将被添加到C中的类型对象.这可能仍然会发生,但它从未发生过,因为没有人有足够的动机去做.可能是因为解决方法是将numpy标量转换为具有优化算术的Python标量.
摘要
如果算术表达式包含numpy
内置数字和内置数字,则Python算术工作速度较慢.避免这种转换几乎消除了我报告的所有性能下降.
细节
请注意,在我的原始代码中:
s = np.float64(1)
for i in range(10000000):
s = (s + 8) * s % 2399232
Run Code Online (Sandbox Code Playgroud)
类型float
和numpy.float64
混合在一个表达式中.也许Python必须将它们全部转换为一种类型?
s = np.float64(1)
for i in range(10000000):
s = (s + np.float64(8)) * s % np.float64(2399232)
Run Code Online (Sandbox Code Playgroud)
如果运行时没有改变(而不是增加),那就表明Python确实在做什么,解释了性能拖累.
实际上,运行时间下降了1.5倍!这怎么可能?Python可能不得不做的最糟糕的事情是这两次转换?
我真的不知道.也许Python必须动态地检查需要转换成什么,这需要花费时间,并被告知要执行的精确转换会使它更快.也许,一些完全不同的机制被用于算术(它根本不涉及转换),并且在不匹配的类型上恰好是超慢的.阅读numpy
源代码可能会有所帮助,但这超出了我的技能范围.
无论如何,现在我们可以通过将转换移出循环来显然加快速度:
q = np.float64(8)
r = np.float64(2399232)
for i in range(10000000):
s = (s + q) * s % r
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,运行时间大幅减少:再增加2.3倍.
公平地说,我们现在需要float
通过将文字常量移出循环来稍微改变版本.这导致微小(10%)减速.
考虑到所有这些变化,np.float64
代码的版本现在只比同等float
版本慢30%; 这种荒谬的5倍性能打击基本消失了.
为什么我们仍然看到30%的延迟?numpy.float64
数字占用的空间相同float
,所以这不是原因.对于用户定义的类型,算术运算符的分辨率可能更长.当然不是主要问题.