Я написал приложение на 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 действительно большие (10 МБ), и, вероятно, их нужно просмотреть 500 000. Это означает, что моя программа очень медленная из-за декодирования 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
.|
наbuffer'
иrunConduit
сrunCConduit
. - person user2407038   schedule 19.01.2018