Потрясающая разница в производительности между почти равными методами - PullRequest
2 голосов
/ 12 января 2012

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

type Stream () =
    static member private write (x, o, a : byte[]) = (for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256)); 4
    static member private format f x l = Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a)
    static member private format1 f x l o = Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a)
    static member Format (value : int) =  Stream.format (fun (x: int, i, a) -> Stream.write(x, i, a)) value 4
    static member Format1 (value : int) =  Stream.format1 (fun (x: int, i, a) -> Stream.write(x, i, a)) value 4

При тестировании, Stream.Format1 работает намного быстрее, чем Stream.Format, хотя единственная разница между закрытыми членами Stream.format и Stream.format1 заключается только в аргументе o, который, кроме того, не используется самим методом.

Как компилятор так по-разному относится к двум почти одинаковым методам?

РЕДАКТИРОВАТЬ: спасибо за объяснение и извините за невежество.

Ответы [ 2 ]

8 голосов
/ 12 января 2012

Проблема в том, что когда вы вызываете Format1 только с одним аргументом, он возвращает только функцию. Это еще не делает фактическое форматирование. Это означает, что если вы сравните производительность:

Stream.Format 42
Stream.Format1 42

... тогда вы фактически сравниваете производительность фактического форматирования (который создает массив и что-то записывает в него) в первом случае и производительность кода, который просто возвращает значение функции, ничего не делая.

Если вы ни для чего не используете параметр o, равный format1, то вы можете просто передать какое-то фиктивное значение, чтобы реально оценить функцию и получить результат. Тогда вы должны получить аналогичную производительность:

Stream.Format 42
Stream.Format1 42 ()
4 голосов
/ 12 января 2012

Format фактически вызывает Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a).

Format1 возвращает функцию, которая при передаче объекта вызывает Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a).

Т.е., один выполняет реальную работу, другой - лишь частичное применение функции; последнее, очевидно, быстрее.

Если вы не знакомы с частичным применением функций, в документации F # под названием «Частичное применение аргументов» есть раздел, который стоит прочитать: Функции (F #)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...