2

我正在尝试使用软件定义的无线电概念。从这篇文章中,我尝试实现 GPU 并行离散傅里叶变换。

我很确定我可以预先计算 90 度的 sin(i) cos(i),然后翻转并重复,而不是我在这段代码中所做的那样,这会加快速度。但到目前为止,我什至认为我没有得到正确的答案。如我所料,全零输入给出 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

两个建议:

  • 确保您不会以某种方式将度数与弧度混淆
  • 尝试无并行,或者只使用 F# 的异步进行并行

(在 F# 中,如果你有一个浮点数数组

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

希望您可以使用这个幼稚的代码来更好地了解加速器代码的行为方式,这可以指导您测试加速器代码。请记住,造成差异的部分原因可能仅仅是计算的精度——数组中有大约 5200 万个元素,因此累积 79 的总误差实际上可能并不算太糟糕。FWIW,在运行上述单精度代码时,我得到了 ~0.05 的幂,但在使用具有双精度数字的等效代码时,我得到了 ~4e-18 的幂。

于 2010-06-27T20:50:18.063 回答