在相同的numpy
数组中,计算np.cos
需要3.2秒,而在Linux Mint上,np.sin
运行548秒(9分钟)。
有关完整代码,请参阅this repo。
我有一个脉冲信号(见下图)我需要调制到HF载波上,模拟Laser Doppler Vibrometer。因此,需要对信号及其时间基础进行重新采样,以匹配载波的更高采样率。
在以下解调过程中,需要同相载波cos(omega * t)
和相移载波sin(omega * t)
。
奇怪的是,评估这些功能的时间在很大程度上取决于时间向量的计算方式。
正在使用t1
直接计算时间向量np.linspace
,t2
使用method implemented in scipy.signal.resample
。
pulse = np.load('data/pulse.npy') # 768 samples
pulse_samples = len(pulse)
pulse_samplerate = 960 # 960 Hz
pulse_duration = pulse_samples / pulse_samplerate # here: 0.8 s
pulse_time = np.linspace(0, pulse_duration, pulse_samples,
endpoint=False)
carrier_freq = 40e6 # 40 MHz
carrier_samplerate = 100e6 # 100 MHz
carrier_samples = pulse_duration * carrier_samplerate # 80 million
t1 = np.linspace(0, pulse_duration, carrier_samples)
# method used in scipy.signal.resample
# https://github.com/scipy/scipy/blob/v0.17.0/scipy/signal/signaltools.py#L1754
t2 = np.arange(0, carrier_samples) * (pulse_time[1] - pulse_time[0]) \
* pulse_samples / float(carrier_samples) + pulse_time[0]
如下图所示,时间向量不相同。在8000万个样本中,差异t1 - t2
达到1e-8
。
在我的机器上计算t1
的同相和移位载波 3.2秒。
但是,使用t2
,计算移位的载波需要 540秒。九分钟几乎相同的8000万个值。
omega_t1 = 2 * np.pi * carrier_frequency * t1
np.cos(omega_t1) # 3.2 seconds
np.sin(omega_t1) # 3.3 seconds
omega_t2 = 2 * np.pi * carrier_frequency * t2
np.cos(omega_t2) # 3.2 seconds
np.sin(omega_t2) # 9 minutes
我可以在我的32位笔记本电脑和我的64位电脑上重现这个错误,它们都运行 Linux Mint 17 。然而,在我的平板伴侣的MacBook上,"慢速正弦"花费的时间与其他三项计算相同。
我在64位AMD处理器上运行 Linux Mint 17.03 ,在32位Intel处理器上运行 Linux Mint 17.2 。
答案 0 :(得分:18)
我不认为numpy与此有任何关系:我认为你在系统上的C数学库中遇到性能错误,这会影响接近pi的大倍数的sin。 (我正在使用" bug"在这里有一个非常广泛的意义 - 据我所知,由于大浮点数的正弦定义不明确," bug"实际上是库正确地处理极端情况!)
在linux上,我得到:
>>> %timeit -n 10000 math.sin(6e7*math.pi)
10000 loops, best of 3: 191 µs per loop
>>> %timeit -n 10000 math.sin(6e7*math.pi+0.12)
10000 loops, best of 3: 428 ns per loop
以及Python chatroom报告
中使用其他Linux的类型10000 loops, best of 3: 49.4 µs per loop
10000 loops, best of 3: 206 ns per loop
和
In [3]: %timeit -n 10000 math.sin(6e7*math.pi)
10000 loops, best of 3: 116 µs per loop
In [4]: %timeit -n 10000 math.sin(6e7*math.pi+0.12)
10000 loops, best of 3: 428 ns per loop
但Mac用户报告了
In [3]: timeit -n 10000 math.sin(6e7*math.pi)
10000 loops, best of 3: 300 ns per loop
In [4]: %timeit -n 10000 math.sin(6e7*math.pi+0.12)
10000 loops, best of 3: 361 ns per loop
没有数量级的差异。作为一种解决方法,您可以先尝试使用mod 2 pi:
>>> new = np.sin(omega_t2[-1000:] % (2*np.pi))
>>> old = np.sin(omega_t2[-1000:])
>>> abs(new - old).max()
7.83773902468434e-09
具有更好的性能:
>>> %timeit -n 1000 new = np.sin(omega_t2[-1000:] % (2*np.pi))
1000 loops, best of 3: 63.8 µs per loop
>>> %timeit -n 1000 old = np.sin(omega_t2[-1000:])
1000 loops, best of 3: 6.82 ms per loop
请注意,正如预期的那样, cos 会发生类似的效果,只是移位:
>>> %timeit -n 1000 np.cos(6e7*np.pi + np.pi/2)
1000 loops, best of 3: 37.6 µs per loop
>>> %timeit -n 1000 np.cos(6e7*np.pi + np.pi/2 + 0.12)
1000 loops, best of 3: 2.46 µs per loop
答案 1 :(得分:4)
这些巨大性能差异的一个可能原因可能是数学库如何创建或处理IEEE浮点下溢(或denorms),这可能是由于在超越函数逼近期间某些较小的尾数位的差异而产生的。并且你的t1和t2向量可能因这些较小的尾数位而不同,以及用于计算链接的任何库中的超越函数的算法,以及每个特定操作系统上的IEEE算术denorms或下溢处理程序。