Профилирование использования плавающей запятой в C

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

Если бы это был вариант, я полагаю, я мог бы заменить «a * b» на «умножить (a, b)» и написать функцию покрытия для собственного оператора *, b/c Меня действительно не волнует производительность по времени во время этого теста, но основным возражением против этого является необходимость переделывать груду исходного кода только для того, чтобы запустить тест.

У меня нет возражений против повторной компиляции исходного кода, возможно, с использованием какой-либо библиотеки или с неясными (афаик) параметрами. На ум пришел Valgrind, но если я понимаю цель valgrind, это больше касается отслеживания значений, чем операций подсчета.


person JustJeff    schedule 21.10.2011    source источник
comment
Заглянуть в PAPI? Я использовал его для подсчета количества операций с плавающей запятой в коде... работал как шарм. Может быть больно настроить.   -  person Patrick87    schedule 22.10.2011


Ответы (4)


Если ваш компилятор поддерживает soft-float (т. е. использование функций с целочисленными реализациями для эмуляции операций с плавающей запятой), вы можете скомпилировать свою программу в этом режиме (-msoft-float в GCC) и использовать свой любимый инструмент профилирования, чтобы измерить, сколько раз они вызываются.

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

person Stephen Canon    schedule 21.10.2011
comment
сладкий. очевидно, вам нужно что-то связать, чтобы получить __muldf3, и, вероятно, есть специальная библиотека для его предоставления, но я могу просто пискнуть свою собственную реализацию __muldf3 в исходный код. - person JustJeff; 22.10.2011
comment
@JustJeff: __muldf3 должно быть найдено в libgcc, но да, вы можете прокрасться в свою собственную реализацию, если вы следуете правильному ABI (который может отличаться от ABI вашей платформы по умолчанию). - person Stephen Canon; 22.10.2011
comment
Теперь, если бы я мог просто понять, как сделать функцию покрытия, которая просто использует обычную операцию *, чтобы мой _muldf3() мог вызывать это. попытался скомпилировать один файл .c с поддельным _muldf3(), используя -msoft-float, и скомпилировать другой файл .c с помощью mfoo(a,b), который просто возвращает a*b, без -msoft-float, а затем связать .o файлы. Теперь я могу возвращать константы из mfoo(), но не значения, вычисленные с помощью * , поэтому кажется, что я что-то упускаю. Кстати, MinGW 4.5.2 - person JustJeff; 22.10.2011
comment
хм. похоже, что это как-то связано с попыткой вернуть поплавки через границу с мягким поплавком/без границы мягкого поплавка, b/c возвращаемое ненужное значение, похоже, зависит от того, что другие вещи происходят в подпрограмме mfoo().. - person JustJeff; 22.10.2011
comment
@justJeff: это будет несоответствие ABI, о котором я говорил. Если вы знаете какой-либо ассемблер, вы сможете понять, как вернуть правильное значение на вашей платформе. - person Stephen Canon; 22.10.2011

Скомпилируйте исходный код на языке ассемблера, а затем найдите инструкции умножения.

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

person wallyk    schedule 21.10.2011
comment
Не совсем то, что он просил. Я собирался ответить тем же, пока не понял, что такие дизассемблированные объекты не содержат информации о том, по каким ветвям спустился работающий код. Другими словами, у вас нет информации о количестве операций умножения во время выполнения, а есть только информация о количестве многоуровневых инструкций. - person Edwin Buck; 22.10.2011
comment
Я думаю, что это хороший подход, но я сомневаюсь в его масштабируемости и общей применимости. Например, как бы вы применили это к итеративным кодам со сложными критериями сходимости? Я понимаю, что это делается часто, но у меня сложилось впечатление, что это применимо только к хорошо понятным небольшим фрагментам кода... а не к большим, сложным или неизвестным системам. - person Patrick87; 22.10.2011
comment
Хорошее направление. Что бы я сделал, так это скомпилировал код в asm (например, используя параметр -S с GCC), добавил в полученный код asm переменную счетчика (достаточно 32 бита?), инициализированную до 0, и вставил инструкцию увеличения счетчика (эквивалент синтаксиса at&t of add dword [counter], 1, если x86) перед каждой инструкцией fmul(p)/fimul (или чем-то, что выполняет умножение на рассматриваемой платформе). Затем перекомпилируйте код из ассемблерных файлов, запустите его в отладчике, чтобы увидеть счетчик при завершении программы. Конечно, счетчик и функцию printf() можно добавить в C до создания ассемблерных файлов. - person Alexey Frunze; 22.10.2011

Примечание: бесстыдная экстраполяция моего комментария на столько повторений, сколько я могу просмотреть.

PAPI имеет две функции API высокого уровня, называемые PAPI_flips и PAPI_flops, которые можно использовать для записи FLOPS, а также количества операций с плавающей запятой. Кроме того, PAPI предлагает множество других возможностей мониторинга счетчиков производительности, в зависимости от архитектуры вашего процессора... кэш, шина, память, ветки и т. д. Я думаю, что поддержка графических ускорителей и CUDA/GPGPU уже существует или скоро появится.

PAPI нужно будет установить в вашей системе, но я думаю, что он достаточно широко распространен, чтобы установка не была слишком болезненной, если вы знаете, что делаете.

Преимущество PAPI в том, что вам не нужно ничего знать о коде; просто запрограммируйте его (интерфейс такой же, как секундомер для FLOPS) и запустите его. Он основан на фактическом динамическом выполнении вашей программы, поэтому он принимает во внимание вещи, которые трудно учесть аналитически, такие как (псевдо-)случайное поведение, ввод пользователя/переменной и связанные ветки.

person Patrick87    schedule 21.10.2011
comment
Перефразируя Уилла Роджерса: я никогда не мета мета, который мне не нравился. Наслаждайтесь поездкой. - person MusiGenesis; 22.10.2011
comment
@MusiGenesis: Сладкие блинчики с большим количеством репутации. - person Patrick87; 22.10.2011

Лучшее, что я могу придумать (при условии, что вы используете gdb):

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

Да, это очень грубо. Конечно, есть и другие решения; однако я бы не решился выбросить свой стек за что-то такое простое, как подсчет.

person Edwin Buck    schedule 21.10.2011