如何解决F#性能比例如Java我在做什么错了?
我目前正在尝试使用F#进行编程。因此,我编写了一个简单的F#脚本,以非常愚蠢的方式计算PI。我已经用许多编程语言编写了该算法,但是这次它的执行速度很慢,而且我不知道自己在做什么错。 与我的Java版本相比,这要慢10到20倍(在我按ALT键和Enter键以VS代码运行的计算机上,大约10-15瑞典克朗),这很多(我认为)。
算法的想法是将飞镖扔在一个1乘1的正方形中,其中有一个圆。圆接触正方形的边缘。如果飞镖击中了圆圈,它将被计算在内。投掷所有飞镖后,您只需将飞镖命中数除以飞镖总数,再乘以4即可得到PI。
我已经尝试了很多方法来得到我的错误,但是找不到它。
- 我试图用固定值替换随机数生成->没做多少
- 我试图用恒定值替换Math.Sqrt->没做多少
- 我希望将Seq.xxx调用替换为Array,同时希望这样做可以减少开销。 ->做得不多
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.0秒
因此在这两者之间是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代码进行比较,因为可能会有一些细微的逻辑更改会影响性能。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。