使用Array.Parallel.map减少运行时间 [英] using Array.Parallel.map for decreasing running time

查看:116
本文介绍了使用Array.Parallel.map减少运行时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经将C#中的项目转换为绘制Mandelbrot集的F#.
不幸的是,渲染全屏大约需要一分钟,所以我尝试寻找一些方法来加速它.

I have converted a project in C# to F# that paints the Mandelbrot set.
Unfortunately does it take around one minute to render a full screen so I am try to find some ways to speed it up.

这是一个几乎需要花费所有时间的呼叫:

It is one call that take almost all of the time:

Array.map (fun x -> this.colorArray.[CalcZ x]) xyArray

xyArray (double * double) [] =>(双精度元组数组)
colorArray是int32长度= 255的数组

xyArray (double * double) [] => (array of tuple of double)
colorArray is an array of int32 length = 255

CalcZ定义为:

 let CalcZ (coord:double * double) =

    let maxIterations = 255

    let rec CalcZHelper (xCoord:double) (yCoord:double) // line break inserted
           (x:double) (y:double) iters =
        let newx = x * x + xCoord - y * y
        let newy = 2.0 * x * y + yCoord
        match newx, newy, iters with
        | _ when Math.Abs newx > 2.0 -> iters
        | _ when Math.Abs newy > 2.0 -> iters
        | _ when iters = maxIterations -> iters
        | _ -> CalcZHelper xCoord yCoord newx newy (iters + 1)

    CalcZHelper (fst coord) (snd coord) (fst coord) (snd coord) 0

由于我仅使用大约一半的处理器容量,因此可以使用更多线程,尤其是Array.Parallel.map,它可以转换为

As I only use around half of the processor capacity is an idea to use more threads and specifically Array.Parallel.map, translates to system.threading.tasks.parallel

现在我的问题

一个幼稚的解决方案是:

A naive solution, would be:

Array.Parallel.map (fun x -> this.colorArray.[CalcZ x]) xyArray  

但这花了两倍的时间,我该如何重写以减少时间,或者我可以采取其他方法更好地利用处理器?

but that took twice the time, how can I rewrite this to take less time, or can I take some other way to utilize the processor better?

预先感谢
戈尔根

Thanks in advance
Gorgen

---编辑---
调用CalcZ的函数如下所示:

---edit---
the function that is calling CalcZ looks like this:

          let GetMatrix =
            let halfX = double bitmap.PixelWidth * scale / 2.0
            let halfY = double bitmap.PixelHeight * scale / 2.0
            let rect:Mandelbrot.Rectangle = 
                {xMax = centerX + halfX; xMin = centerX - halfX;
                 yMax = centerY + halfY; yMin = centerY - halfY;}

            let size:Mandelbrot.Size = 
                {x = bitmap.PixelWidth; y = bitmap.PixelHeight}

            let xyList = GenerateXYTuple rect size
            let xyArray = Array.ofList xyList
            Array.map (fun x -> this.colorArray.[CalcZ x]) xyArray

        let region:Int32Rect = new Int32Rect(0,0,bitmap.PixelWidth,bitmap.PixelHeight)
        bitmap.WritePixels(region, GetMatrix, bitmap.PixelWidth * 4, region.X, region.Y);

GenerateXYTuple:

GenerateXYTuple:

let GenerateXYTuple (rect:Rectangle) (pixels:Size) =
    let xStep = (rect.xMax - rect.xMin)/double pixels.x
    let yStep = (rect.yMax - rect.yMin)/double pixels.y
    [for column in 0..pixels.y - 1 do
       for row in 0..pixels.x - 1 do
         yield (rect.xMin + xStep * double row,
           rect.yMax - yStep * double column)]

---编辑---

根据kvb的建议(非常感谢!),在对我的问题的评论中,我以发布模式构建了该程序.以发布"模式进行构建通常可以加快处理速度.

Following a suggestion from kvb (thanks a lot!) in a comment to my question, I built the program in Release mode. Building in the Relase mode generally speeded up things.

仅仅在Release中进行构建就使我从50年代变到了30年代,将阵列上的所有变换移动到了一起,因此所有操作都通过了一次,因此速度提高了约10秒.最后使用Array.Parallel.init将我带到刚刚超过11秒的时间.

Just building in Release took me from 50s to around 30s, moving in all transforms on the array so it all happens in one pass made it around 10 seconds faster. At last using the Array.Parallel.init brought me to just over 11 seconds.

我从中学到的是....在对事物进行计时并使用并行构造时使用释放模式... 再感谢您一次,感谢您提供的帮助.
-编辑-
通过使用本机dll中的SSE assember,我可以将整个计算最密集点的全屏时间从大约12秒减少到1.2秒.不幸的是我没有图形处理器...

What I learnt from this is.... Use the release mode when timing things and using parallel constructs... One more time, thanks for the help I have recieved.
--edit--
by using SSE assember from a native dll I have been able to slash the time from around 12 seconds to 1.2 seconds for a full screen of the most computational intensive points. Unfortunately I don't have a graphics processor...

戈尔根

推荐答案

顺便说一句,您似乎正在生成一个坐标数组,然后将其映射到结果数组.如果使用init函数而不是map,则无需创建坐标数组:Array.Parallel.init 1000 (fun y -> Array.init 1000 (fun x -> this.colorArray.[CalcZ (x, y)]))

As an aside, it looks like you're generating an array of coordinates and then mapping it to an array of results. You don't need to create the coordinate array if you use the init function instead of map: Array.Parallel.init 1000 (fun y -> Array.init 1000 (fun x -> this.colorArray.[CalcZ (x, y)]))

以下内容可能不正确: 您的问题可能是您调用一个小函数一百万次,从而导致调度开销使您正在执行的实际工作不堪重负.您应该将数组划分为更大的块,以便每个单独的任务花费一毫秒左右的时间.您可以使用数组数组,以便在外部数组上调用Array.Parallel.map在内部数组上调用Array.map.这样,每个并行操作将在整行像素上操作,而不是在单个像素上进行操作.

The following may be inaccurate: Your problem could be that you call a tiny function a million times, causing the scheduling overhead to overwhelm that actual work you're doing. You should partition the array into much larger chunks so that each individual task takes a millisecond or so. You can use an array of arrays so that you would call Array.Parallel.map on the outer arrays and Array.map on the inner arrays. That way each parallel operation will operate on a whole row of pixels instead of just a single pixel.

这篇关于使用Array.Parallel.map减少运行时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆