Je regardais la source de conteneurs_triés et a été surpris de voir cette ligne :
self._load, self._twice, self._half = load, load * 2, load >> 1
Aquí load
est un nombre entier. Pourquoi utiliser le décalage de bits à un endroit, et la multiplication à un autre ? Il semble raisonnable que le décalage de bits puisse être plus rapide que la division intégrale par 2, mais pourquoi ne pas remplacer la multiplication par un décalage également ? J'ai testé les cas suivants :
- (fois, diviser)
- (shift, shift)
- (temps, décalage)
- (shift, divide)
et a constaté que le n°3 est toujours plus rapide que les autres alternatives :
# self._load, self._twice, self._half = load, load * 2, load >> 1
import random
import timeit
import pandas as pd
x = random.randint(10 ** 3, 10 ** 6)
def test_naive():
a, b, c = x, 2 * x, x // 2
def test_shift():
a, b, c = x, x << 1, x >> 1
def test_mixed():
a, b, c = x, x * 2, x >> 1
def test_mixed_swapped():
a, b, c = x, x << 1, x // 2
def observe(k):
print(k)
return {
'naive': timeit.timeit(test_naive),
'shift': timeit.timeit(test_shift),
'mixed': timeit.timeit(test_mixed),
'mixed_swapped': timeit.timeit(test_mixed_swapped),
}
def get_observations():
return pd.DataFrame([observe(k) for k in range(100)])
La question :
Mon test est-il valable ? Si oui, pourquoi (multiply, shift) est-il plus rapide que (shift, shift) ?
J'utilise Python 3.5 sur Ubuntu 14.04.
Modifier
Ci-dessus, l'énoncé original de la question. Dan Getz fournit une excellente explication dans sa réponse.
Dans un souci d'exhaustivité, voici des exemples d'illustrations de plus grande taille. x
lorsque les optimisations de la multiplication ne s'appliquent pas.
3 votes
Où avez-vous défini
x
?3 votes
J'aimerais vraiment voir s'il y a une différence en utilisant little endian/big endian. Question très intéressante, d'ailleurs !
0 votes
JBernardo, il s'est perdu dans ma session interactive avec les rechargements. Je ferai une modification. Les résultats semblent cohérents.
1 votes
@LiGhTx117 Je m'attendrais à ce que ce ne soit pas lié aux opérations, à moins que
x
est très grande, parce que c'est juste une question de comment elle est stockée en mémoire, non ?2 votes
Je suis curieux, que diriez-vous de multiplier par 0,5 au lieu de diviser par 2 ? D'après mon expérience de la programmation en assembleur mips, la division donne normalement lieu à une opération de multiplication de toute façon. (Cela expliquerait la préférence pour le décalage de bits au lieu de la division).
3 votes
@Sayse qui le convertirait en virgule flottante. Avec un peu de chance, la division par le plancher des entiers serait plus rapide qu'un aller-retour en virgule flottante.
0 votes
@DanGetz - Oui, je suis d'accord, d'où ma curiosité (de plus, cette expérience a été faite il y a longtemps en travaillant avec des points flottants).
1 votes
Notez que l'exécution de ce test sur python2.x ne semble pas indiquer qu'une méthode soit systématiquement plus rapide que l'autre (du moins pas pour moi - et je n'ai testé que la méthode
naive
ymixed
). Cela m'amène à penser que cela n'a rien à voir avec le fait que les opérations soient plus rapides au niveau C, puisque les entiers de python3 sont d'une précision arbitraire...1 votes
La multiplication des entiers prend 1 instruction dans la plupart des ALU modernes. Elle ne devrait pas être plus lente que shift. La vraie question est donc de savoir pourquoi l'opérateur shift est plus lent.
1 votes
Et, pour les personnes plus intelligentes que moi : rshift y multiplier . Il semble que les opérations soient effectuées à l'aide de certains algorithmes de Knuth, bien que je n'aie pas regardé. super de près.
1 votes
@DanGetz Je pensais que cela pouvait être lié à des opérations profondes à l'intérieur du CPU. Mais cela pourrait n'être pertinent que pour un très grand nombre de tests. Votre réponse semble vraiment complète et cohérente. Merci pour votre partage.
0 votes
Sur ma Surface 3, Windows 10, avec Python 3, le décalage de bits dans un sens ou dans l'autre prend plus de temps que la multiplication ou la division par 2... et aussi avec des puissances de 2 de n'importe quelle taille. Je ne sais pas pourquoi, mais c'est indéniable. C'est mesurable, cohérent, et statistiquement significatif. J'ai écrit un simple script uniquement pour tester cela, sans autre fonctionnalité, juste pour voir la tendance.