calcul du sinus ordres de grandeur plus lents que le cosinus

tl;dr

De la même numpy array, calcul np.cos prend 3.2 secondes, wheras np.sin fonctionne 548 secondes (neuf minutes) sur Linux Mint.

Voir ce repo pour le code complet.


j'ai un signal d'impulsion (voir image ci-dessous) que je dois moduler sur une porteuse HF, simulant un Vibromètre Laser Doppler. Par conséquent signal et sa base de temps doivent être rééchantillonnés pour correspondre le transporteur du taux d'échantillonnage plus élevé.

pulse signal to be modulated onto HF-carrier

dans le processus de démodulation suivant les deux le porteur en phase cos(omega * t) et la phase décalée transporteur sin(omega * t) sont nécessaires. Curieusement, le temps d'évaluer ces fonctions dépend fortement de la façon dont le vecteur du temps a été calculé.

le vecteur de temps t1 est calculé en utilisant np.linspace directement t2 utilise méthode mise en œuvre dans 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]

comme on peut le voir sur la photo ci-dessous, les vecteurs temporels ne sont pas identiques. A 80 millions d'échantillons, la différence est t1 - t2 atteint 1e-8.

difference between time vectors <codet1</code and <codet2</code

Calcul de la phase de décalé et de transporteur de t1 faut 3.2 secondes chacun sur ma machine.

t2 toutefois, le calcul de l'décalé transporteur prend 540 secondes. Neuf minutes. Pour presque les mêmes 80 millions de valeurs.

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

je peux reproduire ce bug à la fois sur mon ordinateur portable 32 bits et sur ma tour 64 bits, les deux fonctionnant Linux Mint 17. Sur le MacBook de mon colocataire, cependant, le" slow sine " prend aussi peu de temps que les trois autres calculs.


je lance un Linux Mint 17.03 sur un processeur AMD 64 bits et Linux Mint 17.2 sur processeur Intel 32 bits.

25
demandé sur Finwood 2016-03-05 17:06:14
la source

2 ответов

Je ne pense pas que numpy ait quoi que ce soit à voir avec ça: je pense que vous êtes en train de trébucher sur un bug de performance dans la bibliothèque C math de votre système, un bug qui affecte le sin près de grands multiples de pi. (J'utilise "bug "dans un sens assez large ici -- pour autant que je sache, puisque le sinus des grands flotteurs est mal défini, le" bug " est en fait la bibliothèque qui se comporte correctement pour gérer les cas de coin!)

sur linux, je reçois:

>>> %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

et D'autres types D'utilisateurs de Linux Python tchat rapport

10000 loops, best of 3: 49.4 µs per loop 
10000 loops, best of 3: 206 ns per loop

et

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

mais un utilisateur Mac a signalé

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

pour aucune différence d'ordre de grandeur. Comme solution de contournement, vous pourriez essayer de prendre choses mod 2 pi d'abord:

>>> new = np.sin(omega_t2[-1000:] % (2*np.pi))
>>> old = np.sin(omega_t2[-1000:])
>>> abs(new - old).max()
7.83773902468434e-09

qui a de meilleures performances:

>>> %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

Notez que, comme prévu, un effet similaire se produit pour cos, juste modifié:

>>> %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
17
répondu DSM 2016-03-05 22:25:36
la source

une cause possible de ces énormes différences de performance pourrait être dans la façon dont la bibliothèque de mathématiques crée ou manipule le sous-flux de point flottant IEEE (ou denorms), qui pourrait être produit par une différence de certains bits de mantissa tinier pendant l'approximation de la fonction transcendantale. Et vos vecteurs t1 et t2 peuvent différer par ces petits bits de mantissa, aussi bien que l'algorithme utilisé pour calculer la fonction transcendantale dans n'importe quelle bibliothèque que vous avez liée, aussi bien que les dénormes arithmétiques de L'IEEE ou un gestionnaire de sous-flux sur chaque OS particulier.

4
répondu hotpaw2 2016-03-06 09:43:04
la source

Autres questions sur