2

私はソフトウェア無線の概念を実験しようとしています。この記事から、GPU並列処理の離散フーリエ変換を実装しようとしました。

私は、sin(i)cos(i)の90度を事前に計算してから、このコードで行っていることではなく、フリップして繰り返すことができると確信しています。しかし、これまでのところ、私は正しい答えを得ているとは思いません。すべてゼロの入力は私が期待するように0の結果を与えますが、入力としてのすべての0.5は78.9985886fを与えます(この場合も0の結果を期待します)。基本的に、私は一般的に混乱しています。適切な入力データがなく、結果をどう処理するか、または結果を検証する方法がわかりません。

この質問は、ここにある他の投稿に関連しています

open Microsoft.ParallelArrays
open System

 // X64MulticoreTarget is faster on my machine, unexpectedly
let target = new DX9Target() // new X64MulticoreTarget()

ignore(target.ToArray1D(new FloatParallelArray([| 0.0f |]))) // Dummy operation to warm up the GPU

let stopwatch = new System.Diagnostics.Stopwatch() // For benchmarking

let Hz = 50.0f
let fStep = (2.0f * float32(Math.PI)) / Hz
let shift = 0.0f // offset, once we have to adjust for the last batch of samples of a stream

// If I knew that the periodic function is periodic 
// at whole-number intervals, I think I could keep 
// shift within a smaller range to support streams 
// without overflowing shift - but I haven't 
// figured that out

//let elements = 8192 // maximum for a 1D array - makes sense as 2^13
//let elements = 7240 // maximum on my machine for a 2D array, but why?
let elements = 7240

// need good data!!
let buffer : float32[,] = Array2D.init<float32> elements elements (fun i j -> 0.5f) //(float32(i * elements) + float32(j))) 

let input = new FloatParallelArray(buffer)
let seqN : float32[,] = Array2D.init<float32> elements elements (fun i j -> (float32(i * elements) + float32(j)))
let steps = new FloatParallelArray(seqN)
let shiftedSteps = ParallelArrays.Add(shift, steps)
let increments = ParallelArrays.Multiply(fStep, steps)
let cos_i = ParallelArrays.Cos(increments) // Real component series
let sin_i = ParallelArrays.Sin(increments) // Imaginary component series

stopwatch.Start()
// From the documentation, I think ParallelArrays.Multiply does standard element by 
// element multiplication, not matrix multiplication
// Then we sum each element for each complex component (I don't understand the relationship 
// of this, or the importance of the generalization to complex numbers)
let real = target.ToArray1D(ParallelArrays.Sum(ParallelArrays.Multiply(input, cos_i))).[0]
let imag = target.ToArray1D(ParallelArrays.Sum(ParallelArrays.Multiply(input, sin_i))).[0]
printf "%A in " ((real * real) + (imag * imag)) // sum the squares for the presence of the frequency
stopwatch.Stop()

printfn "%A" stopwatch.ElapsedMilliseconds

無視(System.Console.ReadKey())

4

2 に答える 2

2

2つの提案:

  • 度とラジアンをどういうわけか混同しないようにしてください
  • 並列処理なしで、または並列処理用のF#の非同期で実行してみてください

(F#では、floatの配列がある場合

let a : float[] = ...

次に、「それらすべてに並列にステップを追加して」、次のような新しい配列を生成できます。

let aShift = a |> (fun x -> async { return x + shift }) 
               |> Async.Parallel |> Async.RunSynchronously

(ただし、同期ループを実行するよりも遅くなる可能性があります)。)

于 2010-06-27T18:55:13.407 に答える
2

私はあなたの答えがゼロに近くないというあなたの驚きを共有します。F#でDFTを実行するための単純なコードを記述し、不一致の原因を突き止めることができるかどうかを確認することをお勧めします。

これがあなたがやろうとしていることだと私は思います:

let N = 7240
let F = 1.0f/50.0f
let pi = single System.Math.PI

let signal = [| for i in 1 .. N*N -> 0.5f |]

let real = 
  seq { for i in 0 .. N*N-1 -> signal.[i] * (cos (2.0f * pi * F * (single i))) }
  |> Seq.sum

let img = 
  seq { for i in 0 .. N*N-1 -> signal.[i] * (sin (2.0f * pi * F * (single i))) }
  |> Seq.sum

let power = real*real + img*img

うまくいけば、この素朴なコードを使用して、アクセラレータコードがどのように動作するかについてのより良い直感を得ることができます。これは、アクセラレータコードのテストに役立つ可能性があります。不一致の理由の一部は、単に計算の精度にある可能性があることに注意してください。配列には最大5,200万の要素があるため、合計79のエラーを累積しても実際にはそれほど悪くない場合があります。FWIW、上記の単精度コードを実行すると〜0.05の累乗が得られますが、倍精度の数値を持つ同等のコードを使用すると〜4e-18の累乗が得られます。

于 2010-06-27T20:50:18.063 に答える