Я преобразовал проект на C # в F #, который рисует набор Мандельброта .
К сожалению, для рендеринга всего экрана требуется около минуты, поэтому я пытаюсь найти способы его ускорить.
Это один вызов, который занимает почти все время:
Array.map (fun x -> this.colorArray.[CalcZ x]) xyArray
xyArray (double * double) [ ]
=> (массив кортежей типа double)
colorArray - это массив длиной int32 = 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 переводится как system.threading.tasks.parallel
Теперь мой вопрос
Наивное решение:
Array.Parallel.map (fun x -> this.colorArray.[CalcZ x]) xyArray
но это заняло вдвое больше времени, как я могу переписать это, чтобы это занимало меньше времени, или я могу взять какой-нибудь другой способ лучше использовать процессор?
Заранее спасибо
Gorgen
--- edit ---
функция, вызывающая CalcZ
, выглядит так:
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:
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)]
--- edit ---
После предложение от kvb (большое спасибо!) в комментарии к моему вопросу, я построил программу в режиме Release. Сборка в режиме Relase в целом ускорила работу.
Простое построение в Release заняло у меня от 50 до 30 секунд, перемещение всех преобразований в массиве, так что все это происходит за один проход, сделало это примерно на 10 секунд быстрее. Наконец, использование Array.Parallel.init дало мне чуть более 11 секунд.
Из этого я узнал ... Используйте режим релиза при хронометрировании и использовании параллельных конструкций ...
Еще раз спасибо за помощь, которую я получил.
- edit -
с помощью ассемблера SSE из собственной библиотеки dll я смог сократить время с 12 до 1,2 секунды для полноэкранного режима. точек с наибольшей вычислительной нагрузкой. К сожалению, у меня нет графического процессора ...
Gorgen