为什么 F# 比 C# 慢这么多?(质数基准)

我认为 F# 应该比 C# 更快,我制作了一个可能很糟糕的基准测试工具,C# 得到了 16239 毫秒,而 F# 在 49583 毫秒时表现更差。有人能解释一下这是为什么吗?我正在考虑离开 F# 并回到 C#。是否可以使用更快的代码在 F# 中获得相同的结果?


这是我使用的代码,我尽可能地使它相等。


F#(49583 毫秒)


open System

open System.Diagnostics


let stopwatch = new Stopwatch()

stopwatch.Start()


let mutable isPrime = true


for i in 2 .. 100000 do

    for j in 2 .. i do

        if i <> j && i % j = 0 then

            isPrime <- false

    if isPrime then

        printfn "%i" i

    isPrime <- true


stopwatch.Stop()

printfn "Elapsed time: %ims" stopwatch.ElapsedMilliseconds


Console.ReadKey() |> ignore

C#(16239 毫秒)


using System;

using System.Diagnostics;


namespace ConsoleApp1

{

    class Program

    {

        static void Main(string[] args)

        {

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();


            bool isPrime = true;


            for (int i = 2; i <= 100000; i++)

            {

                for (int j = 2; j <= i; j++)

                {

                    if (i != j && i % j == 0)

                    {

                        isPrime = false;

                        break;

                    }

                }

                if (isPrime)

                {

                    Console.WriteLine(i);

                }

                isPrime = true;

            }

            stopwatch.Stop();

            Console.WriteLine("Elapsed time: " + stopwatch.ElapsedMilliseconds + "ms");

            Console.ReadKey();

        }

    }

}


慕田峪4524236
浏览 182回答 3
3回答

慕桂英4014372

F# 程序较慢,因为您的程序不等效。您的 C# 代码break在内for循环中有一条语句,但您的 F# 程序没有。因此,对于每个偶数,C# 代码将在检查可被 2 整除后停止,而 F# 程序将检查从 2 到i. 由于完成的工作有如此大的差异,F# 代码仅慢了三倍实际上令人惊讶!现在,F# 故意没有break语句,因此您不能完全将 C# 代码直接转换为 F#。但是您可以使用包含短路逻辑的函数。例如:{2 .. 100000}|> Seq.filter (fun i -> {2 .. i-1} |> Seq.forall (fun j -> i % j <> 0))|> Seq.iter (printfn "%i")这使用Seq.forall,它确实做了短路:它将根据条件检查序列中的每个输入,如果条件返回false,它将停止并且不再进行检查。(因为Seq模块中的函数是惰性的,并且不会做比获得输出绝对需要的更多的工作)。这就像break在你的 C# 代码中有一个。我将逐步完成此操作,以便您了解它是如何工作的:{2 .. 100000}这将创建一个惰性整数序列,从 2 开始并上升到(并包括)100000。|> Seq.filter (fun i -> (some expression involving i))我将下一行分为两部分:外部Seq.filter部分和涉及i. 该Seq.filter部分获取序列并对其进行过滤:对于序列中的每个项目,调用它i并评估表达式。如果该表达式的计算结果为true,则保留该项目并将其传递到链中的下一步。如果表达式为false,则丢弃该项目。现在,涉及的表达式i是:{2 .. i-1} |> Seq.forall (fun j -> i % j <> 0)这首先构造了一个从 2 开始一直到i负 1的惰性序列。(或者您可以将其视为从 2 开始并上升到i,但不包括i)。然后检查该序列的每个项目是否满足特定条件(即Seq.forall函数)。并且,作为 的实现细节Seq.forall,因为它是懒惰的并且不做绝对必要的工作,所以它在找到false结果的那一刻将停止并且不再通过输入序列。(因为一旦你找到一个反例,forall函数就不可能再返回真了,所以一旦知道结果,它就会停止。)被检入的表达式是Seq.forall什么?它是fun j -> i % j <> 0。所以j是内部循环变量,i是外部变量(在Seq.filter部分中分配的变量),逻辑与您的C#循环相同。现在,请记住我们是在一个Seq.filter这里。因此,如果Seq.forall返回 true,Seq.filter则将保留 的值i。但是如果Seq.forall返回false,Seq.filter则将丢弃这个值i从传递到下一步。最后,我们将这一行作为下一步(也是最后一步):|> Seq.iter (printfn "%i")这与以下内容几乎完全相同:for number in inputSoFar do    printfn "%i" number(printfn "%i")如果您不熟悉 F#,该调用可能看起来不寻常。这是currying,这是一个非常有用的概念,而且很重要的是要习惯它。所以花点时间思考一下:在 F# 中,以下两行代码是完全等价的:(fun y -> someFunctionCall x y)(someFunctionCall x)所以fun item -> printfn "%i" item总是可以替换为printfn "%i. 并且Seq.iter相当于一个for循环:inputSoFar |> Seq.iter (someFunctionCall x)完全等同于:for item in inputSoFar do    someFunctionCall x item所以你知道了:为什么你的 F# 程序更慢,以及如何编写一个 F# 程序,该程序将遵循与 C# 相同的逻辑,但在其中包含等效的break语句。

千万里不及你

这些年来做了很多 C#,但 F# 并不多。以下将更等效于 C# 代码。open Systemopen System.Diagnosticslet stopwatch = new Stopwatch()stopwatch.Start()let mutable loop = truefor i in 2 .. 100000 do    let mutable j = 2    while loop do        if i <> j && i % j = 0 then            loop <- false        else            j <- j + 1            if j >= i then                printfn "%i" i                loop <- false    loop <- truestopwatch.Stop()printfn "Elapsed time: %ims" stopwatch.ElapsedMilliseconds它还得出了惊人的相似 IL。

jeck猫

正如其他人提到的,代码没有做同样的事情,您需要采用技术来确保在找到素数后停止内部循环。此外,您正在将值打印为标准输出。当您进行 CPU 性能测试时,这通常是不希望的,因为大量时间可能是 I/O 歪曲测试结果。无论如何,即使有一个公认的答案,我还是决定对此进行一些修补,以查看将不同的建议解决方案与我自己的一些解决方案进行比较。性能运行x64在 .NET 4.7.1 模式下。我比较了不同的建议 F# 解决方案以及我自己的一些变体:Running 'Original(F#)' with 100000 (10512)...&nbsp; ... it took 14533 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Original(C#)' with 100000 (10512)...&nbsp; ... it took 1343 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Aaron' with 100000 (10512)...&nbsp; ... it took 5027 ms with (3, 1, 0) cc and produces 9592 GOOD primesRunning 'SteveJ' with 100000 (10512)...&nbsp; ... it took 1640 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Dumetrulo1' with 100000 (10512)...&nbsp; ... it took 1908 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Dumetrulo2' with 100000 (10512)...&nbsp; ... it took 970 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Simple' with 100000 (10512)...&nbsp; ... it took 621 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'PushStream' with 100000 (10512)...&nbsp; ... it took 1627 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Unstalling' with 100000 (10512)...&nbsp; ... it took 551 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'Vectors' with 100000 (10512)...&nbsp; ... it took 1076 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'VectorsUnstalling' with 100000 (10512)...&nbsp; ... it took 1072 ms with (0, 0, 0) cc and produces 9592 GOOD primesRunning 'BestAttempt' with 100000 (10512)...&nbsp; ... it took 4 ms with (0, 0, 0) cc and produces 9592 GOOD primes&nbsp;&nbsp;Original(F#)&nbsp;- OP 的原始 F# 代码改为不使用 stdoutOriginal(C#)&nbsp;- OP 的原始 C# 代码改为不使用 stdoutAaron- 使用的惯用方法Seq。正如预期的那样Seq,性能通常不会很好地结合在一起。SteveJ&nbsp;- @SteveJ 试图在 F# 中模仿 C# 代码Dumetrulo1&nbsp;- @dumetrulo 在尾递归中实现了算法Dumetrulo2&nbsp;- @dumetrulo 通过步进 +2 而不是 +1 来改进算法(不需要检查偶数)。Simple- 我尝试使用Dumetrulo2与尾递归类似的方法。PushStream- 我尝试使用简单的推流(Seq是拉流)Unstalling&nbsp;- 如果使用的指令有延迟,我尝试尝试使 CPU 停止运行Vectors- 我尝试使用System.Numerics.Vectors每个操作(又名 SIMD)进行多个除法。不幸的是,vector libary 不支持,mod所以我不得不模仿它。VectorsUnstalling- 我试图Vectors通过尝试停止 CPU来改进。BestAttempt- 喜欢Simple但只sqrt n在确定是否为素数时检查数字。包起来F# 循环没有也continue没有break.&nbsp;F# 中的尾递归是 IMO 实现需要break.在比较语言的性能时,应该比较最佳性能还是比较惯用解决方案的性能?我个人认为最好的性能是正确的方法,但我知道人们不同意我的意见(我写了一个mandelbrot 版本来对 F#的游戏进行基准测试,其性能与 C 相当,但它没有被接受,因为这种风格被视为非-F# 的惯用语)。Seq不幸的是,在 F# 中会增加大量开销。即使开销不相关,我也很难让自己使用它。现代 CPU 指令具有不同的吞吐量和延迟数量。这意味着有时为了提高性能,需要在内循环中处理多个独立样本,以允许乱序执行单元重新排序程序以隐藏延迟。如果您的 CPU 具有超线程并且您在多个线程上运行算法,则超线程可以“自动”减轻延迟。mod向量的缺乏阻止了使用 SIMD 来获得优于非 SIMD 解决方案的任何性能的尝试。如果我修改Unstalling尝试以与 C# 代码相同的次数循环,则最终结果1100 ms在 F# 中与1343 ms在 C# 中相比。因此,F# 可以与 C# 非常相似地运行。如果应用更多的技巧,它只需要,4 ms但对于 C# 也是一样的。无论如何,从几乎15 sec到4 ms.希望它对某人很有趣完整源代码:module Common =&nbsp;&nbsp; open System&nbsp; open System.Diagnostics&nbsp; let now =&nbsp; &nbsp; let sw = Stopwatch ()&nbsp; &nbsp; sw.Start ()&nbsp; &nbsp; fun () -> sw.ElapsedMilliseconds&nbsp; let time i a =&nbsp; &nbsp; let inline cc i&nbsp; &nbsp; &nbsp; &nbsp;= GC.CollectionCount i&nbsp; &nbsp; let ii = i ()&nbsp; &nbsp; GC.Collect (2, GCCollectionMode.Forced, true)&nbsp; &nbsp; let bcc0, bcc1, bcc2&nbsp; = cc 0, cc 1, cc 2&nbsp; &nbsp; let b&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= now ()&nbsp; &nbsp; let v = a ii&nbsp; &nbsp; let e = now ()&nbsp; &nbsp; let ecc0, ecc1, ecc2&nbsp; = cc 0, cc 1, cc 2&nbsp; &nbsp; v, (e - b), ecc0 - bcc0, ecc1 - bcc1, ecc2 - bcc2&nbsp; let limit&nbsp; &nbsp; = 100000&nbsp; // pi(x) ~= limit/(ln limit - 1)&nbsp; // Using pi(x) ~= limit/(ln limit - 2) to over-estimate&nbsp; let estimate = float limit / (log (float limit) - 1.0 - 1.0) |> round |> intmodule Original =&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; let mutable isPrime = true&nbsp; &nbsp; for i in 2 .. limit do&nbsp; &nbsp; &nbsp; for j in 2 .. i do&nbsp; &nbsp; &nbsp; &nbsp; if i <> j && i % j = 0 then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isPrime <- false&nbsp; &nbsp; &nbsp; if isPrime then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrime <- true&nbsp; &nbsp; ra.ToArray ()module SolutionAaron =&nbsp; let primes limit =&nbsp; &nbsp; {2 .. limit}&nbsp; &nbsp; |> Seq.filter (fun i -> {2 .. i-1} |> Seq.forall (fun j -> i % j <> 0))&nbsp; &nbsp; |> Seq.toArraymodule SolutionSteveJ =&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; let mutable loop = true&nbsp; &nbsp; for i in 2 .. limit do&nbsp; &nbsp; &nbsp; &nbsp; let mutable j = 2&nbsp; &nbsp; &nbsp; &nbsp; while loop do&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if i <> j && i % j = 0 then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; loop <- false&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; j <- j + 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if j >= i then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; loop <- false&nbsp; &nbsp; &nbsp; &nbsp; loop <- true&nbsp; &nbsp; ra.ToArray ()module SolutionDumetrulo1 =&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i j limit =&nbsp; &nbsp; if i > limit then ra.ToArray ()&nbsp; &nbsp; elif j > i then&nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 1) 2 limit&nbsp; &nbsp; elif i <> j && i % j = 0 then&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 1) 2 limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; isPrimeLoop ra i (j + 1) limit&nbsp; let primes limit =&nbsp; &nbsp; isPrimeLoop (ResizeArray Common.estimate) 2 2 limitmodule SolutionDumetrulo2 =&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i j limit =&nbsp; &nbsp; let incr x = if x = 2 then 3 else x + 2&nbsp; &nbsp; if i > limit then ra.ToArray ()&nbsp; &nbsp; elif j > i then&nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (incr i) 2 limit&nbsp; &nbsp; elif i <> j && i % j = 0 then&nbsp; &nbsp; &nbsp; isPrimeLoop ra (incr i) 2 limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; isPrimeLoop ra i (incr j) limit&nbsp; let primes limit =&nbsp; &nbsp; isPrimeLoop (ResizeArray Common.estimate) 2 2 limitmodule SolutionSimple =&nbsp; let rec isPrime i j k =&nbsp; &nbsp; if i < k then&nbsp; &nbsp; &nbsp; (j % i) <> 0 && isPrime (i + 2) j k&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; true&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i limit =&nbsp; &nbsp; if i < limit then&nbsp;&nbsp; &nbsp; &nbsp; if isPrime 3 i i then&nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 2) limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; ra.ToArray ()&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; ra.Add 2&nbsp; &nbsp; isPrimeLoop ra 3 limitmodule SolutionPushStream =&nbsp; type Receiver<'T> = 'T -> bool&nbsp; type PushStream<'T> = Receiver<'T> -> bool&nbsp; module Details =&nbsp; &nbsp; module Loops =&nbsp; &nbsp; &nbsp; let rec range e r i =&nbsp; &nbsp; &nbsp; &nbsp; if i <= e then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if r i then&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; range e r (i + 1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; false&nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; true&nbsp; open Details&nbsp; let range s e : PushStream<int> =&nbsp; &nbsp; fun r -> Loops.range e r s&nbsp; let filter p (t : PushStream<'T>) : PushStream<'T> =&nbsp; &nbsp; fun r -> t (fun v -> if p v then r v else true)&nbsp; let forall p (t : PushStream<'T>) : bool =&nbsp; &nbsp; t p&nbsp; let toArray (t : PushStream<'T>) : _ [] =&nbsp; &nbsp; let ra = ResizeArray 16&nbsp; &nbsp; t (fun v -> ra.Add v; true) |> ignore&nbsp; &nbsp; ra.ToArray ()&nbsp; let primes limit =&nbsp; &nbsp; range 2 limit&nbsp; &nbsp; |> filter (fun i -> range 2 (i - 1) |> forall (fun j -> i % j <> 0))&nbsp; &nbsp; |> toArraymodule SolutionUnstalling =&nbsp; let rec isPrime i j k =&nbsp; &nbsp; if i + 6 < k then&nbsp; &nbsp; &nbsp; (j % i) <> 0 && (j % (i + 2)) <> 0 && (j % (i + 4)) <> 0 && (j % (i + 6)) <> 0&nbsp; && isPrime (i + 8) j k&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; true&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i limit =&nbsp; &nbsp; if i < limit then&nbsp;&nbsp; &nbsp; &nbsp; if isPrime 3 i i then&nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 2) limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; ra.ToArray ()&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; ra.Add 2&nbsp; &nbsp; ra.Add 3&nbsp; &nbsp; ra.Add 5&nbsp; &nbsp; ra.Add 7&nbsp; &nbsp; ra.Add 11&nbsp; &nbsp; ra.Add 13&nbsp; &nbsp; ra.Add 17&nbsp; &nbsp; ra.Add 19&nbsp; &nbsp; ra.Add 23&nbsp; &nbsp; isPrimeLoop ra 29 limitmodule SolutionVectors =&nbsp; open System.Numerics&nbsp; assert (Vector<int>.Count = 4)&nbsp; type I4 = Vector<int>&nbsp; let inline (%%) (i : I4) (j : I4) : I4 =&nbsp; &nbsp; i - (j * (i / j))&nbsp; let init : int [] = Array.zeroCreate 4&nbsp; let i4 v0 v1 v2 v3 =&nbsp; &nbsp; init.[0] <- v0&nbsp; &nbsp; init.[1] <- v1&nbsp; &nbsp; init.[2] <- v2&nbsp; &nbsp; init.[3] <- v3&nbsp; &nbsp; I4 init&nbsp; let i4_ (v0 : int) =&nbsp; &nbsp; I4 v0&nbsp; let zero&nbsp; &nbsp; = I4.Zero&nbsp; let one&nbsp; &nbsp; &nbsp;= I4.One&nbsp;&nbsp; let two&nbsp; &nbsp; &nbsp;= one + one&nbsp; let eight&nbsp; &nbsp;= two*two*two&nbsp; let step = i4 3 5 7 9&nbsp; let rec isPrime (i : I4) (j : I4) k l =&nbsp; &nbsp; if l + 6 < k then&nbsp; &nbsp; &nbsp; Vector.EqualsAny (j %% i, zero) |> not && isPrime (i + eight) j k (l + 8)&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; true&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i limit =&nbsp; &nbsp; if i < limit then&nbsp;&nbsp; &nbsp; &nbsp; if isPrime step (i4_ i) i 3 then&nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 2) limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; ra.ToArray ()&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; ra.Add 2&nbsp; &nbsp; ra.Add 3&nbsp; &nbsp; ra.Add 5&nbsp; &nbsp; ra.Add 7&nbsp; &nbsp; ra.Add 11&nbsp; &nbsp; ra.Add 13&nbsp; &nbsp; ra.Add 17&nbsp; &nbsp; ra.Add 19&nbsp; &nbsp; ra.Add 23&nbsp; &nbsp; isPrimeLoop ra 29 limitmodule SolutionVectorsUnstalling =&nbsp; open System.Numerics&nbsp; assert (Vector<int>.Count = 4)&nbsp; type I4 = Vector<int>&nbsp; let init : int [] = Array.zeroCreate 4&nbsp; let i4 v0 v1 v2 v3 =&nbsp; &nbsp; init.[0] <- v0&nbsp; &nbsp; init.[1] <- v1&nbsp; &nbsp; init.[2] <- v2&nbsp; &nbsp; init.[3] <- v3&nbsp; &nbsp; I4 init&nbsp; let i4_ (v0 : int) =&nbsp; &nbsp; I4 v0&nbsp; let zero&nbsp; &nbsp; = I4.Zero&nbsp; let one&nbsp; &nbsp; &nbsp;= I4.One&nbsp;&nbsp; let two&nbsp; &nbsp; &nbsp;= one + one&nbsp; let eight&nbsp; &nbsp;= two*two*two&nbsp; let sixteen = two*eight&nbsp; let step = i4 3 5 7 9&nbsp; let rec isPrime (i : I4) (j : I4) k l =&nbsp; &nbsp; if l + 14 < k then&nbsp; &nbsp; &nbsp; // i - (j * (i / j))&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; let i0 = i&nbsp; &nbsp; &nbsp; let i8 = i + eight&nbsp; &nbsp; &nbsp; let d0 = j / i0&nbsp; &nbsp; &nbsp; let d8 = j / i8&nbsp; &nbsp; &nbsp; let n0 = i0 * d0&nbsp; &nbsp; &nbsp; let n8 = i8 * d8&nbsp; &nbsp; &nbsp; let r0 = j - n0&nbsp; &nbsp; &nbsp; let r8 = j - n8&nbsp; &nbsp; &nbsp; Vector.EqualsAny (r0, zero) |> not && Vector.EqualsAny (r8, zero) |> not && isPrime (i + sixteen) j k (l + 16)&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; true&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i limit =&nbsp; &nbsp; if i < limit then&nbsp;&nbsp; &nbsp; &nbsp; if isPrime step (i4_ i) i 3 then&nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 2) limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; ra.ToArray ()&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; ra.Add 2&nbsp; &nbsp; ra.Add 3&nbsp; &nbsp; ra.Add 5&nbsp; &nbsp; ra.Add 7&nbsp; &nbsp; ra.Add 11&nbsp; &nbsp; ra.Add 13&nbsp; &nbsp; ra.Add 17&nbsp; &nbsp; ra.Add 19&nbsp; &nbsp; ra.Add 23&nbsp; &nbsp; isPrimeLoop ra 29 limitmodule SolutionBestAttempt =&nbsp; let rec isPrime i j k =&nbsp; &nbsp; if i < k then&nbsp; &nbsp; &nbsp; (j % i) <> 0 && isPrime (i + 2) j k&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; true&nbsp; let inline isqrt i = (i |> float |> sqrt) + 1. |> int&nbsp; let rec isPrimeLoop (ra : ResizeArray<_>) i limit =&nbsp; &nbsp; if i < limit then&nbsp;&nbsp; &nbsp; &nbsp; if isPrime 3 i (isqrt i) then&nbsp; &nbsp; &nbsp; &nbsp; ra.Add i&nbsp; &nbsp; &nbsp; isPrimeLoop ra (i + 2) limit&nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; ra.ToArray ()&nbsp; let primes limit =&nbsp; &nbsp; let ra = ResizeArray Common.estimate&nbsp; &nbsp; ra.Add 2&nbsp; &nbsp; isPrimeLoop ra 3 limit[<EntryPoint>]let main argv =&nbsp; let testCases =&nbsp; &nbsp; [|&nbsp; &nbsp; &nbsp; "Original"&nbsp; &nbsp; , Original.primes&nbsp; &nbsp; &nbsp; "Aaron"&nbsp; &nbsp; &nbsp; &nbsp;, SolutionAaron.primes&nbsp; &nbsp; &nbsp; "SteveJ"&nbsp; &nbsp; &nbsp; , SolutionSteveJ.primes&nbsp; &nbsp; &nbsp; "Dumetrulo1"&nbsp; , SolutionDumetrulo1.primes&nbsp; &nbsp; &nbsp; "Dumetrulo2"&nbsp; , SolutionDumetrulo2.primes&nbsp; &nbsp; &nbsp; "Simple"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , SolutionSimple.primes&nbsp; &nbsp; &nbsp; "PushStream"&nbsp; &nbsp; &nbsp; &nbsp; , SolutionPushStream.primes&nbsp; &nbsp; &nbsp; "Unstalling"&nbsp; &nbsp; &nbsp; &nbsp; , SolutionUnstalling.primes&nbsp; &nbsp; &nbsp; "Vectors"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, SolutionVectors.primes&nbsp; &nbsp; &nbsp; "VectorsUnstalling" , SolutionVectors.primes&nbsp; &nbsp; &nbsp; "BestAttempt"&nbsp; &nbsp; &nbsp; &nbsp;, SolutionBestAttempt.primes&nbsp; &nbsp; |]&nbsp; do&nbsp; &nbsp; // Warm-up&nbsp; &nbsp; printfn "Warm up"&nbsp; &nbsp; for _, a in testCases do&nbsp; &nbsp; &nbsp; for i = 0 to 100 do&nbsp; &nbsp; &nbsp; &nbsp; a 100 |> ignore&nbsp; do&nbsp; &nbsp; let init ()&nbsp; &nbsp;= Common.limit&nbsp; &nbsp; let expected&nbsp; = SolutionSimple.primes Common.limit&nbsp; &nbsp; for testCase, a in testCases do&nbsp; &nbsp; &nbsp; printfn "Running '%s' with %d (%d)..." testCase Common.limit Common.estimate&nbsp; &nbsp; &nbsp; let actual, time, cc0, cc1, cc2 = Common.time init a&nbsp; &nbsp; &nbsp; let result = if expected = actual then "GOOD" else "BAD"&nbsp; &nbsp; &nbsp; printfn "&nbsp; ... it took %d ms with (%d, %d, %d) cc and produces %d %s primes" time cc0 cc1 cc2 actual.Length result&nbsp;&nbsp; 0
打开App,查看更多内容
随时随地看视频慕课网APP