По моим предыдущим вопрос Моя идея состояла в том, чтобы оптимизировать алгоритм, удалив вычисления, когда коэффициенты m_a, m_b равны 1,0 или 0,0. Теперь я попытался оптимизировать алгоритм и получил некоторые любопытные результаты, которые я не могу объяснить.
Первый анализатор работает на 100 тыс. Образцов. Значения параметров читаются из файла (!):
b0 = 1,0; b1 = -1,480838022915731; b2 = 1,0.
a0 = 1,0 a1 = -1,784147570544337 a2 = 0,854309980957510
Второй анализатор запускает те же 100 тыс. Образцов. Значения параметров читаются из файла (!):
b0 = 1,0; b1 = -1,480838022915731; b2 = 1,0.
a0 = 1,0 a1 = -1,784147570544337 a2 = 0,0 <— Только а2 отличается!
На рисунках цифры слева (серый фон) представляют необходимые циклы ЦП. Как хорошо видно, второй прогон с параметром a2 = 0.0 намного быстрее.
Я проверил разницу между кодом отладки и выпуска. Выпуск кода быстрее (как и ожидалось). Код отладки и выпуска имеет одинаковое странное поведение при изменении параметра a2.
Затем я проверил код ASM. Я заметил, что используются инструкции SSE. Это верно, потому что я скомпилировал с / arch: SSE2. Поэтому я отключил SSE. Полученный код больше не использует SSE, и производительность больше НЕ зависит от значения параметра a2 (как и ожидалось)
Поэтому я пришел к выводу, что это своего рода выигрыш в производительности, когда используется SSE, и движок SSE обнаруживает, что a2 равно 0.0, и поэтому исключает устаревшие умножение и вычитание. Я никогда не слышал об этом и пытался найти информацию, но безуспешно.
Так есть ли у кого-нибудь объяснение моих результатов работы?
Для полноты, это соответствующий код ASM для версии выпуска:
00F43EC0 mov edx,dword ptr [ebx]
00F43EC2 movss xmm0,dword ptr [eax+edi*4]
00F43EC7 cmp edx,dword ptr [ebx+4]
00F43ECA je $LN419+193h (0F43F9Dh)
00F43ED0 mov esi,dword ptr [ebx+4]
00F43ED3 lea eax,[edx+68h]
00F43ED6 lea ecx,[eax-68h]
00F43ED9 cvtps2pd xmm0,xmm0
00F43EDC cmp ecx,esi
00F43EDE je $LN419+180h (0F43F8Ah)
00F43EE4 movss xmm1,dword ptr [eax+4]
00F43EE9 mov ecx,dword ptr [eax]
00F43EEB mov edx,dword ptr [eax-24h]
00F43EEE movss xmm3,dword ptr [edx+4]
00F43EF3 cvtps2pd xmm1,xmm1
00F43EF6 mulsd xmm1,xmm0
00F43EFA movss xmm0,dword ptr [ecx]
00F43EFE cvtps2pd xmm4,xmm0
00F43F01 cvtps2pd xmm3,xmm3
00F43F04 mulsd xmm3,xmm4
00F43F08 xorps xmm2,xmm2
00F43F0B cvtpd2ps xmm2,xmm1
00F43F0F movss xmm1,dword ptr [ecx+4]
00F43F14 cvtps2pd xmm4,xmm1
00F43F17 cvtps2pd xmm2,xmm2
00F43F1A subsd xmm2,xmm3
00F43F1E movss xmm3,dword ptr [edx+8]
00F43F23 mov edx,dword ptr [eax-48h]
00F43F26 cvtps2pd xmm3,xmm3
00F43F29 mulsd xmm3,xmm4
00F43F2D subsd xmm2,xmm3
00F43F31 movss xmm3,dword ptr [edx+4]
00F43F36 cvtps2pd xmm4,xmm0
00F43F39 cvtps2pd xmm3,xmm3
00F43F3C mulsd xmm3,xmm4
00F43F40 movss xmm4,dword ptr [edx]
00F43F44 cvtps2pd xmm4,xmm4
00F43F47 cvtpd2ps xmm2,xmm2
00F43F4B xorps xmm5,xmm5
00F43F4E cvtss2sd xmm5,xmm2
00F43F52 mulsd xmm4,xmm5
00F43F56 addsd xmm3,xmm4
00F43F5A movss xmm4,dword ptr [edx+8]
00F43F5F cvtps2pd xmm1,xmm1
00F43F62 movss dword ptr [ecx+4],xmm0
00F43F67 mov edx,dword ptr [eax]
00F43F69 cvtps2pd xmm4,xmm4
00F43F6C mulsd xmm4,xmm1
00F43F70 addsd xmm3,xmm4
00F43F74 xorps xmm1,xmm1
00F43F77 cvtpd2ps xmm1,xmm3
00F43F7B movss dword ptr [edx],xmm2
00F43F7F movaps xmm0,xmm1
00F43F82 add eax,70h
00F43F85 jmp $LN419+0CCh (0F43ED6h)
00F43F8A movss xmm1,dword ptr [ebx+10h]
00F43F8F cvtps2pd xmm1,xmm1
00F43F92 mulsd xmm1,xmm0
00F43F96 xorps xmm0,xmm0
00F43F99 cvtpd2ps xmm0,xmm1
00F43F9D mov eax,dword ptr [ebp-4Ch]
00F43FA0 movss dword ptr [eax+edi*4],xmm0
00F43FA5 mov ecx,dword ptr [ebp-38h]
00F43FA8 mov eax,dword ptr [ebp-3Ch]
00F43FAB sub ecx,eax
00F43FAD inc edi
00F43FAE sar ecx,2
00F43FB1 cmp edi,ecx
00F43FB3 jb $LN419+0B6h (0F43EC0h)
Изменить: Заменить код отладки ASM на код выпуска.
На SSE нет ранних выходов для умножения FP. Это полностью конвейерная операция с коротким временем ожидания, поэтому добавление ранних выходов усложнит удаление инструкций, обеспечивая при этом нулевой выигрыш в производительности. Единственные инструкции, которые обычно имеют зависящие от данных характеристики исполнения на современных процессорах, — это деление и квадратный корень (игнорирование субнормалей, которые влияют на более широкий набор инструкций). Это подробно задокументировано как Intel, так и AMD, а также независимо от Agner Fog.
Так почему вы видите изменение в производительности? Наиболее вероятным объяснением является то, что вы сталкиваетесь с остановками из-за ненормальных входных данных или результатов; это очень часто встречается с фильтрами и задержками DSP, такими как у вас. Не видя ваш код и входные данные, невозможно быть уверенным, что это именно то, что происходит, но это, безусловно, наиболее вероятное объяснение. Если это так, вы можете устранить проблему, установив биты DAZ и FTZ в MXCSR.
Документация Intel:
http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf (обратитесь к таблицам задержки в приложении, обратите внимание, что есть фиксированное значение для mulss
а также mulsd
.)
Задержки инструкций 16 драм (электронная таблица Excel):
http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/AMD64_16h_InstrLatency_1.1.xlsx
Таблицы задержки инструкций Agner Fog для Intel и AMD:
http://www.agner.org/optimize/instruction_tables.pdf
Это было бы нормальное поведение, если Модуль умножения FP HW будет выполнять ранние операции. Ты можешь видеть Вот больше деталей. Это означает, что когда HW обнаруживает значение 0.0, оно не пропускает его через весь конвейер.
Однако вы используете SSE mulsd инструкция. В своем посте Стивен Кэнон указал, что в реализациях Intel и AMD задержка для mulsd инструкция исправлена. Это указывает на то, что нет никаких ранних функций, связанных с SSE.
Также Стивен Кэнон указал, что проблемы с производительностью возникают при использовании ненормальных чисел. В этом сообщение Вы можете прочитать больше о том, что вызывает это.
Однако все ваши коэффициенты являются частыми значениями, которые не кажутся ненормальными. Так что проблема может быть где-то еще. Все инструкции ASM в вашем коде задокументированы с фиксированными задержками, но огромная разница в циклах указывает на то, что что-то происходит.
Ваш вывод профилирования показывает, что все ваши задержки изменились, даже если коэффициент 0,0 появляется только в нескольких умножениях. Правильно ли рассчитан результат? Все остальные переменные постоянны между прогонами?