为什么(Haskell)Repa只使用一个CPU?

3
我一直在使用Repa库制作路径追踪器。最近我进行了重构,使用单子computeP实现并行化。但是,我发现性能提升微不足道。此外,在监视htop时,似乎程序仍然只使用一个CPU。为了深入研究问题,我打开了ghci并运行了以下内容:
~
❯ stack ghci --package repa
Configuring GHCi with the following packages: 
GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /tmp/ghci12667/ghci-script
Prelude> import Data.Array.Repa
Prelude Data.Array.Repa> import System.Random
Prelude Data.Array.Repa System.Random> randomList = randoms (mkStdGen 0)
Prelude Data.Array.Repa System.Random> shape = (Z :. 1000000)
Prelude Data.Array.Repa System.Random> array = fromFunction shape $ \(Z :. i) -> randomList !! i
Prelude Data.Array.Repa System.Random> sumP array

不行。根据 htop 显示,repa 似乎仍然只使用一个 CPU 核心:

enter image description here

此外,执行团队在sumPsumS之间几乎没有差异,略微偏向于sumS
Prelude Data.Array.Repa System.Random> array = fromListUnboxed (Z :. 1000000) $ take 1000000 $ randoms (mkStdGen 0)
(0.01 secs, 0 bytes)
Prelude Data.Array.Repa System.Random> sumP array
AUnboxed Z [500140.92257232184]
(0.99 secs, 1,916,158,952 bytes)
Prelude Data.Array.Repa System.Random> sumS array
AUnboxed Z [500140.92257232184]
(0.93 secs, 2,348,156,248 bytes)

我错过了什么?如果有影响的话,我正在使用Arch Linux:
~
❯ uname -a
Linux roskolnikov 4.11.9-1-ARCH #1 SMP PREEMPT Wed Jul 5 18:23:08 CEST 2017 x86_64 GNU/Linux

更新

一些评论表明我应该像repa文档中所示使用-threaded选项来运行ghci。我曾经(误?)认为ghci默认使用-threaded。无论如何,我的程序已经在使用这些标志——这是来自.cabal文件的代码片段:

executable write
  hs-source-dirs:      app
  main-is:             Write.hs
  ghc-options:         -Odph 
                       -rtsopts 
                       -threaded 
                       -fno-liberate-case 
                       -funfolding-use-threshold1000 
                       -funfolding-keeness-factor1000 
                       -fllvm 
                       -optlo-O3
  build-depends:       base 
                     , pathtracer
                     , repa
                     , JuicyPixels
  default-language:    Haskell2010

此外,我使用(我想是)正确的ghci选项在ghci中重新运行了命令:
~
❯ stack ghci\
 --package repa\
 --ghc-options -Odph\
 --ghc-options -rtsopts\
 --ghc-options -with-rtsopts=-N\
 --ghc-options -threaded\
 --ghc-options -fno-liberate-case\
 --ghc-options -funfolding-use-threshold1000\
 --ghc-options -funfolding-keeness-factor1000\
 --ghc-options -fllvm\
 --ghc-options -optlo-O3

Configuring GHCi with the following packages: 

when making flags consistent: warning:
    -O conflicts with --interactive; -O ignored.
GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /tmp/ghci31252/ghci-script
Prelude> import Data.Array.Repa
Prelude Data.Array.Repa> import System.Random
Prelude Data.Array.Repa System.Random> randomList = randoms (mkStdGen 0)
Prelude Data.Array.Repa System.Random> shape = (Z :. 1000000)
Prelude Data.Array.Repa System.Random> array = fromFunction shape $ \(Z :. i) -> randomList !! i
Prelude Data.Array.Repa System.Random> sumP array

仍然没有结果:

enter image description here

我非常感谢对此事的任何进一步协助。


2
它是否使用了线程化运行时进行编译? - MathematicalOrchid
5
repa的README实际上只有3个句子,没有不阅读它的借口。其中之一是:“使用Repa combinator编写的函数在提供 +RTS -Nwhatever 命令行参数运行程序时会自动并行。”请注意不要改变原意。 - user2407038
6
我不同意这些负评。是的,原帖作者错过了打开线程运行时的步骤,正如文档所建议的那样。但至少这个问题展示了他在诊断问题方面的努力。我希望所有在SO上的问题都能像这个问题一样展示出这么多的努力。 - chi
我更新了原始问题。如果我仍然误解了您,请告诉我。 - ethanabrooks
1个回答

0

出于某种原因,似乎 ghci 忽略了某些输入选项,因此像 sumP 这样的单子计算只会使用一个 CPU 核心。然而,这个实验的目的是为了在我正在开发的个人项目中使用多个核心,并且我成功地达到了这个目标。我认为关键是在我的 .cabal 文件中的 ghc-options 下添加了 -with-rtsopts=-N。最终的 ghc-options 如下:

executable write
  hs-source-dirs:      app
  main-is:             Write.hs
  ghc-options:         -Odph 
                       -rtsopts 
                       -with-rtsopts=-N
                       -threaded 
                       -fno-liberate-case 
                       -funfolding-use-threshold1000 
                       -funfolding-keeness-factor1000 
                       -fllvm 
                       -optlo-O3

嗯,ghci应该忽略了哪些选项?看起来你根本没有使用过例如+RTS -N4的推荐方式(就像README和评论者所说的那样)! - leftaroundabout
我认为大部分评论者关注的关键点是,即使在你问题的更新中指出你使用了-with-rtsopts=-N,但你回答的措辞以及传递给stack ghcighc-options似乎表明你直到现在都没有使用该选项,这在Repa的README中有说明。请注意,-with-rtsopts=-N与直接传递+RTS -N相同,这表明运行时将使用机器上的所有核心。 - badcook
请注意,仅指定“-threaded”是不够的,因为那只是一个编译时选项,仅打开多核支持;它启用了“-N$SOME_NUM_HERE”标志。 RTS选项是运行时选项(RTS代表运行时系统),因此可以针对每个不同的程序运行更改(一次运行您可以将最大核心数限制为3,另一次可能是1)。 - badcook
抱歉!那是一个错误。正如屏幕截图所证明的那样,我确实将-with-rtsopts=-N传递给了ghci,但没有在cabal文件中传递给ghc-options。我认为如果它对ghci不起作用,那么对编译后的代码也不会起作用。结果证明这个假设是错误的。 - ethanabrooks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接