#performance #f# #profiling
#Производительность #f# #профилирование
Вопрос:
Может ли кто-нибудь порекомендовать инструмент профилирования производительности с хорошей поддержкой F #?
Я использую профилировщик Visual Studio 2010, но обнаружил несколько проблем при использовании F #. Больше похоже, что я профилирую байтовый код после отражения, чем исходный F #.
Например, при профилировании следующего слегка надуманного примера:
let Add a b =
a b
let Add1 = Add 1
let rec MultiAdd count =
match count with
| 1 -> 1
| _ -> (Add1 1) (MultiAdd (count - 1))
MultiAdd 10000 |> ignore
Я получаю следующее дерево вызовов:
Когда я просматриваю Microsoft.FSharp.Core.FSharpFunc`2.Invoke(0) в деталях функции, которые я вижу:
Я понимаю, что то, что я вижу, основано на базовой реализации скомпилированного кода, и хотя я могу следовать ему, это сложно.
Есть ли у кого-нибудь опыт использования других инструментов профилирования с F # и лучше ли они справляются с сопоставлением с исходным кодом F #?
Комментарии:
1. Вы пробовали опцию для выборки процессора вместо инструментария при вызовах функций?
2. Я не смотрел на это в последнее время, поэтому не могу сказать, какие сейчас лучшие варианты, но в то время я использовал dotTrace и просто привык работать с декомпилированным кодом, сгенерированным dotTrace. Отслеживание всех вызовов ‘.Invoke ()’ на самом деле весьма полезно для диагностики проблем с производительностью, поскольку оно показывает вам, что на самом деле происходит под капотом. Как правило, чем сложнее был декомпилированный код, тем медленнее он выполнялся.
Ответ №1:
Мой ответ может вас разочаровать, но он может быть полезен.
Несколько месяцев назад я пытался найти хороший бесплатный профилировщик .NET для моего проекта F #. Мой опыт работы с nprof, slimtune, EQATEC и (недавно коммерческим) Xte profiler не был достойным вообще. Я обнаружил, что их поддержка F # была очень ограниченной, и мне пришлось вернуться к профилировщику Visual Studio 2010. Я думаю, что лучше всего здесь использовать какой-нибудь коммерческий профилировщик (с которым у меня нет опыта).
Через некоторое время я привыкаю к профилировщику и вижу его представление результатов простым, ясным и понятным. Если бы вы оптимизировали параллельные программы, использование параллельного визуализатора было бы неизбежно. Тем не менее, единственное, что вас волнует, — это производительность; стоит попробовать хорошо поработать с профилировщиком VS 2010.
Для профилирования кода F # я также считаю, что стоит упомянуть CLR Profiler и ILSpy. Первый может визуализировать кучи, если вы хотите минимизировать выделение памяти или сбор мусора. Последний может создавать эквивалентный код на IL или C # (с которым я знаком больше, чем с F #); это может помочь понять, как работают конструкции высокого порядка в F #, чтобы использовать их надлежащим образом.
Обновить:
Дейв Томас написал отличный пост в блоге, в котором он использовал несколько коммерческих профилировщиков для обнаружения утечек памяти и настройки асинхронного приложения. Взгляните на эти профилировщики; они могут соответствовать вашим предпочтениям.
Комментарии:
1. Спасибо за упоминание! Основная проблема заключается в том, чтобы сопоставить имена обратно с типами F #, проверить оценку jetbrains.com/profiler если бы у вас была опция «строки кода», которая позволяет вам профилировать вплоть до нарушающей строки
Ответ №2:
Это похоже на ваше профилирование в режиме отладки. Вам необходимо включить «Оптимизировать код» в меню проект -> свойства -> сборка. Вы также можете профилировать в режиме выпуска, в котором это включено по умолчанию. Если вы этого не сделаете, среди прочего, будет много вызовов invoke и создания объектов Tuple.
Приведенная выше функция MultiAdd не является хвостовой рекурсией. Если бы это было так, вам также нужно было бы включить «Генерировать хвостовые вызовы» в режиме отладки для профилирования.
Это также было бы хорошим примером для оптимизации конечных вызовов.
let Add a b =
a b
let Add1 = Add 1
let rec MultiAdd total count =
match count with
| 1 -> 1 total
| _ -> MultiAdd (count - 1) (total Add1 1)
MultiAdd 10000 0 |> ignore
Комментарии:
1. Я попытался запустить его в режиме выпуска и убедился, что «Оптимизировать код» проверено, но все равно получил тот же результат. Я что-то упускаю?
2. @KeithHarrison вызовы invoke в этом случае не исчезнут, потому что эта функция не является хвостовой рекурсивной. Попробуйте версию хвостового вызова, которую я добавил. Отладчик также имеет фильтр шумоподавления. Вы можете настроить его так, чтобы он игнорировал вызовы ниже% времени.