今天在我的代码中查看一个函数,我想知道是否可以组合部分组合和优化:
let foo (X:float) y1 y2 dx =
y1 + (y2 - y1) * dx / X
基本上,只需应用一个比率 - 所以前三个参数在给定的循环中通常是相同的。
我想也许如果我这样做了:
let foo2 (X:float) y1 y2 dx =
let dy = (y2 - y1) / X
y1 + dy * dx
当我部分应用前三个参数时,F#会变得聪明并为我优化,但是调试模式看起来并不是这样(虽然我不确定我是否在右边测试它方式)。
问题是,这应该有效吗?如果没有更好的方法(除了用两个参数编写另一个函数)?
答案 0 :(得分:4)
我认为大多数这样的“魔术优化”都需要“效果分析”,这只能通过神秘的“足够聪明的编译器”来完成。
考虑一下:
let Expensive x y =
printfn "I am a side-effect of Expensive"
x + y // imagine something expensive
let F x y z =
let tmp = Expensive x y
z + tmp
printfn "Least chance of magic compiler optimization"
for i in 1..3 do
F 3 4 i
printfn "Slightly better chance"
let Part = F 3 4
for i in 1..3 do
Part i
printfn "Now for real"
let F2 x y =
let tmp = Expensive x y
(fun z -> z + tmp)
printfn "Of course this still re-does it"
for i in 1..3 do
F2 3 4 i
printfn "Of course this finally saves re-do-ing Expensive"
let Opt = F2 3 4
for i in 1..3 do
Opt i
(* output
Least chance of magic compiler optimization
I am a side-effect of Expensive
I am a side-effect of Expensive
I am a side-effect of Expensive
Slightly better chance
I am a side-effect of Expensive
I am a side-effect of Expensive
I am a side-effect of Expensive
Now for real
Of course this still re-does it
I am a side-effect of Expensive
I am a side-effect of Expensive
I am a side-effect of Expensive
Of course this finally saves re-do-ing Expensive
I am a side-effect of Expensive
*)
关键是,关于效果的语言语义要求编译器的行为与此完全相同,除非“昂贵”没有效果且编译器真的很聪明并且可以自己发现它。
答案 1 :(得分:3)
(这不是声誉唠叨,当我开始提问时,老实说我没有想到这一点)
这是我提出的一个解决方案,不确定它是否是最好的:
let foo3 (X:float) y1 y2 =
let dy = (y2 - y1) / X
(fun dx -> y1 + dy * dx)
工作得更快。
答案 2 :(得分:1)
在调试模式下,没有什么明显不同,我并不感到惊讶。为什么不实际重复N次重复(#time;;
在F#交互式提示下。)
至于你希望分享除dx之外的所有固定值的通用计算,试试这个:
let fdx = foo2 X y1 y2
for dx in dxes do
fdx dx
也就是说,fdx是部分应用程序。将它明确地存储在循环之外会让我对优化器有更多的希望。
至少在交互式提示中(我认为没有完全优化在那里,是吗?)看起来我的建议只有15%的加速(很奇怪,有任何加速,因为它肯定重复完整foo2的身体。这样做要快得多:
let fdx = foo3 X y1 y2
for dx in dxes do
fdx dx
foo3来自(来自Benjlol):
let foo3 (X:float) y1 y2 =
let dy = (y2 - y1) / X
(fun dx -> y1 + dy * dx)
请注意,在循环中仅使用foo3作为4参数函数的速度是foo2的两倍,但是将部分应用程序存储在循环外部,速度提高了3倍。