나는 그것을 시도하고 이해하기 위해 Riccardo Terrell의 Akka.NET Fractal 데모 ( https://github.com/rikace/akkafractal )를 약간 엉망으로 만들고 있습니다. (좋아, btw)
나 자신을 위해 사소한 도전으로 시도한 한 가지는 더 기능적인 방법으로 일부를 다시 작성하는 것이 었습니다. 나는 그것을 작동하지만 원본보다 훨씬 느립니다.
다음은 테스트를 위해 조정 된 원래 Mandelbrot Set 계산입니다.
let mandelbrotSet (xp : int) (yp : int) (w : int) (h :int) (width : int) (height : int)
(maxr : float) (minr : float) (maxi : float) (mini : float) : List<int> =
let mutable zx = 0.
let mutable zy = 0.
let mutable cx = 0.
let mutable cy = 0.
let mutable xjump = ((maxr - minr) / ( float width))
let yjump = ((maxi - mini) / (float height))
let mutable tempzx = 0.
let loopmax = 1000
let mutable loopgo = 0
let outputList: int list = List.empty
for x = xp to (xp + w) - 1 do
cx <- (xjump * float x) - abs(minr)
for y = yp to (yp + h) - 1 do
zx <- 0.
zy <- 0.
cy <- (yjump * float y) - abs(mini)
loopgo <- 0
while (zx * zx + zy * zy <= 4. && loopgo < loopmax) do
loopgo <- loopgo + 1
tempzx <- zx
zx <- (zx * zx) - (zy * zy) + cx
zy <- (2. * tempzx * zy) + cy
(List.append outputList [loopgo]) |> ignore
outputList
그리고 다음은 재귀 mbCalc 함수가 작업을 수행하는 내 버전입니다.
let mandelbrotSetRec (xp : int) (yp : int) (w : int) (h :int) (width : int) (height : int)
(maxr : float) (minr : float) (maxi : float) (mini : float) : List<int> =
let xjump = ((maxr - minr) / (float width))
let yjump = ((maxi - mini) / (float height))
let loopMax = 1000
let outputList: int list = List.empty
let rec mbCalc(zx:float, zy:float, cx:float, cy:float, loopCount:int) =
match (zx * zx + zy * zy), loopCount with //The square of the magnitude of z
| a,b when a > 4. || b = loopMax -> loopCount
| _ -> mbCalc((zx * zx) - (zy * zy) + cx, (2. * zx * zy) + cy, cx, cy, loopCount+1) //iteration is the next value of z^2+c
[|0..w-1|] //For each x...
|> Array.map (fun x -> let cx = (xjump * float (x+xp) - abs(minr))
[|0..h-1|] ///and for each y...
|> Array.map (fun y -> let cy = (yjump * float (y+yp) - abs(mini))
let mbVal = mbCalc(0., 0., cx, cy,0) //Calculate the number of iterations to convergence (recursively)
List.append outputList [mbVal]))|>ignore
outputList
재귀 호출이 많은 액터를 무의미하게로드하는 것이 예상되는 일입니까, 아니면 매우 비효율적으로 수행하는 것입니까? 감사하게도 모든 포인터를 받았습니다!
실행하려면 여기에 작은 테스트 스크립트가 있습니다.
let xp = 1500
let yp = 1500
let w = 200
let h = 200
let width = 4000
let height = 4000
let timer1 = new System.Diagnostics.Stopwatch()
timer1.Start()
let ref = mandelbrotSet xp yp w h width height 0.5 -2.5 1.5 -1.5
timer1.Stop()
let timer2 = new System.Diagnostics.Stopwatch()
timer2.Start()
let test = mandelbrotSetRec xp yp w h width height 0.5 -2.5 1.5 -1.5
timer2.Stop
timer1.ElapsedTicks;;
timer2.ElapsedTicks;;
ref = test;;
편집 : 아래 Philip의 대답에 따라 목록 출력을 빠르게 (너무 빨리!) 추가하여 가져 오기를 요구하지 않고 스크립트에서 실행되는 것을 만들었습니다. 이미지를 반환하는 코드는 다음과 같습니다.
let mandelbrotSetRec (xp : int) (yp : int) (w : int) (h :int) (width : int) (height : int)
(maxr : float) (minr : float) (maxi : float) (mini : float) : Image<Rgba32> =
let img = new Image<Rgba32>(w, h)
let xjump = ((maxr - minr) / (float width))
let yjump = ((maxi - mini) / (float height))
let loopMax = 1000
//Precalculate the possible colour list
let palette = List.append ([0..loopMax - 1] |> List.map(fun c -> Rgba32(byte(c % 32 * 7), byte(c % 128 * 2), byte(c % 16 * 14)))) [Rgba32.Black]
let rec mbCalc(zx:float, zy:float, cx:float, cy:float, loopCount:int) =
match (zx * zx + zy * zy), loopCount with //The square of the magnitude of z
| a,b when a > 4. || b = loopMax -> loopCount
| _ -> mbCalc((zx * zx) - (zy * zy) + cx, (2. * zx * zy) + cy, cx, cy, loopCount+1) //iteration is the next value of z^2+c
[|0..w-1|] //For each x...
|> Array.map (fun x -> let cx = (xjump * float (x+xp) - abs(minr))
[|0..h-1|] ///and for each y...
|> Array.map (fun y -> let cy = (yjump * float (y+yp) - abs(mini))
let mbVal = mbCalc(0., 0., cx, cy,0) //Calculate the number of iterations to convergence (recursively)
img.[x,y] <- palette.[mbVal]))|>ignore
img
첫째, 두 함수가 모두 반환 []
되므로 올바르게 계산 된 경우에도 반환되는 mandlebrot 집합이 없습니다. List.append
목록을 반환하지만 기존 목록을 변경하지 않습니다.
아래의 빠른 BenchmarkDotNet 프로그램을 사용하면 각 기능이 자체 모듈에 있습니다.
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Running
open ActorTest
[<MemoryDiagnoser>]
type Bench() =
let xp = 1500
let yp = 1500
let w = 200
let h = 200
let width = 4000
let height = 4000
[<Benchmark(Baseline=true)>]
member _.Mutable() =
Mutable.mandelbrotSet xp yp w h width height 0.5 -2.5 1.5 -1.5
[<Benchmark>]
member _.Recursive() =
Recursive.mandelbrotSet xp yp w h width height 0.5 -2.5 1.5 -1.5
[<EntryPoint>]
let main argv =
let summary = BenchmarkRunner.Run<Bench>()
printfn "%A" summary
0 // return an integer exit code
코드는 다음과 같은 결과를 얻었습니다.
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------- |---------:|----------:|----------:|------:|--------:|---------:|---------:|------:|----------:|
| Mutable | 1.356 ms | 0.0187 ms | 0.0166 ms | 1.00 | 0.00 | 406.2500 | - | - | 1.22 MB |
| Recursive | 2.558 ms | 0.0303 ms | 0.0283 ms | 1.89 | 0.03 | 613.2813 | 304.6875 | - | 2.13 MB |
나는 당신이 사용하고 Array.map
있지만 어디에서나 캡처되는 결과가 없다는 것을 알았 으므로 Array.iter
코드를 거의 동일하게 변경하십시오.
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------- |---------:|----------:|----------:|------:|---------:|------:|------:|----------:|
| Mutable | 1.515 ms | 0.0107 ms | 0.0094 ms | 1.00 | 406.2500 | - | - | 1.22 MB |
| Recursive | 1.652 ms | 0.0114 ms | 0.0101 ms | 1.09 | 607.4219 | - | - | 1.82 MB |
이 차이는 매핑 호출로 수행 된 추가 할당으로 설명 될 수 있습니다. 특히 더 큰 어레이의 경우 할당은 비용이 많이 들기 때문에 가능하면 피하는 것이 가장 좋습니다. 정확한 타이밍은 기계마다 다르지만 BenchmarkDotNet을 사용할 때 비슷한 전후 비율을 기대합니다.
이는 목록 할당을 피하고 사용자가 채운 목록이나 배열을 미리 할당하여 더욱 최적화 될 수 있습니다. 반복 호출의 경우에도 마찬가지입니다. 또한 a Span<'T>
를 반복 하는 것은 경계 검사를 제거하기 때문에 배열보다 빠르지 만 그렇게하려면 코드의 모양을 많이 변경해야 할 것입니다.
마지막으로 항상 BenchmarkDotNet과 같은 통계적 벤치마킹 도구를 사용하여 이와 같은 마이크로 벤치 마크의 성능을 측정하십시오. 빠른 스크립트는 시작점으로는 괜찮지 만 컴퓨터의 실행 시간 변동성을 고려하는 도구를 대체 할 수는 없습니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다