Pergunta

Estou tentando escrever a vida em F# usando o acelerador V2, mas por algum motivo estranho minha produção não é quadrada, apesar de todas as minhas matrizes serem quadradas - parece que tudo, menos uma área retangular no canto superior esquerdo da matriz, está sendo definido para falso. Não tenho idéia de como isso pode estar acontecendo, pois todas as minhas operações devem tratar a matriz inteira igualmente. Alguma ideia?

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())
Foi útil?

Solução

Como Robert mencionou, escrevi um artigo que mostra como implementar Jogo da vida em f# Usando o Accelerator V2, para que você possa dar uma olhada nisso para uma versão de trabalho. Lembro -me de ter um problema semelhante, mas não sei exatamente em que cenário.

Enfim, se você está usando DX9Target Então, o problema pode ser que esse destino não suporta operações com números inteiros (porque emular a aritmética inteira na GPU precisamente não é possível usando o DX9). Eu acredito que essa também é uma razão pela qual acabei usando FloatParallelArray na minha implementação. Você tem alguma chance de experimentar o X64MulticoreTarget Para ver se isso funcionaria?

EDITAR: Eu fiz algumas investigações adicionais e (a menos que esteja perdendo algo importante), parece ser um bug com o CompareEqual método. Aqui está um exemplo muito mais simples que mostra o problema:

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")

O resultado esperado seria true (várias vezes), mas se você executar, ele imprime true apenas 4 vezes e depois imprime 12 vezes false. Vou pedir a alguém da equipe do acelerador e postar uma resposta aqui. Enquanto isso, você pode fazer a mesma coisa que eu no meu exemplo - ou seja, simular operações booleanas usando FPA e evite usar BPA e CompareEqual.

Editar 2: Aqui está uma resposta dos membros da equipe do Accelerator:

Isso está relacionado à falta de cálculos de número inteiro precisos nas GPUs DX9. Por causa do jitter numérico, uma comparação booleana de um número inteiro consigo mesmo nem sempre é calculada como exatamente igual. (...)

Então, em resumo, você realmente não pode confiar BPA. A única opção é fazer o que sugeri - simular booleanos usando FPA (e possivelmente compare o número com um pequeno delta-vizinho para evitar o jitter causado pelas GPUs). Este shoudl, no entanto, trabalhe com o X86MulticoreTarget - Se você encontrar uma reprovação mínima que mostra quais situações a biblioteca trava, isso seria realmente útil!

Outras dicas

Sobre problemas de precisão: as GPUs de classe DX9 não possuem hardware inteiro dedicado, portanto os fluxos inteiros são interpretados como fluxos de ponto flutuante (com a falta de precisão que você conheceu).

As GPUs de classe DX10 agora suportam 32 bits inteiros precisos com todas as operações b bit-blue. Mas isso não é necessário significa que eles têm 32 bits inteiros Alus. Por exemplo, a matemática inteira do DX10 NVIDIA atual é realizada com unidades inteiras de 24 bits, portanto, são emuladas operações inteiras de 32 bits. A próxima geração DX11 NVIDIA trará verdadeiras unidades inteiras de 32 bits.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top