Подтвердить что ты не робот

Почему F # inline вызывает 11-кратное повышение производительности

Я работаю над проблемой тяжелого процессора. Я вижу большое улучшение производительности, когда я использую ключевое слово inline. Я создаю словарь из стандартной библиотеки .net, проходящей в пользовательском ключе Comparer, смотрите результаты кода и времени ниже

https://gist.github.com/4409734

без встроенного ключевого слова на Eq_cmp

> perf_run 10000000 ;;
Real: 00:00:11.039, CPU: 00:00:11.029, GC gen0: 771, gen1: 3, gen2: 1
val it : unit = ()

используя ключевое слово inline на Eq_cmp

perf_run 10000000 ;;
Real: 00:00:01.319, CPU: 00:00:01.388, GC gen0: 1, gen1: 1, gen2: 1
val it : unit = ()
> 

Я также заметил огромную разницу в размере Gen 0 GC с встроенным кодом и не встроенным кодом.

Может ли кто-нибудь объяснить, почему существует такая огромная разница?

4b9b3361

Ответ 1

Я могу воспроизвести поведение на моей машине с повышением производительности 3 раза после добавления ключевого слова inline.

Декомпиляция двух версий бок о бок в ILSpy дает почти идентичный код С#. Заметное различие заключается в двух тестах равенства:

// Version without inline
bool IEqualityComparer<Program.Pair<a>>.System-Collections-Generic-IEqualityComparer(Program.Pair<a> x, Program.Pair<a> y)
{
    a [email protected] = [email protected];
    a [email protected] = [email protected];
    if (LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>([email protected], [email protected]))
    {
        a [email protected] = [email protected];
        a [email protected] = [email protected];
        return LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>([email protected], [email protected]);
    }
    return false;
}

// Version with inline
bool IEqualityComparer<Program.Pair<int>>.System-Collections-Generic-IEqualityComparer(Program.Pair<int> x, Program.Pair<int> y)
{
    int [email protected] = [email protected];
    int [email protected] = [email protected];
    if ([email protected] == [email protected])
    {
        int [email protected] = [email protected];
        int [email protected] = [email protected];
        return [email protected] == [email protected];
    }
    return false;
}

Общее равенство гораздо менее эффективно, чем специализированная версия.

Я также заметил огромную разницу в размере Gen 0 GC с встроенным кодом и не встроенным кодом.

Может ли кто-нибудь объяснить, почему существует такая огромная разница?

Взгляд на функцию GenericEqualityIntrinsic в F # исходный код:

let rec GenericEqualityIntrinsic (x : 'T) (y : 'T) : bool = 
    fsEqualityComparer.Equals((box x), (box y))

Он делает бокс по аргументам, что объясняет значительное количество мусора в вашем первом примере. Когда GC входит в игру слишком часто, это значительно замедлит вычисления. Второй пример (используя inline) почти не мусор, когда Pair является структурой.

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

Вам может быть интересен очень похожий поток Почему этот код F # настолько медленный?.

Ответ 2

Тип специализации

Без inline вы используете общее сравнение, которое очень неэффективно. С помощью inline общая стираемость удаляется, а сравнение int используется напрямую.