Показать сообщение отдельно
Старый 20.02.2015, 23:48   #1
Samodelkin
Мастер
 
Регистрация: 12.01.2009
Сообщений: 979
Написано 388 полезных сообщений
(для 631 пользователей)
Автоматический оптимизатор

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

Первый случай это векторная обработка данных. Вроде как говорят "просто включи флаги -O3 -msse2" и всё будет ок. На самом деле так не будет (или будет очень редко). Потому что если в программе нет данных которые можно рассувать по xmm регистрам то и не будет ускорения. А эти самые данные из неоткуда не возникнут. В моём случае у меня была функция (софтварный аналог пиксельного шейдера) которая обрабатывала по одному пикселю. Код был не подходящий для векторной обработки, кое-где были типы наподобие float3 которые возможно местами компилятор могбы оптимизировать, но ускорение было бы не такое большое. Чтобы лучше разделить обработку на 4 потока данных я написал новую функцию которая разом обрабатывает сразу 4 пикселя. По сути это было частичное изменение пайплайна рендера, ведь в месте вызова функции и в месте получения данных тоже пришлось изменять код. Но внутри функции удалось несколько сократить рассчёты и главное разместить соответствующие данные вместе. Теперь на выбор есть два варианта: либо включить флаги -msse2 и компилятор сможет адекватно разместить данные по xmm регистрам или же самому вставить ассемблерные вставки или интринсики. Главное здесь то что 80% процентов работы это написание новой функции обрабатывающей по 4 пикселя и частичное изменение пайплайна рендера, и остальные 20% это то что можно потратить на ассемблерные вставки. Вопрос: нужно ли после уже выполнения 4/5 работы воспользоваться оптимизатором и получить в целом ту же производительность, но обфусцированный ассемблерный код?

Второй случай -- создание карты памяти. Карта памяти это когда программист заранее располагает какие-то критические к производительности данные особым образом, например с учётом выравнивания или уплотнения для избежания кеш промахов. У меня было 3 функции, часть данных передавалась через стек, часть через данные-члены общего объекта. Я создал более продуманную структуру которая отображалась на выровненную область памяти. Удалось сократить количество переменных (а это значит трафик по шине памяти) более чем в два раза. Компилятор этого тоже сделать не может и не должен, потому что если ему сказали создать переменную, он её должен создать. Он не может предположить типа "вон та переменная 100 строками выше видимо больше не понадобиться, давай сохраним значение туда". Задача компилятора оптимизировать вещи локальные, например распределение регистров. Думать глобально он не может из за ограниченной области видимости. В то время как относительно-стратегические решения дают более ощутимый результат в производительности. Оптимизация компиляторов скорее связана больше с издержками внутреннего устройства языков. Например преподавание ООП в целом ничего не говорит о том как данные располагаются в памяти, и что при частом применении механизмов RTTI или обращения к vtable будут возникать кеш-промахи. Только когда сталкиваешься с созданием быстрого кода начинаешь узнавать о подобных правилах типа "не пользоваться наследованием, библиотека std слишком тяжёлая" и т. п. Вот в этих случаях оптимизация немного сглаживает ситуацию. Но в целом согласно DOD подходу можно создавать хорошо структурированный код не прибегая к тяжёлым для компилятора ситуациям, не требующим включения оптимизации, и потратив не более чем столько-же времени.
(Offline)
 
Ответить с цитированием