Хороший инструмент профилирования производительности F #

#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
 

Я получаю следующее дерево вызовов:

CallTree

Когда я просматриваю 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 в этом случае не исчезнут, потому что эта функция не является хвостовой рекурсивной. Попробуйте версию хвостового вызова, которую я добавил. Отладчик также имеет фильтр шумоподавления. Вы можете настроить его так, чтобы он игнорировал вызовы ниже% времени.