2013-03-11 39 views
19

所以我做了一些测试并得到了奇怪的结果。Python Numpy数据类型性能

代码:

import numpy as np 
import timeit 


setup = """ 
import numpy as np 
A = np.ones((1000,1000,3), dtype=datatype) 
""" 

datatypes = "np.uint8", "np.uint16", "np.uint32", "np.uint64", "np.float16", "np.float32", "np.float64" 

stmt1 = """ 
A = A * 255 
A = A/255 
A = A - 1 
A = A + 1 
""" 
#~ np.uint8 : 1.04969205993 
#~ np.uint16 : 1.19391073202 
#~ np.uint32 : 1.37279821351 
#~ np.uint64 : 2.99286961148 
#~ np.float16 : 9.62375889588 
#~ np.float32 : 0.884994368045 
#~ np.float64 : 0.920502625252 

stmt2 = """ 
A *= 255 
A /= 255 
A -= 1 
A += 1 
""" 
#~ np.uint8 : 0.959514497259 
#~ np.uint16 : 0.988570167659 
#~ np.uint32 : 0.963571471946 
#~ np.uint64 : 2.07768933333 
#~ np.float16 : 9.40085450056 
#~ np.float32 : 0.882363984225 
#~ np.float64 : 0.910147440048 

stmt3 = """ 
A = A * 255/255 - 1 + 1 
""" 
#~ np.uint8 : 1.05919667881 
#~ np.uint16 : 1.20249978404 
#~ np.uint32 : 1.58037744789 
#~ np.uint64 : 3.47520357571 
#~ np.float16 : 10.4792515701 
#~ np.float32 : 1.29654744484 
#~ np.float64 : 1.80735079168 

stmt4 = """ 
A[:,:,:2] *= A[:,:,:2] 
""" 
#~ np.uint8 : 1.23270964172 
#~ np.uint16 : 1.3260807837 
#~ np.uint32 : 1.32571002402 
#~ np.uint64 : 1.76836543305 
#~ np.float16 : 2.83364821535 
#~ np.float32 : 1.31282323872 
#~ np.float64 : 1.44151875479 

stmt5 = """ 
A[:,:,:2] = A[:,:,:2] * A[:,:,:2] 
""" 
#~ np.uint8 : 1.38166223494 
#~ np.uint16 : 1.49569114821 
#~ np.uint32 : 1.53105315419 
#~ np.uint64 : 2.03457943366 
#~ np.float16 : 3.01117795524 
#~ np.float32 : 1.51807271679 
#~ np.float64 : 1.7164808877 

stmt6 = """ 
A *= 4 
A /= 4 
""" 
#~ np.uint8 : 0.698176392658 
#~ np.uint16 : 0.709560468038 
#~ np.uint32 : 0.701653066443 
#~ np.uint64 : 1.64199069295 
#~ np.float16 : 4.86752675499 
#~ np.float32 : 0.421001675475 
#~ np.float64 : 0.433056710408 

stmt7 = """ 
np.left_shift(A, 2, A) 
np.right_shift(A, 2, A) 
""" 
#~ np.uint8 : 0.381521115341 
#~ np.uint16 : 0.383545967785 
#~ np.uint32 : 0.386147272415 
#~ np.uint64 : 0.665969478824 


for stmt in [stmt1, stmt2, stmt3, stmt4, stmt5, stmt6, stmt7]: 
    print stmt 
    for d in datatypes: 
     s = setup.replace("datatype", d) 
     T = timeit.Timer(stmt=stmt, setup=s) 
     print d,":", min(T.repeat(number=30)) 
    print 
print 

为什么float16这么慢? 为什么float32这么快?它通常比整数运算更快。

如果您有任何相关的性能提示,我会很高兴听到他们。

这是Windows 8 64位上的python 2.6.6 32bit。 数字为Numpy 1.6,Numpy 1.7相似。现在将测试MKL优化版本:http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy

编辑:原来的MKL版本稍快一些浮点情况下,但有时很多慢整数OPS:

stmt2 = """ 
A *= 255 
A /= 255 
A -= 1 
A += 1 
""" 
#np1.6 
#~ np.uint8 : 0.959514497259 
#~ np.uint16 : 0.988570167659 
#~ np.uint32 : 0.963571471946 
#~ np.uint64 : 2.07768933333 
#~ np.float16 : 9.40085450056 
#~ np.float32 : 0.882363984225 
#~ np.float64 : 0.910147440048 

# np1.7 
#~ np.uint8 : 0.979 
#~ np.uint16 : 1.010 
#~ np.uint32 : 0.972 
#~ np.uint64 : 2.081 
#~ np.float16 : 9.362 
#~ np.float32 : 0.882 
#~ np.float64 : 0.918 

# np1.7 mkl 
#~ np.uint8 : 1.782 
#~ np.uint16 : 1.145 
#~ np.uint32 : 1.265 
#~ np.uint64 : 2.088 
#~ np.float16 : 9.029 
#~ np.float32 : 0.800 
#~ np.float64 : 0.866 
+1

https://en.wikipedia.org/wiki/Half-precision_floating-point_format“它用于存储,而不是用于执行算术计算。” – endolith 2016-04-05 21:39:41

回答

17

半精度运算(float16)是什么必须由numpy“模拟”,因为底层C语言(以及相应的处理器指令)中没有相应的类型。另一方面,使用原生数据类型可以非常高效地完成单精度(float32)和双精度(float64)操作。

由于单精度操作的良好性能:现代处理器具有用于向量化浮点算术(例如AVX)的高效单元,因为它也是良好的多媒体性能所必需的。

8

16位浮点数不直接得到大多数常用CPU的支持(虽然显卡厂商显然参与了这种数据类型,所以我希望GPU最终支持它)。我希望他们能够以相对缓慢的方式效仿。谷歌告诉我,float16 was once hardware-dependent和一些人想模仿它不支持它的硬件,但我没有发现是否真的发生了什么。另一方面,32位浮点数不仅可以本地支持,还可以使用SIMD指令集扩展向量化矢量化它们的许多操作,从而大大降低了基准操作类型的开销。这个例外是对数据进行混洗,但是在这种情况下,float32与int32一样,都可以使用相同的SIMD指令来加载和存储更大的内存块。

虽然也有整数数学的SIMD指令,但它们不太常见(例如,SEE在比浮点版本更高的版本中引入了它们)并且通常较不复杂。我的猜测是(你的版本)NumPy没有SIMD实现对你来说比较慢的操作。或者,整数运算可能未被优化:浮点用于许多易于向量化的应用程序,这些应用程序的性能很重要(例如,图像/媒体/视频编码和解码),因此它们可能会更加优化。