F# 使用currying是否会导致F中的性能降低?

F# 使用currying是否会导致F中的性能降低?,f#,currying,F#,Currying,在编写可以接受curry的函数时,可以将其编写为返回函数的单参数函数。比如说, let add x = let inner y = x + y inner 因此,您可以: add 3 4 或: 我的问题是,因为你返回一个函数,你在概念上调用一个函数,是外部函数和内部函数的两倍。这比: let add x y = x + y 或者编译器是否优化了当前定义中add 3 4的调用?我在中对此进行了测试 当关闭编译器优化时,F编译器将为每个代码段生成不同的IL。换句话说,如果有任何

在编写可以接受curry的函数时,可以将其编写为返回函数的单参数函数。比如说,

let add x =
    let inner y = x + y
    inner
因此,您可以:

add 3 4
或:

我的问题是,因为你返回一个函数,你在概念上调用一个函数,是外部函数和内部函数的两倍。这比:

let add x y = x + y
或者编译器是否优化了当前定义中add 3 4的调用?

我在中对此进行了测试

当关闭编译器优化时,F编译器将为每个代码段生成不同的IL。换句话说,如果有任何优化正在进行,则由抖动决定,调用第一个表单可能会非常慢

然而,当编译器优化被开启时,两种形式在我所能想到的每一个测试场景中都会产生相同的IL输出。事实上,对于这两种形式,调用:

add 3 4
生成与硬编码7相当的IL,并优化整个函数调用:

ldc.i4.7
换句话说,F编译器在优化逻辑上相同的代码块时非常彻底

当然,这并不是一个详尽的答案,在某些情况下,编译器可能会对它们进行不同的处理

let f x   = fun y -> x + y
let g x y = x + y
在dnSpy中查看这些函数定义以获得优化的构建,结果显示它们是:

public static int f(int x, int y)
{
    return x + y;
}

public static int g(int x, int y)
{
    return x + y;
}
这并不奇怪,因为g实际上是f的简写定义,这是一般情况。在类F语言中,函数在概念上总是接受一个值并返回一个值。值可能是函数。这更容易看出是否存在f和g的函数签名

为了使F在.NET上更快地执行,程序集中F的物理表示形式为:

public static int f(int x, int y)
而这是F函数更自然的表示形式

public static Func<int, int> f(int x)
这里F不能完全优化fsv。原因是f可能有一个比上面更复杂的实现,它可能根据s返回不同的函数

若您查看dnSpy,您会注意到F正在使用InvokeFast调用函数,但这会进行内部测试,以查看是否可以快速调用该函数。在fold中,我们对每个值进行测试,即使这是相同的函数

public static Func<int, int> f(int x)
这就是人们有时会看到这样写的原因:

let fold f s vs =
  let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt f
  let rec loop s vs =
    match vs with
    | v::vs -> loop (f.Invoke (s, v)) vs
    | []    -> s
  loop s vs
如果f确实可以优化,则在循环之前进行Adapt here测试,然后返回一个有效的适配器。在一般情况下,它可能仍然有点慢,但这是调用者的意图

注;对于像't->'U这样的简单函数值,这种潜在的性能下降不会发生。这始终可以有效地调用


希望这能有所帮助。

Oops!我是凭记忆打字的。我会编辑
let rec fold f s vs =
  match vs with
  | v::vs -> fold f (f s v) vs
  | []    -> s
let fold f s vs =
  let f = OptimizedClosures.FSharpFunc<_, _, _>.Adapt f
  let rec loop s vs =
    match vs with
    | v::vs -> loop (f.Invoke (s, v)) vs
    | []    -> s
  loop s vs