如何在haskell快速傅里叶变换中应用数据并行?

时间:2014-03-11 20:09:46

标签: haskell parallel-processing fft

我有一个用于解决快速傅里叶变换的haskell代码,我想在其上应用数据并行性。但是,我使用的每一种策略都会产生太多的火花,其中大部分都会溢出。

有没有人知道如何在以下算法上应用良好的数据并行策略:

-- radix 2 Cooley-Tukey FFT

fft::[Complex Float] -> [Complex Float]
fft [a] = [a]
fft as = interleave ls rs
  where
    (cs,ds) = bflyS as
    ls = fft cs
    rs = fft ds

interleave [] bs = bs
interleave (a:as) bs = a : interleave bs as

bflyS :: [Complex Float] -> ([Complex Float], [Complex Float])
bflyS as = (los,rts)
  where
    (ls,rs) = halve as
    los = zipWith (+) ls rs
    ros = zipWith (-) ls rs
    rts = zipWith (*) ros [tw (length as) i | i <- [0..(length ros) - 1]]

halve as = splitAt n' as
  where
    n' = div (length as + 1) 2

-- twiddle factors
tw :: Int -> Int -> Complex Float
tw n k = cis (-2 * pi * fromIntegral k / fromIntegral n)

PAR MONAD

左下角的答案帮助我了解了如何在代码上应用数据并行性。但是,我研究了par monad并尝试将任务并行性应用于它。问题是它的运行速度比原来的bflyS慢。我认为我开发的代码与我正在进行的相关工作相比,创建线程的成本很高。有谁知道如何以更好的方式使用par monad?

--New Task Parallelism bflyS

bflySTask :: [Complex Float] -> ([Complex Float], [Complex Float])
bflySTask as = runPar $ do
    let (ls, rs) = halve as
    v1<-new
    v2<-new
    let ros = DATA.zipWith (-) ls rs
    let aux = DATA.map  (tw n) [0..n-1]
    fork $ put v1 (DATA.zipWith (+) ls rs)
    fork $ put v2 (DATA.zipWith (*) ros aux)
    los <- get v1
    rts <- get v2   
    return (los, rts)
        where
                n = DATA.length as

1 个答案:

答案 0 :(得分:12)

首先:在我开始考虑并行性之前,有很多优化要做:

  • 列出摇滚,但他们的非连续内存模型意味着他们不能允许遍历几乎与使用Data.Vector这样的紧密数组所能达到的速度一样快,因为你不可避免地最终得到很多缓存未命中。事实上,我很少看到基于列表的算法从并行化中获得很多,因为它们受内存限制而不是CPU性能。

  • 你的旋转因子一遍又一遍地计算,你可以通过这里的记忆获得很多收获。

  • 你继续打电话给length,但那是一个非常浪费的功能( O n ),可能是 O (1))。使用一些可能处理长度的容器;列表并不意味着(我们希望保持其无限的能力)。

并行化本身将非常简单;我会检查John L所建议的长度,确实我需要一个相当大的尺寸才能引发一个线程,至少类似于256:因为性能可能只在数千的大小时变得至关重要,这应该是基石有足够的线程来保持你的核心忙。

import Data.Vector.Unboxed as UBV
import Control.Parallel.Strategies

type ℂ = Complex Float

fft' :: UBV.Vector ℂ -> UBV.Vector ℂ
fft' aₓs = interleave lᵥs rᵥs
 where (lᵥs, rᵥs) = (fft lₓs, fft rₓs)
                     `using` if UBV.length aₓs > 256 then parTuple2 else r0
       (lₓs, rₓs) = byflyS aₓs