Вопрос

Десять лет или два назад было полезно написать числовой код, чтобы избежать использования умножений и разделения и вместо этого использовать добавление и вычитание. Хорошим примером является использование вперед различия Оценить полиномиальную кривую вместо того, чтобы напрямую вычислять полином.

Это все еще так, или современные компьютерные архитектуры продвинулись до такой степени, что *,/ не уже много раз медленнее, чем +,-?

Чтобы быть конкретным, я заинтересован в скомпилированном коде C/C ++, работающем на современных типичных чипах x86 с обширным бортовым оборудованием для плавающей запятой, а не небольшим микро-попытками сделать FP в программном обеспечении. Я понимаю, что трубопроводы и другие архитектурные усовершенствования исключают конкретное количество циклов, но я все равно хотел бы получить полезную интуицию.

Это было полезно?

Решение

Это также зависит от микса инструкций. У вашего процессора будет несколько вычислительных единиц в любое время, и вы получите максимальную пропускную способность, если все они будут заполнены все время. Таким образом, выполнение петли Mul's так же быстро, как выполнение цикла или добавление, но то же самое не содержится, если выражение становится более сложным.

Например, возьмите эту петлю:

for(int j=0;j<NUMITER;j++) {
  for(int i=1;i<NUMEL;i++) {
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ;
  }
}

Для numiter = 10^7, numel = 10^2, оба массива, инициализированные до небольших положительных чисел (NAN намного медленнее), это занимает 6,0 секунды, используя удваивание на 64-битной Proc. Если я заменю петлю на

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ;

Это займет всего 1,7 секунды ... так что, поскольку мы «преувеличивали» дополнения, мул был по существу свободным; и сокращение дополнений помогло. Это становится более запутанным:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ;

- Такое же распределение Mul/добавление, но теперь константа добавляется, а не умножается- займет 3,7 секунды. Ваш процессор, вероятно, оптимизирован для более эффективного выполнения типичных численных вычислений; Таким образом, точечный продукт, как суммы мул и масштабированных сумм, примерно так же хороши, как и; Добавление констант не так распространено, так что это медленнее ...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/

снова занимает 1,7 секунды.

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/

(То же, что и начальный цикл, но без дорогостоящего постоянного дополнения: 2,1 секунды)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/

(в основном мул, но одно дополнение: 1,9 секунды)

Итак, в основном; Трудно сказать, что быстрее, но если вы хотите избежать узких мест, более важно иметь вменяемой смесь, избегать NAN или Inf, избегать добавления констант. Что бы вы ни делали, убедитесь, что вы проверяете и проверяете различные настройки компилятора, так как часто небольшие изменения могут просто иметь значение.

Еще несколько случаев:

bla *= someval; // someval very near 1.0; takes 2.1 seconds
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86

Другие советы

Теоретически информация здесь:

Справочное руководство по оптимизации архитектуры Intel®64 и IA-32, задержка инструкции и пропускная способность приложения C и пропускная способность

Для каждого процессора, который они перечисляют, задержка на FMUL очень близка к задержке FADD или FDIV. На некоторых более старых процессорах FDIV на 2-3 времена медленнее, чем на более новых процессорах, это то же самое, что и FMUL.

Предостережения:

  1. В документе, который я связан, на самом деле говорится, что вы не можете полагаться на эти цифры в реальной жизни, поскольку процессор сделает то, что хочет сделать вещи быстрее, если это правильно.

  2. Есть большая вероятность, что ваш компилятор решит использовать один из множества новых наборов инструкций, которые имеют доступный разрыв / разделение с плавающей точкой.

  3. Это сложный документ, предназначенный только для чтения писателей компилятора, и я мог бы ошибаться. Как будто мне не ясно, почему номер задержки FDIV полностью отсутствует для некоторых процессоров.

Лучший способ ответить на этот вопрос - на самом деле написать эталон/профиль обработки, которую вам нужно сделать. Эмпирический должен использоваться над теоретическим, когда это когда -либо возможно. Особенно, когда это легко достичь.

Если вы уже знаете различные реализации математики, которую вам нужно сделать, вы можете написать несколько различных передачи кода математики и посмотреть, где пики вашей производительности. Это позволит процессору/компилятору генерировать различные потоки выполнения, чтобы заполнить трубопроводы процессора и дать вам конкретный ответ на ваш ответ.

Если вы заинтересованы конкретно о производительности инструкций Div/Mul/Add/Sub -тип, вы можете даже бросить в какую -то встроенную сборку, чтобы конкретно контролировать, какие варианты этих инструкций выполняются. Однако вы должны убедиться, что вы сохраняете многочисленные подразделения выполнения, чтобы получить хорошее представление о производительности, на которую способна система.

Кроме того, что -то подобное позволило бы вам сравнить производительность по нескольким вариациям процессора, просто запустив одну и ту же программу на них, а также позволит вам учитывать различия материнской платы.

Редактировать:

Основная архитектура A +- идентична. Поэтому они логически принимают то же время, чтобы вычислить. * С другой стороны, требуется несколько слоев, обычно построенных из «полных добавок», чтобы завершить одну операцию. Это, что, хотя A * может быть выпущен в трубопровод каждый цикл, он будет иметь более высокую задержку, чем схема добавления/вычтения. Операция FP / обычно реализуется с использованием метода аппроксимации, который итеративно сходится к правильному ответу с течением времени. Эти типы приближений обычно реализуются путем умножения. Таким образом, для плавающей запятой вы, как правило, можете предположить, что деление займет больше времени, потому что нецелесообразно «развернуть» умножения (что уже является большой схемой в его самим собой) в трубопровод множества цепей множителя. Тем не менее, производительность данной системы лучше всего измеряется с помощью тестирования.

Я не могу найти окончательную ссылку, но обширные эксперименты говорят мне, что умножение плавания в настоящее время является примерно такой же скоростью, что и с добавлением и вычитанием, в то время как деление не (но не «много раз», либо медленнее). Вы можете получить желаемую интуицию, только проведя свои собственные эксперименты - не забудьте заранее генерировать случайные числа (миллионы из них), прочитать их перед началом времени, и использовать счетчики производительности процессора (без другого процесса. Как вы можете остановить их) для точного измерения!

Разница скорости * / VS + - зависит от вашей архитектуры процессора. В целом и с x86, в частности, разница в скорости стала меньше с современными процессорами. * Должен быть близок к +, когда сомневается: просто экспериментируйте. Если у вас есть действительно сложная проблема с большим количеством операций FP, также рассмотрите возможность использования вашего GPU (GeForce, ...), который работает в качестве векторного процессора.

Вероятно, существует очень небольшая разница во времени между умножением и дополнением. С другой стороны, деление по -прежнему значительно медленнее, чем умножение из -за его рекурсивной природы. На современных инструкциях по архитектуре X86 следует учитывать при выполнении операции с плавающей запятой, а не с использованием FPU. Да, хороший компилятор C/C ++ должен дать вам возможность использования SSE вместо FPU.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top