gpt4 book ai didi

f# - 使用柯里化(Currying)会导致 F# 的性能下降吗?

转载 作者:行者123 更新时间:2023-12-04 00:34:13 24 4
gpt4 key购买 nike

在编写可以接受柯里化(Currying)的函数时,可以将其编写为返回函数的单参数函数。例如,

let add x =
let inner y = x + y
inner

所以你可以这样做:
add 3 4

或者:
let add3 = add 3
add3 4

我的问题是因为你返回一个函数,你在概念上调用了一个函数两次(外部函数和内部函数)。这比:
let add x y = x + y

或者编译器是否优化了 add 3 4 的调用?在 curry 定义中?

最佳答案

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
val f: int -> int -> int
// Actually is
// val f: int -> (int -> int)
// ie f is a function that takes a single int and returns a function that takes a single int and returns an int.

为了让 F# 在 .NET 上更快地执行, f 的物理表示在一个程序集中是:
public static int f(int x, int y)

虽然这是 F# 函数的更自然表示。
public static Func<int, int> f(int x)

虽然会表现不佳。

通常 F# 足够聪明,可以通过上述优化和调用来避免抽象的开销。但是,在某些情况下 F# 无法为您优化。

假设您正在实现 fold
let rec fold f s vs =
match vs with
| v::vs -> fold f (f s v) vs
| [] -> s

这里 F# 不能完全优化 f s v .原因是 f可能有一个比上面更复杂的实现,它可能会根据 s 返回不同的函数.

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

这就是人们有时会看到 fold 的原因。像这样写:
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
Adapt这里在循环之前测试 if f确实可以优化,然后返回一个高效的适配器。在一般情况下,它可能仍然会慢一些,但这就是调用者的意图。

笔记;对于像 'T -> 'U 这样的简单函数值,这种潜在的性能下降不会发生。 .这总是可以有效地调用。

希望这可以帮助。

关于f# - 使用柯里化(Currying)会导致 F# 的性能下降吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53527210/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com