Haskell - Некоторые вопросы о System.Process и многопоточности - PullRequest
4 голосов
/ 19 января 2010

У меня есть небольшое численное моделирование в C (мне нужно было сделать это в C, чтобы поделиться им с моим советником), но я хочу использовать «скрипт на Haskell», как для организации моделирования. Программа принимает некоторые аргументы командной строки и выводит некоторые данные, которые я хотел бы перенаправить в файл, поэтому я сделал что-то вроде этого:

 import Control.Monad
 import System.Process

У меня есть функция для создания имени выходного файла:

filename :: Int -> String  
filename n = some stuff here...

и команда, которую я хочу выполнить:

command :: Int -> String
command n = "./mycutesimulation " ++ show n ++ " >" ++ filename n

и, наконец, я создаю список прогонов, которые я хочу сделать, и запускаю их с runCommand:

commands = map command [1,2..1000]

main = do
   sequence_ $ map runCommand commands

Проблема в том, что после запуска этого «скрипта» мой компьютер практически зависает при загрузке. Выполняемая программа очень легка в использовании памяти и выполняется за доли секунды. Этого не должно быть.

Итак, мои вопросы:

1) Я только что бросил 1000 процессов для одновременного выполнения ??? Как я могу выполнить их в рациональном порядке - последовательно или всего лишь несколькими процессами одновременно.

2) Я запускаю это в четырехъядерном ядре, и было бы неплохо использовать это в мою пользу. Есть ли способ, которым я могу скомпилировать это с этим флагом -threaded и получить процессы, которые будут выполняться одновременно, но организованно?

Ответы [ 3 ]

4 голосов
/ 19 января 2010

Вам нужно waitForProcess =<< runCommand.

import System.Process

main = sequence $ map (\x -> runCommand x) commands
 where commands = map (\x -> "echo " ++ show x) [1, 2..1000]

имеет симптомы, похожие на ваши, но

import System.Process

main = sequence $ map (\x -> waitForProcess =<< runCommand x) commands
 where commands = map (\x -> "echo " ++ show x) [1, 2..1000]

работает.

3 голосов
/ 19 января 2010

Прежде всего вы должны проверить топ или менеджер задач, чтобы увидеть, действительно ли вы создаете 1000 процессов в быстрой последовательности, а затем искать решение, основанное на этом.

Простой способ замедлить создание процесса - дождаться завершения каждого процесса, прежде чем создавать следующий. Поэтому вместо сопоставления runCommand с вашим commands вы должны отобразить свою собственную функцию, которая сначала вызывает runCommand, а затем вызывает waitForProcess для возвращенного ProcessHandle, т.е. каждый вызов вашей вспомогательной функции будет блокироваться до тех пор, пока не будет создан закончил.

Недостатком вышеупомянутого решения является то, что оно будет использовать только одно из ваших четырех ядер. Итак, что вы можете сделать, чтобы использовать все четыре ядра - это partition commands на четыре (или столько ядер, сколько вы хотите использовать) списков, а затем создать четыре рабочих потока с forkIO для каждого подсписка, который будет каждый запустите map в этом подсписке.

Btw. mapM_ f == sequence_ . map f

1 голос
/ 19 января 2010

Вот быстрое и грязное «запустить несколько за раз», если это поможет:

import System.Process

commands = replicate 16 "sleep 2"

runSome handles cmd = do
    (h:hs) <- handles
    waitForProcess h
    h' <- runCommand cmd
    return $ hs ++ [h']

test n = 
    let initial = mapM runCommand $ take n commands
    in foldl runSome initial (drop n commands)

Это просто (неправильно) использует список в качестве простой очереди, запускает столько команд, сколько вы ему скажете, затем ожидает одну в начале очереди и, когда это будет сделано, добавляет новую команду. Обратите внимание, что это не будет вести себя идеально, если смешать несколько длительных команд, но может быть достаточно для вас. Пожалуйста, не думайте, что это вообще «правильный» способ сделать это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...