【发布时间】:2011-03-15 14:12:53
【问题描述】:
自从 F# 2.0 成为 VS2010 的一部分后,我对 F# 产生了兴趣。我想知道使用它有什么意义。我读了一点,我做了一个基准来衡量函数调用。 我用过阿克曼函数:)
C#
sealed class Program
{
public static int ackermann(int m, int n)
{
if (m == 0)
return n + 1;
if (m > 0 && n == 0)
{
return ackermann(m - 1, 1);
}
if (m > 0 && n > 0)
{
return ackermann(m - 1, ackermann(m, n - 1));
}
return 0;
}
static void Main(string[] args)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Console.WriteLine("C# ackermann(3,10) = " + Program.ackermann(3, 10));
stopWatch.Stop();
Console.WriteLine("Time required for execution: " + stopWatch.ElapsedMilliseconds + "ms");
Console.ReadLine();
}
}
C++
class Program{
public:
static inline int ackermann(int m, int n)
{
if(m == 0)
return n + 1;
if (m > 0 && n == 0)
{
return ackermann(m - 1, 1);
}
if (m > 0 && n > 0)
{
return ackermann(m - 1, ackermann(m, n - 1));
}
return 0;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
clock_t start, end;
start = clock();
std::cout << "CPP: ackermann(3,10) = " << Program::ackermann(3, 10) << std::endl;
end = clock();
std::cout << "Time required for execution: " << (end-start) << " ms." << "\n\n";
int i;
std::cin >> i;
return 0;
}
F#
// Ackermann
let rec ackermann m n =
if m = 0 then n + 1
elif m > 0 && n = 0 then ackermann (m - 1) 1
elif m > 0 && n > 0 then ackermann (m - 1) (ackermann m (n - 1))
else 0
open System.Diagnostics;
let stopWatch = Stopwatch.StartNew()
let x = ackermann 3 10
stopWatch.Stop();
printfn "F# ackermann(3,10) = %d" x
printfn "Time required for execution: %f" stopWatch.Elapsed.TotalMilliseconds
Java
public class Main
{
public static int ackermann(int m, int n)
{
if (m==0)
return n + 1;
if (m>0 && n==0)
{
return ackermann(m - 1,1);
}
if (m>0 && n>0)
{
return ackermann(m - 1,ackermann(m,n - 1));
}
return 0;
}
public static void main(String[] args)
{
System.out.println(Main.ackermann(3,10));
}
}
然后
C# = 510 毫秒
c++ = 130 毫秒
F# = 185 毫秒
Java = Stackoverflow :)
如果我们想使用 .Net 并获得更快的执行速度,是不是 F# 的力量(除了少量代码)?我可以优化任何这些代码(尤其是 F#)吗?
更新。我摆脱了 Console.WriteLine 并在没有调试器的情况下运行 C# 代码:C# = 400ms
【问题讨论】:
-
在运行基准测试之前执行一次您的方法。您的时间包括 JIT 中间语言所花费的时间。此外,在基准测试之外采用 Console.WriteLine() 等方法,因为它们非常慢。
-
"在运行基准测试之前执行一次你的方法。你的时间已经包括了 JIT 中间语言的时间。" 是吗?我添加了 let dd = ackermann 3 10 并给了我 7 毫秒的额外时间。对于 C# 没有改变 :) “另外,在基准测试之外采用 Console.WriteLine() 之类的方法,因为它们非常慢。”好主意,但没有加快速度
-
是的,F# 和 C# 都是 JIT 编译的 - 运行该方法两次并使用第二个基准测试。原因是 JIT 编译器针对您的处理器的特定功能(CISC 计算优势)优化机器代码
-
为什么不在基准测试中包含第一次执行?我们真的可以忽略 JIT 吗?这不是作弊吗?
-
并非如此。您可以对应用程序进行 prejit,如果应用程序运行一个小时 - 第一个方法调用将根本不相关。