F # 재귀 대 반복 속도 / 오버 헤드

앤드류 윌셔

나는 그것을 시도하고 이해하기 위해 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] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

반복에 대한 C ++ 재귀 코드

분류에서Dev

템플릿 인수 집합에 대한 메서드 오버로드를위한 재귀 상속

분류에서Dev

재귀를 반복으로 대체

분류에서Dev

반복에 대한 다중 재귀

분류에서Dev

BST의 재귀 대 반복 순회

분류에서Dev

반복에 대한 재귀 (Java)

분류에서Dev

약한 재산에 대한 많은 오버 헤드?

분류에서Dev

재귀 쿼리 속도 향상 / 값 반복

분류에서Dev

이 재귀 함수의 반복 버전

분류에서Dev

방대한 목록을 반복하면 gc 오버 헤드 제한이 초과 됨

분류에서Dev

코드는 자바 재귀 스택 오버플로 오류를 반환

분류에서Dev

비 재귀 함수에 대한 FIbonacci 시간 복잡도

분류에서Dev

복합 패턴에 대한 재귀 반복기

분류에서Dev

JavaScript의 개체 속성 오버 헤드에 대한 지역 변수 참조

분류에서Dev

내 현재 코드는 5 개의 노드에 대한 정점 커버를 찾습니다. 여러 노드로 어떻게 일반화합니까? 재귀를 시도해야합니까?

분류에서Dev

Gradle 배포 : GC 오버 헤드 한도 초과 (최대 힙 : 1024MB)

분류에서Dev

반복기 오버로드 멤버 선택 대 간접 연산자

분류에서Dev

OpenMesh 재귀 반복

분류에서Dev

재귀 후 Java 반환 값 (버킷 채우기 도구)

분류에서Dev

leetcode 1011에 대한 재귀 코드의 시간 복잡성에 대한 질문

분류에서Dev

반복 형식에 대한 TSP 재귀 솔루션

분류에서Dev

재귀에 대한 다음 반복을 이해하지 못함

분류에서Dev

중첩 배열에 대한 PHP 재귀 반복

분류에서Dev

노드 q 재귀 약속

분류에서Dev

재귀 속성 반복으로 속성 구조 유지

분류에서Dev

F #의 예외가있는 재귀 스택 오버플로

분류에서Dev

F # 재귀 함수 인수 및 스택 오버플로

분류에서Dev

오류 : 선형 회귀에서 노드 재정의 시도

분류에서Dev

심도 우선 검색 재귀 또는 반복

Related 관련 기사

  1. 1

    반복에 대한 C ++ 재귀 코드

  2. 2

    템플릿 인수 집합에 대한 메서드 오버로드를위한 재귀 상속

  3. 3

    재귀를 반복으로 대체

  4. 4

    반복에 대한 다중 재귀

  5. 5

    BST의 재귀 대 반복 순회

  6. 6

    반복에 대한 재귀 (Java)

  7. 7

    약한 재산에 대한 많은 오버 헤드?

  8. 8

    재귀 쿼리 속도 향상 / 값 반복

  9. 9

    이 재귀 함수의 반복 버전

  10. 10

    방대한 목록을 반복하면 gc 오버 헤드 제한이 초과 됨

  11. 11

    코드는 자바 재귀 스택 오버플로 오류를 반환

  12. 12

    비 재귀 함수에 대한 FIbonacci 시간 복잡도

  13. 13

    복합 패턴에 대한 재귀 반복기

  14. 14

    JavaScript의 개체 속성 오버 헤드에 대한 지역 변수 참조

  15. 15

    내 현재 코드는 5 개의 노드에 대한 정점 커버를 찾습니다. 여러 노드로 어떻게 일반화합니까? 재귀를 시도해야합니까?

  16. 16

    Gradle 배포 : GC 오버 헤드 한도 초과 (최대 힙 : 1024MB)

  17. 17

    반복기 오버로드 멤버 선택 대 간접 연산자

  18. 18

    OpenMesh 재귀 반복

  19. 19

    재귀 후 Java 반환 값 (버킷 채우기 도구)

  20. 20

    leetcode 1011에 대한 재귀 코드의 시간 복잡성에 대한 질문

  21. 21

    반복 형식에 대한 TSP 재귀 솔루션

  22. 22

    재귀에 대한 다음 반복을 이해하지 못함

  23. 23

    중첩 배열에 대한 PHP 재귀 반복

  24. 24

    노드 q 재귀 약속

  25. 25

    재귀 속성 반복으로 속성 구조 유지

  26. 26

    F #의 예외가있는 재귀 스택 오버플로

  27. 27

    F # 재귀 함수 인수 및 스택 오버플로

  28. 28

    오류 : 선형 회귀에서 노드 재정의 시도

  29. 29

    심도 우선 검색 재귀 또는 반복

뜨겁다태그

보관