В этом ответе @Dunes говорится, что из-за конвейерности (почти) нет разницы между умножением и делением с плавающей запятой. Однако из моего опыта с другими языками я ожидал бы, что деление будет медленнее.
Мой маленький тест выглядит следующим образом:
A=np.random.rand(size)
command(A)
Для разных команд и size=1e8
я получаю следующие моменты на моей машине:
Command: Time[in sec]:
A/=0.5 2.88435101509
A/=0.51 5.22591209412
A*=2.0 1.1831600666
A*2.0 3.44263911247 //not in-place, more cache misses?
A+=A 1.2827270031
Самая интересная часть: деление на 0.5
почти в два раза быстрее, чем деление на 0.51
. Можно предположить, что это связано с некоторой интеллектуальной оптимизацией, например. заменяя деление на A+A
. Однако таймеры A*2
и A+A
слишком далеко, чтобы поддержать это утверждение.
В общем случае деление по поплавкам со значениями (1/2)^n
выполняется быстрее:
Size: 1e8
Command: Time[in sec]:
A/=0.5 2.85750007629
A/=0.25 2.91607499123
A/=0.125 2.89376401901
A/=2.0 2.84901714325
A/=4.0 2.84493684769
A/=3.0 5.00480890274
A/=0.75 5.0354950428
A/=0.51 5.05687212944
Это становится еще интереснее, если мы посмотрим на size=1e4
:
Command: 1e4*Time[in sec]:
A/=0.5 3.37723994255
A/=0.51 3.42854404449
A*=2.0 1.1587908268
A*2.0 1.19793796539
A+=A 1.11329007149
Теперь нет разницы между делением на .5
и на .51
!
Я попробовал его для разных версий numpy и разных машин. На некоторых машинах (например, Intel Xeon E5-2620) этот эффект можно увидеть, но не на некоторых других машинах - и это не зависит от версии numpy.
С помощью script @Ralph Versteegen (см. его отличный ответ!) я получаю следующие результаты:
- тайминг с i5-2620 (Haswell, 2x6 ядер, но очень старая версия с числовым значением, которая не использует SIMD):
- тайминги с i7-5500U (Broadwell, 2 ядра, numpy 1.11.2):
Вопрос: В чем причина более высокой стоимости деления на 0.51
по сравнению с делением на 0.5
для некоторых процессоров, если размеры массива велики ( > 10 ^ 6).
В ответе @nneonneo говорится, что для некоторых процессоров Intel существует оптимизация при делении на две степени, но это не объясняет, почему мы можем видеть преимущество этого только для больших массивов.
Первоначальный вопрос: "Как можно объяснить эти разные поведения (деление на 0.5
и деление на 0.51
)?"
Здесь также мое первоначальное тестирование script, которое создало тайминги:
import numpy as np
import timeit
def timeit_command( command, rep):
print "\t"+command+"\t\t", min(timeit.repeat("for i in xrange(%d):"
%rep+command, "from __main__ import A", number=7))
sizes=[1e8, 1e4]
reps=[1, 1e4]
commands=["A/=0.5", "A/=0.51", "A*=2.2", "A*=2.0", "A*2.2", "A*2.0",
"A+=A", "A+A"]
for size, rep in zip(sizes, reps):
A=np.random.rand(size)
print "Size:",size
for command in commands:
timeit_command(command, rep)