Haskell Concurrent Search with Early Interrupt

I would like to do a list search by testing each element of property X and then return when an element with property X is found.

This list is very large and will benefit from parallelism, but the cost of the spark is quite high relative to the calculation time. parListChunkit would be great, but then he should search the entire list.

Is there a way to write something like parListChunk, but with an early interrupt?

This is a naive search code:

hasPropertyX :: Object -> Bool

anyObjectHasPropertyX :: [Object] -> Bool
anyObjectHasPropertyX [] = False
anyObjectHasPropertyX l
| hasPropertyX (head l) == True = True
| otherwise = anyObjectHasPropertyX (tail l)

and this is my first attempt at parallelism:

anyObjectHasPropertyXPar [] = False
anyObjectHasPropertyXPar [a] = hasPropertyX a
anyObjectHasPropertyXPar (a:b:rest) = runEval $ do c1 <- rpar (force (hasPropertyX a))
                                                   c2 <- rpar (force (hasPropertyX b))
                                                   rseq c1
                                                   rseq c2
                                                   if (c1 == True) || (c2 == True) then return True else return (anyObjectHasPropertyXPar rest)

, ( -N1, ), ( , ). , , .

, parListChunk, , ?

: , , . rseq -

if (c1 == True) || (c2 == True) then ...

, ?

+4
1

, Control.Parallel.Strategies. , " parallelism", . , , , .

: , True, , . , , Strategies. , , , .

parFind, IO Control.Concurrent , , , . MVars: runningV , , , ; resultV Just Nothing, . , , , ( hasPropertyX ) , , .

import Control.Monad
import Control.Concurrent
import Data.List
import System.Environment

-- Thin a list to every `n`th element starting with index `i`
thin :: Int -> Int -> [a] -> [a]
thin i n = unfoldr step . drop i
  where step [] = Nothing
        step (y:ys) = Just (y, drop (n-1) ys)

-- Use `n` parallel threads to find first element of `xs` satisfying `f`
parFind :: Int -> (a -> Bool) -> [a] -> IO (Maybe a)
parFind n f xs = do
  resultV <- newEmptyMVar
  runningV <- newMVar n
  comparisonsV <- newMVar 0
  threads <- forM [0..n-1] $ \i -> forkIO $ do
    case find f (thin i n xs) of
      Just x -> void (tryPutMVar resultV (Just x))
      Nothing -> do m <- takeMVar runningV
                    if m == 1
                      then void (tryPutMVar resultV Nothing)
                      else putMVar runningV (m-1)
  result <- readMVar resultV
  mapM_ killThread threads
  return result

myList :: [Int]
myList = [1..1000000000]

-- Use `n` threads to find first element equal to `y` in `myList`
run :: Int -> Int -> IO ()
run n y = do x <- parFind n (== y) myList
             print x

-- e.g.,  stack ghc -- -O2 -threaded SearchList.hs
--        time ./SearchList +RTS -N4 -RTS 4 12345  # find 12345 using 4 threads -> 0.018s
--        time ./SearchList +RTS -N4 -RTS 4 -1     # full search w/o match -> 6.7s
main :: IO ()
main = do [n,y] <- getArgs
          run (read n) (read y)

, , , . , (1) , "" ; (2) , , .

, - - , , , , .

, , , , , , .

+2
source

Source: https://habr.com/ru/post/1679560/


All Articles