Вот забавный глупый вопрос:
Допустим, нам нужно выполнить простую операцию, когда нам нужна половина значения переменной. Есть обычно два способа сделать это:
y = x / 2.0;
// or...
y = x * 0.5;
Предполагая, что мы используем стандартные операторы, поставляемые с языком, какой из них имеет лучшую производительность?
Я предполагаю, что умножение обычно лучше, поэтому я стараюсь придерживаться этого, когда пишу код, но я хотел бы подтвердить это.
Хотя лично меня интересует ответ для Python 2.4-2.5, не стесняйтесь также публиковать ответ для других языков! И если хотите, не стесняйтесь публиковать другие более изящные способы (например, с использованием операторов побитового сдвига).
performance
programming-languages
Edmundito
источник
источник
Ответы:
Python:
умножение на 33% быстрее
Lua:
=> нет реальной разницы
LuaJIT:
=> это всего на 5% быстрее
Выводы: в Python умножать быстрее, чем делить, но по мере приближения к ЦП с использованием более продвинутых виртуальных машин или JIT преимущество исчезает. Вполне возможно, что будущая виртуальная машина Python сделает это неактуальным.
источник
Всегда используйте самое чистое. Все остальное, что вы делаете, - это попытки перехитрить компилятор. Если компилятор хоть сколько-нибудь умен, он сделает все возможное, чтобы оптимизировать результат, но ничто не может заставить следующего парня не возненавидеть вас за ваше дерьмовое решение для сдвига битов (кстати, я люблю манипуляции с битами, это весело. Но весело! )
Преждевременная оптимизация - это корень всех зол. Всегда помните о трех правилах оптимизации!
Если вы специалист и можете обосновать необходимость, воспользуйтесь следующей процедурой:
Кроме того, такие действия, как удаление внутренних циклов, когда они не требуются, или выбор связанного списка над массивом для сортировки вставкой - это не оптимизация, а просто программирование.
источник
Я думаю, что это становится настолько придирчивым, что вам лучше сделать то, что делает код более читабельным. Если вы не выполните операции тысячи, если не миллионы, раз, я сомневаюсь, что кто-то когда-нибудь заметит разницу.
Если вам действительно нужно сделать выбор, сравнительный анализ - единственный выход. Найдите, какие функции вызывают проблемы, затем выясните, где в функции возникают проблемы, и исправьте эти разделы. Однако я все еще сомневаюсь, что одна математическая операция (даже одна, повторяемая много-много раз) могла бы стать причиной возникновения узких мест.
источник
Умножение происходит быстрее, деление точнее. Вы потеряете некоторую точность, если ваше число не является степенью двойки:
Даже если вы позволите компилятору вычислить инвертированную константу с идеальной точностью, ответ все равно может быть другим.
Проблема скорости, вероятно, будет иметь значение только в языках C / C ++ или JIT, и даже тогда, только если операция выполняется в цикле в узком месте.
источник
y = x * (1.0/3.0);
и компилятор обычно вычисляет 1/3 во время компиляции. Да, 1/3 не является идеальным представлением в IEEE-754, но когда вы выполняете арифметику с плавающей запятой, вы все равно теряете точность , независимо от того, выполняете ли вы умножение или деление, потому что биты младшего разряда округляются. Если вы знаете, что ваши вычисления настолько чувствительны к ошибке округления, вы также должны знать, как лучше всего решить эту проблему.(1.0/3.0)
с делением на3.0
. Я получил 1,0000036666774155, и в этом месте 7,3% результатов были другими. Я предполагаю, что они различались только на 1 бит, но поскольку арифметика IEEE гарантированно округляет до ближайшего правильного результата, я придерживаюсь своего утверждения, что деление более точное. Будет ли разница значительна - решать вам.Если вы хотите оптимизировать свой код, но при этом сохранять ясность, попробуйте следующее:
Компилятор должен иметь возможность выполнять деление во время компиляции, поэтому вы получите умножение во время выполнения. Ожидаю, что точность будет такой же, как и в
y = x / 2.0
случае.Там, где это может иметь значение, ЛОТ находится во встроенных процессорах, где эмуляция с плавающей запятой требуется для вычисления арифметики с плавающей запятой.
источник
y = x / 2.0;
но в реальном мире вам, возможно, придется уговорить компилятор выполнить менее затратное умножение. Возможно, менее понятно, почемуy = x * (1.0 / 2.0);
лучше, и было бы яснее заявитьy = x * 0.5;
вместо этого. Но измените значение2.0
на a,7.0
и я лучше посмотрю,y = x * (1.0 / 7.0);
чемy = x * 0.142857142857;
.Просто собираюсь добавить что-нибудь для опции «другие языки».
C: Поскольку это просто академическое упражнение, которое на самом деле не имеет значения, я подумал, что внесу что-то другое.
Я скомпилировал в сборку без оптимизации и посмотрел на результат.
Код:
составлено с
gcc tdiv.c -O1 -o tdiv.s -S
деление на 2:
и умножение на 0,5:
Однако, когда я изменил эти
int
s наdouble
s (что, вероятно, сделал бы python), я получил следующее:деление:
умножение:
Я не тестировал какой-либо из этих кодов, но, просто изучив код, вы можете увидеть, что при использовании целых чисел деление на 2 короче, чем умножение на 2. При использовании удвоений умножение короче, поскольку компилятор использует коды операций процессора с плавающей запятой, которые вероятно, работают быстрее (но на самом деле я не знаю), чем не использовать их для той же операции. Таким образом, в конечном итоге этот ответ показал, что производительность умножения на 0,5 по сравнению с делением на 2 зависит от реализации языка и платформы, на которой он работает. В конечном итоге разница незначительна, и вам практически никогда не стоит беспокоиться, кроме как с точки зрения удобочитаемости.
В качестве примечания, вы можете видеть, что в моей программе
main()
возвращаетсяa + b
. Когда я уберу ключевое слово volatile, вы никогда не угадаете, как выглядит сборка (за исключением настройки программы):он выполнял и деление, и умножение, и сложение в одной инструкции! Ясно, что вам не нужно об этом беспокоиться, если оптимизатор хоть сколько-нибудь уважаемый.
Извините за слишком длинный ответ.
источник
movl $5, %eax
название оптимизации не важно и даже не имеет значения. Вы просто хотели снисходительно отреагировать на ответ четырехлетней давности.Во-первых, если вы не работаете на C или ASSEMBLY, вы, вероятно, работаете на языке более высокого уровня, где задержки памяти и общие накладные расходы на вызовы полностью уменьшат разницу между умножением и делением до точки несущественности. Так что просто выберите то, что в этом случае читается лучше.
Если вы говорите с очень высокого уровня, это не будет заметно медленнее для всего, для чего вы, вероятно, будете его использовать. В других ответах вы увидите, что людям нужно сделать миллион операций умножения / деления, чтобы измерить разницу в доли миллисекунды между ними.
Если вам все еще интересно, с точки зрения оптимизации низкого уровня:
У Divide, как правило, значительно более длинный конвейер, чем у умножения. Это означает, что для получения результата требуется больше времени, но если вы можете держать процессор занятым независимыми задачами, это не будет стоить вам больше, чем умножение.
Какова разница в длине конвейера, полностью зависит от оборудования. Последнее оборудование, которое я использовал, было примерно 9 циклов для умножения FPU и 50 циклов для деления FPU. Звучит много, но тогда вы потеряете 1000 циклов из-за промаха памяти, так что это может представить ситуацию в перспективе.
Аналогия: кладете пирог в микроволновую печь во время просмотра телешоу. Общее время, которое у вас ушло от телешоу, - это то, сколько времени потребовалось, чтобы положить его в микроволновую печь и вынуть из нее. В остальное время вы все еще смотрели телешоу. Таким образом, если на приготовление пирога ушло 10 минут, а не 1 минута, на самом деле это не отнимало у вас больше времени на просмотр телевизора.
На практике, если вы собираетесь достичь уровня заботы о разнице между Multiply и Divide, вам необходимо понимать конвейеры, кеш, срывы ветвлений, прогнозирование вне очереди и зависимости конвейеров. Если это не похоже на то, что вы собирались задать с этим вопросом, то правильный ответ - игнорировать разницу между ними.
Много (много) лет назад было абсолютно необходимо избегать делений и всегда использовать умножения, но тогда обращения к памяти были менее актуальны, а деления были намного хуже. В наши дни я оцениваю читаемость выше, но если разницы в читаемости нет, я думаю, что это хорошая привычка выбирать умножение.
источник
Напишите, что более четко выражает ваши намерения.
После того, как ваша программа заработает, выясните, что медленное, и сделайте это быстрее.
Не делайте этого наоборот.
источник
Делай все, что тебе нужно. Сначала подумайте о своем читателе, не беспокойтесь о производительности, пока не убедитесь, что у вас проблемы с производительностью.
Позвольте компилятору сделать работу за вас.
источник
Если вы работаете с целыми числами или типами без чисел с плавающей запятой, не забудьте операторы сдвига битов: << >>
источник
На самом деле, есть веская причина, по которой, как правило, умножение происходит быстрее, чем деление. Аппаратное деление с плавающей запятой выполняется либо с помощью алгоритмов сдвига и условного вычитания («длинное деление» с двоичными числами), либо - что более вероятно в наши дни - с помощью итераций, подобных алгоритму Гольдшмидта . Для сдвига и вычитания требуется по крайней мере один цикл на бит точности (итерации почти невозможно распараллелить, как и сдвиг-и-сложение умножения), а итерационные алгоритмы выполняют по крайней мере одно умножение на итерацию.. В любом случае весьма вероятно, что деление займет больше циклов. Конечно, это не учитывает причуды в компиляторах, перемещение данных или точность. Однако по большому счету, если вы кодируете внутренний цикл в чувствительной ко времени части программы,
0.5 * x
или,1.0/2.0 * x
скорее,x / 2.0
это разумный поступок. Педантизм «кодируйте самое ясное» абсолютно верно, но все три из них настолько близки по удобочитаемости, что педантизм в данном случае просто педантичен.источник
Я всегда знал, что умножение более эффективно.
источник
Умножение обычно происходит быстрее, но никогда не медленнее. Однако, если скорость не критична, напишите наиболее четкую.
источник
Деление с плавающей запятой (как правило) особенно медленное, поэтому, хотя умножение с плавающей запятой также относительно медленное, оно, вероятно, быстрее, чем деление с плавающей запятой.
Но я более склонен ответить «на самом деле это не имеет значения», если только профилирование не показало, что деление является узким местом по сравнению с умножением. Однако я предполагаю, что выбор умножения или деления не окажет большого влияния на производительность вашего приложения.
источник
Это становится большим вопросом, когда вы программируете на ассемблере или, возможно, на C. Я полагаю, что с большинством современных языков такая оптимизация выполняется за меня.
источник
Будьте осторожны с «угадывать умножение, как правило, лучше, поэтому я стараюсь придерживаться этого, когда пишу код»,
В контексте этого конкретного вопроса «лучше» здесь означает «быстрее». Что не очень полезно.
Думать о скорости может быть серьезной ошибкой. В конкретной алгебраической форме вычислений есть серьезные ошибки.
См. Раздел Арифметика с плавающей запятой с анализом ошибок . См. Раздел « Основные вопросы арифметики с плавающей запятой и анализа ошибок» .
Хотя некоторые значения с плавающей запятой являются точными, большинство значений с плавающей запятой являются приблизительными; это некое идеальное значение плюс некоторая ошибка. Каждая операция применяется к идеальному значению и значению ошибки.
Самые большие проблемы возникают при попытке манипулировать двумя почти равными числами. Крайние правые биты (биты ошибок) определяют результаты.
В этом примере вы можете видеть, что по мере уменьшения значений разница между почти равными числами дает ненулевые результаты, где правильный ответ равен нулю.
источник
Я где-то читал, что умножение более эффективно в C / C ++; Понятия не имею относительно интерпретируемых языков - разница, вероятно, незначительна из-за всех других накладных расходов.
Если это не станет проблемой, придерживайтесь того, что является более удобным в обслуживании / читаемом - я ненавижу, когда люди говорят мне это, но это так верно.
источник
Я бы предложил умножение в целом, потому что вам не нужно тратить циклы на то, чтобы ваш делитель не был равен нулю. Это, конечно, не применяется, если ваш делитель постоянный.
источник
Java android, профилированный на Samsung GT-S5830
Полученные результаты?
Деление примерно на 20% быстрее умножения (!)
источник
a = i*0.5
, а неa *= 0.5
. Вот как большинство программистов будут использовать операции.То же, что и посты №24 (умножение быстрее) и №30, но иногда они оба так же легко понять:
~ Я считаю, что их обоих так же легко читать, и мне приходится повторять их миллиарды раз. Поэтому полезно знать, что умножение обычно происходит быстрее.
источник
Есть разница, но она зависит от компилятора. Сначала на vs2003 (c ++) у меня не было существенной разницы для типов double (64-битная плавающая точка). Однако, снова запустив тесты на vs2010, я обнаружил огромную разницу, до 4 раз быстрее при умножении. Отслеживая это, кажется, что vs2003 и vs2010 генерируют другой код fpu.
На Pentium 4, 2,8 ГГц, vs 2003:
На Xeon W3530, vs2003:
На Xeon W3530, vs2010:
Похоже, что на vs2003 деление в цикле (так что делитель использовался несколько раз) было переведено в умножение на обратное. На vs2010 эта оптимизация больше не применяется (я полагаю, потому что результаты этих двух методов немного отличаются). Также обратите внимание, что процессор выполняет деление быстрее, как только ваш числитель равен 0,0. Я не знаю точного алгоритма, встроенного в чип, но, возможно, он зависит от числа.
Изменить 18-03-2013: наблюдение за vs2010
источник
n/10.0
выражением формы(n * c1 + n * c2)
? Я ожидал, что на большинстве процессоров деление займет больше времени, чем два умножения и деление, и я считаю, что деление на любую константу может дать правильно округленный результат во всех случаях, используя указанную формулировку.Вот глупый забавный ответ:
x / 2.0 не является эквивалентно х * 0,5
Допустим, вы написали этот метод 22 октября 2008 года.
Теперь, 10 лет спустя, вы узнаете, что можете оптимизировать этот фрагмент кода. На этот метод ссылаются сотни формул в вашем приложении. Вы меняете его и получаете заметное улучшение производительности на 5%.
Было ли решение изменить код правильным? В математике эти два выражения действительно эквивалентны. В информатике это не всегда верно. Прочтите статью Минимизация влияния проблем с точностью для более подробной информации. Если ваши вычисленные значения - в какой-то момент - сравниваются с другими значениями, вы измените результат крайних случаев. Например:
Итог: как только вы согласитесь на одно из двух, придерживайтесь его!
источник
2
и0.5
числа могут быть представлены в IEEE 754 точно, без потери точности (в отличие, например, от0.4
или0.1
, они не могут).Что ж, если мы предположим, что операция сложения / вычитания стоит 1, тогда умножим затраты на 5 и разделим затраты примерно на 20.
источник
После такого долгого и интересного обсуждения вот мой взгляд на это: на этот вопрос нет окончательного ответа. Как отмечали некоторые люди, это зависит как от оборудования (см. Piotrk и gast128 ), так и компилятора (см. Тесты @Javier ). Если скорость не критична, если вашему приложению не требуется обрабатывать в реальном времени огромный объем данных, вы можете выбрать для ясности использование деления, тогда как, если скорость обработки или загрузка процессора являются проблемой, умножение может быть самым безопасным. Наконец, если вы точно не знаете, на какой платформе будет развернуто ваше приложение, тест не имеет смысла. А для ясности кода достаточно одного комментария!
источник
Технически деления не существует, есть просто умножение на обратные элементы. Например, вы никогда не делите на 2, вы фактически умножаете на 0,5.
«Деление» - давайте обманывать себя тем, что оно существует на секунду - всегда сложнее умножения, потому что для «деления»
x
наy
единицу сначала нужно вычислить такое значениеy^{-1}
,y*y^{-1} = 1
а затем произвести умножениеx*y^{-1}
. Если вы уже знаете,y^{-1}
то не вычислениеy
должно быть оптимизацией.источник