首页 > 解决方案 > 混合内置 Python 数值类型和 Numpy 标量数值数据类型的计算性能

问题描述

我正在为存储在大型文本文件(1Gb+)中的一些数据编写解析器

输出是一个 numpy 复数数组,所以我很自然地使用了类似的语句

vals[...] = real_part + 1j*imag_part

从哪里real_part获得imag_partnumpy.fromstring(...)

我注意到,如果我简单地替换vals[...] = real_part + 1j*imag_part

vals[...] = 1j*imag_part + real_part

我几乎获得了 x2 的性能提升,这对于大型数据集来说意义重大。

我做了一些测试,得到了令人困惑的结果:

代码:

import timeit
import numpy as np

a = np.float64(1.0)
print('type of 1j*a+a is',type(1j*a+a))
print('type of a+1j*a is',type(a+1j*a))
print('type of a+a*1j is',type(a+a*1j))

setup_line = 'import numpy as np; b = np.zeros(1,dtype=complex)'
N = 1000000
t1 = timeit.timeit("a=np.fromstring('1.1 2.2',sep=' ',dtype=float); b[0]=1j*a[1]+a[0]", setup=setup_line, number=N)
t2 = timeit.timeit("a=np.fromstring('1.1 2.2',sep=' ',dtype=float); b[0]=a[0]+1j*a[1]", setup=setup_line, number=N)
t3 = timeit.timeit("a=np.fromstring('1.1 2.2',sep=' ',dtype=float); b[0]=a[0]+a[1]*1j", setup=setup_line, number=N)

print(f't2/t1 = {t2/t1}')
print(f't3/t1 = {t3/t1}')

print(f'type of 1.0*a is {type(1.0*a)}')
print(f'type of 1.0.__mul__(a) is {type((1.0).__mul__(a))}')
print(f'type of a.__rmul__(1.0) is {type(a.__rmul__(1.0))}')
print(f'type of a*1.0 is {type(a*1.0)}')


print(f'type of 1j*a is {type(1j*a)}')
print(f'type of a*1j is {type(a*1j)}')

输出:

type of 1j*a+a is <class 'complex'>
type of a+1j*a is <class 'numpy.complex128'>
type of a+a*1j is <class 'numpy.complex128'>
t2/t1 = 2.720535618997823
t3/t1 = 3.9634173211365487
type of 1.0*a is <class 'numpy.float64'>
type of 1.0.__mul__(a) is <class 'float'>
type of a.__rmul__(1.0) is <class 'numpy.float64'>
type of a*1.0 is <class 'numpy.float64'>
type of 1j*a is <class 'complex'>
type of a*1j is <class 'numpy.complex128'>

所以在第一种情况下性能更好,因为所有计算都在 Python 内置complex类中执行。性能提升也接近于逐行解析的实际情况。

更令人困惑的是为什么 type of1.0*a不等于(1.0).__mul__(a)但等于a.__rmul__(1.0)?是应该的样子吗?

1.0*a和有什么区别1j*a

标签: pythonnumpy

解决方案


我怀疑“原始”时间会告诉我们比比率更多的信息:

In [182]: timeit 1j*a+a
209 ns ± 12.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [183]: timeit a+1j*a
5.26 µs ± 11.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [184]: timeit a+a*1j
10 µs ± 9.28 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

通过像这样的单值计算,函数调用和与 numpy 的转换可以主导时代。我们没有看到“纯”计算时间。为此,我们需要使用更大的数组。

例如,众所周知,这math.sinnp.sin单个值要好。np.sin具有 100 个值的速度更快。我怀疑这里正在发生这样的事情,但现在没有时间去探索它。


推荐阅读