varianza - Tipos de datos de Python Numpy Rendimiento
python visualizar datos (2)
La aritmética de media precisión (float16) es algo que debe ser "emulado" por el número, supongo, ya que no hay tipos correspondientes en el lenguaje C subyacente (y en las instrucciones apropiadas del procesador) para ello. Por otro lado, las operaciones de precisión simple (float32) y de precisión doble (float64) pueden realizarse de manera muy eficiente utilizando tipos de datos nativos.
A partir del buen rendimiento para operaciones de precisión simple: los procesadores modernos tienen unidades eficientes para la aritmética de punto flotante vectorizada (por ejemplo, AVX), ya que también se necesita para un buen rendimiento multimedia.
Así que hice algunas pruebas y obtuve resultados extraños.
Código:
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
¿Por qué es tan lento el float16? ¿Por qué es tan rápido float32? A menudo es más rápido que las operaciones enteras.
Si tiene algún consejo relacionado con el rendimiento, me encantaría escucharlos.
Esto es Python 2.6.6 32 bits en Windows 8 64 bits. Números para Numpy 1.6, Numpy 1.7 similares. Probaré la versión optimizada de MKL ahora: http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy
edit: resulta que la versión MKL es ligeramente más rápida en algunos casos de punto flotante pero a veces mucho más lenta para operaciones con enteros:
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
Los números de punto flotante de 16 bits no son compatibles directamente con la mayoría de las CPU comunes (aunque los proveedores de tarjetas gráficas parecen estar involucrados en este tipo de datos, por lo que espero que las GPU lo admitan eventualmente). Espero que sean emulados, de una manera relativamente lenta. Google me dice que float16 dependía del hardware y algunas personas querían emularlo para hardware que no lo admite, aunque no encontré nada sobre si eso sucedió realmente.
Por otra parte, los flotadores de 32 bits no solo son compatibles de forma nativa, también puede vectorizar muchas operaciones con las extensiones de conjuntos de instrucciones SIMD, lo que reduce drásticamente la sobrecarga para el tipo de operación que evalúa. La excepción es la mezcla de datos, pero en ese caso, float32 está a la par con int32 y ambos pueden usar las mismas instrucciones SIMD para cargar y almacenar bloques de memoria más grandes.
Si bien también hay instrucciones SIMD para matemáticas de enteros, son menos comunes (por ejemplo, SEE las introdujo en una versión posterior a las versiones flotantes) y, a menudo, menos sofisticadas. Mi conjetura es que (su compilación de) NumPy no tiene implementaciones SIMD de las operaciones que son más lentas para usted. Alternativamente, las operaciones de enteros pueden no estar tan optimizadas: los flotadores se usan en muchas aplicaciones fáciles de vectorizar cuyo rendimiento importa mucho (por ejemplo, en / decodificación / imagen / medios de video), por lo que pueden estar más optimizados.