Это также зависит от набора инструкций. У вашего процессора в любой момент времени будет несколько резервных вычислительных блоков, и вы получите максимальную производительность, если все они будут постоянно заполнены. Таким образом, выполнение цикла mul так же быстро, как выполнение цикла или добавления, но то же самое не выполняется, если выражение становится более сложным.
Например, возьмите этот цикл:
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-битной процедуре. Если я заменю цикл на
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/add, но теперь константа добавляется, а не умножается -- занимает 3,7 секунды. Ваш процессор, вероятно, оптимизирован для более эффективного выполнения типичных числовых вычислений; так что скалярные произведения, такие как суммы muls и масштабированные суммы, примерно настолько хороши, насколько это возможно; добавление констант не так распространено, так что это медленнее...
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*/
(в основном muls, но одно дополнение: 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
person
Eamon Nerbonne
schedule
18.07.2009
divps
может быть приемлемым для пропускной способности, если вы не делаете это очень часто, и всегда имеет гораздо большую задержку. - person Peter Cordes   schedule 30.10.2018