haskell - 并发性能比顺序性能差

标签 haskell concurrency

我编写了一个函数,通过滤波器将文件从 48kHz 上采样到 192kHz:

upsample :: Coefficients -> FilePath -> IO ()

它获取滤波器系数、文件路径(必须进行上采样)并将结果写入新文件。

我必须对许多文件进行上采样,因此我编写了一个函数,使用 Control.Concurrent.Async 中的 forConcurrently_ 并行对整个目录进行上采样:

upsampleDirectory :: Directory -> FilePath -> IO ()
upsampleDirectory dir coefPath = do
  files <- getAllFilesFromDirectory dir
  coefs <- loadCoefficients coefPath
  forConcurrently_ files $ upsample coefs

我使用 -threaded 选项进行编译,并使用 +RTS -N2 运行。我看到的是,顺序对 2 个文件进行上采样比并行对两个文件进行上采样要快。

上采样 file1.wav 需要 18.863 秒。 上采样 file2.wav 需要 18.707 秒。 使用 file1.wavfile2.wav 对目录进行上采样需要 66.250 秒。

我做错了什么?

我试图让这篇文章保持简洁,所以如果您需要有关某些功能的更多详细信息,请询问我。

最佳答案

这里有几种可能性。首先,确保自己 100% 确定您确实正在使用 +RTS -N2 -RTS 运行程序。 。我无法告诉你我对并行程序进行了多少次基准测试并编写了:

stack exec myprogram +RTS -N2 -RTS

代替:

stack exec myprogram -- +RTS -N2 -RTS

让自己陷入无可救药的困惑。 (第一个版本在两个处理器上运行堆栈可执行文件,但在一个处理器上运行目标可执行文件!)也许添加 print $ getNumCapabilities在您的 main 的开头程序以确定。

确认您在两个处理器上运行后,下一个最可能的问题是您的实现没有在恒定空间中运行并且正在炸毁堆。这是我用来尝试复制您的问题的一个简单的测试程序。 (您可以随意使用我很棒的上采样过滤器!)

module Main where

import Control.Concurrent.Async
import System.Environment
import qualified Data.ByteString as B

upsample :: FilePath -> IO ()
upsample fp = do c <- B.readFile fp
                 let c' = B.pack $ concatMap (replicate 4) $ B.unpack c
                 B.writeFile (fp ++ ".out") c'

upsampleFiles :: [FilePath] -> IO ()
upsampleFiles files = do
  forConcurrently_ files $ upsample

main :: IO ()
main = upsampleFiles =<< getArgs   -- sample all file on command line

当我在单个 70meg 测试文件上运行此程序时,它运行了 14 秒。当我在两个副本上并行运行它时,它运行了超过一分钟,然后才开始疯狂地交换,我不得不杀死它。切换到后:

import qualified Data.ByteString.Lazy as B

单个文件的运行时间为 3.7 秒,单个处理器上的两个副本的运行时间为 7.8 秒,两个处理器上的两个副本的运行时间为 4.0 秒(+RTS -N2) .

确保您在编译时启用了优化,分析您的程序,并确保它在恒定(或至少合理)的堆空间中运行。上面的程序在恒定的 100k 字节堆中运行。使用严格的 ByteString 的类似版本用于阅读和懒惰ByteString用于写入将整个文件读取到内存中,但堆几乎立即在不到一秒的时间内增长到 70megs(文件的大小),然后在处理文件时保持不变。

无论您的过滤器有多复杂,如果您的程序的堆增长到千兆字节,那么实现就会被破坏,并且您需要在担心性能、并行或其他方面之前修复它。

关于haskell - 并发性能比顺序性能差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43569721/

相关文章:

haskell - 在 Haskell 中组合 monad

haskell - 将类型族约束约束为 "some pair"

java - 如何在并发环境中只创建一个实例

haskell - Windows 上的 Cabal 安装问题

haskell - 如何将托盘设置在 xmonad 中的所有工作区上?

haskell - 你如何在 Notepad++ 上编译和运行haskell

java - "In a distributed environment, one does not use multithreding"- 为什么?

java - 并发和 CacheLoader.load()

java - 我可以在 MessageConsumer 上注册一个监听器,然后调用 receive() 方法吗? (JMS)

AWS lambda 中的 Java 8 并发?