【发布时间】:2018-02-28 06:42:57
【问题描述】:
我在 Haskell 中编写了一个应用程序,它执行以下操作:
- 递归列出目录,
- 从目录列表中解析 JSON 文件,
- 寻找匹配的键值对,并且
- 返回找到匹配项的文件名。
我的这个应用程序的第一个版本是我能写的最简单、幼稚的版本,但我注意到空间使用量似乎单调增加。
因此,我切换到conduit,现在我的主要功能如下所示:
conduitFilesFilter :: ProjectFilter -> Path Abs Dir -> IO [Path Abs File]
conduitFilesFilter projFilter dirname' = do
(_, allFiles) <- listDirRecur dirname'
C.runConduit $
C.yieldMany allFiles
.| C.filterMC (filterMatchingFile projFilter)
.| C.sinkList
现在我的应用程序的内存使用量有限,但仍然很慢。对此,我有两个问题。
1)
我使用stack new 生成骨架来创建这个应用程序,它默认使用ghc 选项-threaded -rtsopts -with-rtsopts=-N。
(对我而言)令人惊讶的是,当我实际运行该应用程序时,它使用了所有可用的处理器(目标机器中大约有 40 个)。但是,我没有编写要并行运行的应用程序的任何部分(实际上我考虑过)。
什么是并行运行的?
2)
此外,大多数 JSON 文件都非常大(10mb),可能有 500k 需要遍历。这意味着由于所有 Aeson 解码,我的程序非常慢。我的想法是并行运行我的filterMatchingFile 部分,但是查看stm-conduit 库,我看不到在少数处理器上并行运行此中间操作的明显方法。
谁能建议一种使用stm-conduit 或其他方式巧妙地并行化我上面的函数的方法?
编辑
我意识到我可以将我的readFile -> decodeObject -> runFilterFunction 分解为conduit 的单独部分,然后我可以在有界通道中使用stm-conduit。也许我会试一试...
我使用+RTS -s 运行我的应用程序(我将其重新配置为-N4),我看到以下内容:
115,961,554,600 bytes allocated in the heap
35,870,639,768 bytes copied during GC
56,467,720 bytes maximum residency (681 sample(s))
1,283,008 bytes maximum slop
145 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 108716 colls, 108716 par 76.915s 20.571s 0.0002s 0.0266s
Gen 1 681 colls, 680 par 0.530s 0.147s 0.0002s 0.0009s
Parallel GC work balance: 14.99% (serial 0%, perfect 100%)
TASKS: 10 (1 bound, 9 peak workers (9 total), using -N4)
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.001s ( 0.007s elapsed)
MUT time 34.813s ( 42.938s elapsed)
GC time 77.445s ( 20.718s elapsed)
EXIT time 0.000s ( 0.010s elapsed)
Total time 112.260s ( 63.672s elapsed)
Alloc rate 3,330,960,996 bytes per MUT second
Productivity 31.0% of total user, 67.5% of total elapsed
gc_alloc_block_sync: 188614
whitehole_spin: 0
gen[0].sync: 33
gen[1].sync: 811204
【问题讨论】:
-
1) 几乎可以肯定没有。您需要明确并行性(Haskell 只是让您的代码更容易并行化 - 它不适合您)。
-
嗨@Alec。这正是我的想法,但是当我查看 htop 中的进程时,子进程与 CPU 一样多,并且每个子进程似乎都在做某事(主要是“S”)。这就是提出这个问题的原因。它只是在做并行 GC(这是一回事吗?)?
-
将
.|替换为buffer',将runConduit替换为runCConduit。 -
@erewok 并行 GC 绝对是一回事。 (它甚至在你的统计数据中就这么说!)我不是 100% 肯定,但 RTS 可能也在为 I/O 管理做一些线程。我怀疑您的大部分实际用户代码是并行的。
-
@MathematicalOrchid 感谢您的解释。这就是我认为可能会发生的事情。
标签: json haskell aeson conduit