Игра жизни в F# с акселератором
-
21-09-2019 - |
Вопрос
Я пытаюсь написать жизнь в F#, используя Accelerator v2, но по какой -то странной причине мой выход не квадрат, несмотря на то, что все мои массивы были квадратными - кажется, что все, кроме прямоугольной области в левом верхнем левом ЛОЖЬ. Я понятия не имею, как это может произойти, так как все мои операции должны относиться к всем массиву одинаково. Любые идеи?
open Microsoft.ParallelArrays
open System.Windows.Forms
open System.Drawing
type IPA = IntParallelArray
type BPA = BoolParallelArray
type PAops = ParallelArrays
let RNG = new System.Random()
let size = 1024
let arrinit i = Array2D.init size size (fun x y -> i)
let target = new DX9Target()
let threearr = new IPA(arrinit 3)
let twoarr = new IPA(arrinit 2)
let onearr = new IPA(arrinit 1)
let zeroarr = new IPA(arrinit 0)
let shifts = [|-1;-1|]::[|-1;0|]::[|-1;1|]::[|0;-1|]::[|0;1|]::[|1;-1|]::[|1;0|]::[|1;1|]::[]
let progress (arr:BPA) = let sums = shifts //adds up whether a neighbor is on or not
|> List.fold (fun (state:IPA) t ->PAops.Add(PAops.Cond(PAops.Rotate(arr,t),onearr,zeroarr),state)) zeroarr
PAops.Or(PAops.CompareEqual(sums,threearr),PAops.And(PAops.CompareEqual(sums,twoarr),arr)) //rule for life
let initrandom () = Array2D.init size size (fun x y -> if RNG.NextDouble() > 0.5 then true else false)
type meform () as self=
inherit Form()
let mutable array = new BoolParallelArray(initrandom())
let timer = new System.Timers.Timer(1.0) //redrawing timer
do base.DoubleBuffered <- true
do base.Size <- Size(size,size)
do timer.Elapsed.Add(fun _ -> self.Invalidate())
do timer.Start()
let draw (t:Graphics) =
array <- array |> progress
let bmap = new System.Drawing.Bitmap(size,size)
target.ToArray2D array
|> Array2D.iteri (fun x y t ->
if not t then bmap.SetPixel(x,y,Color.Black))
t.DrawImageUnscaled(bmap,0,0)
do self.Paint.Add(fun t -> draw t.Graphics)
do Application.Run(new meform())
Решение
Как упоминал Роберт, я написал статью, которая показывает, как реализовать Игра жизни в F# Используя Accelerator v2, чтобы вы могли взглянуть на это для рабочей версии. Я помню, как имел подобную проблему, но я не знаю точно в каком сценарии.
В любом случае, если вы используете DX9Target
Тогда проблема может заключаться в том, что эта цель не должна поддерживать операции с целыми числами (потому что эмуляция целочисленного арифметики на графическом процессоре точно невозможно с использованием DX9). Я считаю, что это также причина, по которой я в итоге использовал FloatParallelArray
в моей реализации. У вас есть шанс попробовать X64MulticoreTarget
Чтобы посмотреть, сработает ли это?
РЕДАКТИРОВАТЬ: Я провел дополнительные исследования и (если я не упускаю что -то важное), это кажется ошибкой с CompareEqual
метод Вот гораздо более простой пример, который показывает проблему:
open Microsoft.ParallelArrays
let target = new DX9Target()
let zeros = new IntParallelArray(Array2D.create 4 4 0)
let trues = target.ToArray2D(ParallelArrays.CompareEqual(zeros, zeros))
trues |> Array2D.iter (printfn "%A")
Ожидаемый результат будет true
(Несколько раз), но если вы запустите его, он печатает true
только 4 раза, а затем печатает 12 раз false
. Анкет Я спрошу кого -нибудь из команды Accelerator и опубликую здесь ответ. В то же время, вы можете сделать то же самое, что и в моем примере, то есть имитировать логические операции, используя FPA
и избегать использования BPA
а также CompareEqual
.
Редактировать 2: Вот ответ от членов команды Accelerator:
Это связано с отсутствием точных целочисленных расчетов на графических процессорах DX9. Из -за численного дрожания, логическое сравнение целого числа с самим собой не всегда вычисляется как ровно. (...)
Итак, в итоге, вы не можете полагаться на BPA
. Анкет Единственный вариант - сделать то, что я предложил - симулировать логические FPA
(и, возможно, сравните число с небольшим дельтой-инициатором, чтобы избежать дрожания, вызванного графическими процессорами). Этот Shoudl, однако, работает с X86MulticoreTarget
- Если вы можете найти минимальную резо, которая показывает, в каких ситуациях библиотека сбивается, это было бы действительно полезно!
Другие советы
Что касается вопросов точности: графические процессоры класса DX9 не имеют выделенного целочисленного оборудования, поэтому целочисленные потоки интерпретируются как потоки с плавающей запятой (с отсутствием точности, с которой вы встречались).
Графические процессоры класса DX10 теперь поддерживают точные 32 бита целых числа со всеми бить. Но это не обязательно означает, что у них есть истинные 32 бита целых чисел. Например, в текущем DX10 NVIDIA Gen Integer Math выполняется с 24-разрядными целочисленными единицами, таким образом, имитация 32-разрядного целого числа. Следующее поколение DX11 NVIDIA принесет истинные 32-разрядные целочисленные единицы.