与例如相比,F# 性能较差Java。我究竟做错了什么?
F# performance bad compared to e.g. Java. What am I doing wrong?
我目前正在尝试使用 F# 进行编程。因此,我编写了一个简单的 F# 脚本来以一种非常愚蠢的方式计算圆周率。我已经用多种编程语言编写了这个算法,但这次执行起来很慢,我不知道我做错了什么。
与我的 Java 版本相比,这个版本慢了大约 10-20 倍(在我的机器 运行 上按 Alt+Enter 在我的机器上 运行 大约慢了 10-15 秒)这是很多(在我看来) .
该算法的想法是将飞镖投向一个 1 x 1 的正方形,其中有一个圆圈。圆接触正方形的边缘。如果飞镖击中圆圈,它就会被计算在内。投完所有飞镖后,您只需将飞镖命中数除以飞镖总数再乘以 4 即可得到 PI。
我已经尝试了很多方法来解决我的错误,但找不到它。
- 我尝试用固定值替换随机数生成 -> 没做什么
- 我尝试将 Math.Sqrt 替换为常量值 -> 没有做太多事情
- 我用数组替换了 Seq.xxx 调用,同时希望这会减少开销。 -> 没做什么
PS:我知道这种计算 Pi 的方法很糟糕。但这不是我在这里要表达的重点。
open System
let random = Random(DateTime.Now.Millisecond)
let throwDart i = (random.NextDouble(), random.NextDouble())
let distance dart point = let (x1, y1), (x2, y2) = dart, point
(x2 - x1, y2 - y1)
let length distance = let (x, y) = distance
Math.Sqrt(x * x + y * y)
let isInside circle dartHit =
let (center, radius) = circle
distance center dartHit |> length |> (fun x -> x < radius)
let circle = ((0.5, 0.5), 0.5)
let dartCount = 100000000
let dartSeqence = Seq.init dartCount throwDart
let pi = float(dartSeqence |> Seq.filter (isInside circle) |> Seq.length) / float(dartCount) * 4.0
也许有人知道我做错了什么。我希望 F# 能够很好地完成这项任务,因为它是一种非常简单直接的算法。
提前感谢您的帮助。
更新 1:
嗨,在 运行 我的 Java 代码再次出现后,我有点失望,因为我认为它更快。这是大约的代码 运行。在我的机器上 4.4 秒:
import java.util.Random;
public class DartThrower{
Random random;
public DartThrower(){
random = new Random();
}
public boolean throwDart(){
double x = random.nextDouble();
double y = random.nextDouble();
// wenn Entfernung des Punktes vom Mittelpunkt (0.5|0.5) größer als 0.5 ist wird false ausgegeben
boolean inTheCircle = Math.sqrt((0.5 - x) * (0.5 - x) + (0.5 - y) * (0.5 - y)) <= 0.5;
return inTheCircle;
}
}
class PiCalculator{
public static void main(String[] args){
long start = System.currentTimeMillis();
double hitCount = 0.0;
double throwCount = 100000000L;
DartThrower thrower = new DartThrower();
for (long i = 0; i < throwCount; i++){
boolean hit = thrower.throwDart();
hitCount += hit ? 1 : 0;
}
double a = hitCount / throwCount;
double pi = a * 4.0;
System.out.println("Pi= " + pi);
System.out.println("took " + (System.currentTimeMillis() - start) + "ms");
}
}
抱歉,我认为时间不到 1 秒 - 这就是为什么我说的是执行速度提高 10-20 倍。
那是不对的!在我重新确定我在时间上有多么错误之后,我决定为两者添加一些时间戳并得到
- Java:大约。 4.3秒
- F#:大约。 13.0s
所以这两者之间的因数为 3-4。如果我尝试使用@Tomas Petricek 的性能更新,这种差异可能会过时。我会试试看,然后告诉你结果。
感谢您迄今为止的帮助。
通过用一个简单的可变变量和一个 for
循环替换惰性序列,您可以将其速度提高约 2 倍(这在性能关键的 F# 代码中没有什么不好):
let mutable count = 0
for i in 0 .. dartCount do
if isInside circle (throwDart i) then count <- count + 1
let pi = float count / float dartCount * 4.0
您可以通过制作函数 inline
来增加一点性能(在我的粗略测试中大约增加 30%),这可能会消除一些值作为元组的装箱:
let inline distance dart point =
let (x1, y1), (x2, y2) = dart, point
(x2 - x1, y2 - y1)
let inline length distance =
let (x, y) = distance
Math.Sqrt(x * x + y * y)
let inline isInside circle dartHit =
let (center, radius) = circle
length (distance center dartHit) < radius
有了这个,我没有看到任何明显的问题(但我可能遗漏了一些东西)。很高兴看到您的 Java 代码进行比较,因为可能会有一些影响性能的细微逻辑变化。
我目前正在尝试使用 F# 进行编程。因此,我编写了一个简单的 F# 脚本来以一种非常愚蠢的方式计算圆周率。我已经用多种编程语言编写了这个算法,但这次执行起来很慢,我不知道我做错了什么。 与我的 Java 版本相比,这个版本慢了大约 10-20 倍(在我的机器 运行 上按 Alt+Enter 在我的机器上 运行 大约慢了 10-15 秒)这是很多(在我看来) .
该算法的想法是将飞镖投向一个 1 x 1 的正方形,其中有一个圆圈。圆接触正方形的边缘。如果飞镖击中圆圈,它就会被计算在内。投完所有飞镖后,您只需将飞镖命中数除以飞镖总数再乘以 4 即可得到 PI。
我已经尝试了很多方法来解决我的错误,但找不到它。
- 我尝试用固定值替换随机数生成 -> 没做什么
- 我尝试将 Math.Sqrt 替换为常量值 -> 没有做太多事情
- 我用数组替换了 Seq.xxx 调用,同时希望这会减少开销。 -> 没做什么
PS:我知道这种计算 Pi 的方法很糟糕。但这不是我在这里要表达的重点。
open System
let random = Random(DateTime.Now.Millisecond)
let throwDart i = (random.NextDouble(), random.NextDouble())
let distance dart point = let (x1, y1), (x2, y2) = dart, point
(x2 - x1, y2 - y1)
let length distance = let (x, y) = distance
Math.Sqrt(x * x + y * y)
let isInside circle dartHit =
let (center, radius) = circle
distance center dartHit |> length |> (fun x -> x < radius)
let circle = ((0.5, 0.5), 0.5)
let dartCount = 100000000
let dartSeqence = Seq.init dartCount throwDart
let pi = float(dartSeqence |> Seq.filter (isInside circle) |> Seq.length) / float(dartCount) * 4.0
也许有人知道我做错了什么。我希望 F# 能够很好地完成这项任务,因为它是一种非常简单直接的算法。
提前感谢您的帮助。
更新 1:
嗨,在 运行 我的 Java 代码再次出现后,我有点失望,因为我认为它更快。这是大约的代码 运行。在我的机器上 4.4 秒:
import java.util.Random;
public class DartThrower{
Random random;
public DartThrower(){
random = new Random();
}
public boolean throwDart(){
double x = random.nextDouble();
double y = random.nextDouble();
// wenn Entfernung des Punktes vom Mittelpunkt (0.5|0.5) größer als 0.5 ist wird false ausgegeben
boolean inTheCircle = Math.sqrt((0.5 - x) * (0.5 - x) + (0.5 - y) * (0.5 - y)) <= 0.5;
return inTheCircle;
}
}
class PiCalculator{
public static void main(String[] args){
long start = System.currentTimeMillis();
double hitCount = 0.0;
double throwCount = 100000000L;
DartThrower thrower = new DartThrower();
for (long i = 0; i < throwCount; i++){
boolean hit = thrower.throwDart();
hitCount += hit ? 1 : 0;
}
double a = hitCount / throwCount;
double pi = a * 4.0;
System.out.println("Pi= " + pi);
System.out.println("took " + (System.currentTimeMillis() - start) + "ms");
}
}
抱歉,我认为时间不到 1 秒 - 这就是为什么我说的是执行速度提高 10-20 倍。 那是不对的!在我重新确定我在时间上有多么错误之后,我决定为两者添加一些时间戳并得到
- Java:大约。 4.3秒
- F#:大约。 13.0s
所以这两者之间的因数为 3-4。如果我尝试使用@Tomas Petricek 的性能更新,这种差异可能会过时。我会试试看,然后告诉你结果。
感谢您迄今为止的帮助。
通过用一个简单的可变变量和一个 for
循环替换惰性序列,您可以将其速度提高约 2 倍(这在性能关键的 F# 代码中没有什么不好):
let mutable count = 0
for i in 0 .. dartCount do
if isInside circle (throwDart i) then count <- count + 1
let pi = float count / float dartCount * 4.0
您可以通过制作函数 inline
来增加一点性能(在我的粗略测试中大约增加 30%),这可能会消除一些值作为元组的装箱:
let inline distance dart point =
let (x1, y1), (x2, y2) = dart, point
(x2 - x1, y2 - y1)
let inline length distance =
let (x, y) = distance
Math.Sqrt(x * x + y * y)
let inline isInside circle dartHit =
let (center, radius) = circle
length (distance center dartHit) < radius
有了这个,我没有看到任何明显的问题(但我可能遗漏了一些东西)。很高兴看到您的 Java 代码进行比较,因为可能会有一些影响性能的细微逻辑变化。