3

The following code produces the (dreaded) "nested parallelism" error with repa-3.4.0.1:

import Control.Monad.Identity (runIdentity, liftM)
import Data.Array.Repa              as R
import Data.Array.Repa.Repr.Unboxed
import Data.Vector.Unboxed
import Test.Framework
import Test.Framework.Providers.QuickCheck2
import Test.QuickCheck hiding (generate,output)

main :: IO ()
main = defaultMainWithArgs [prop,prop] ["--maximum-generated-tests=1"]

prop = testProperty "Test" $ property prop_fmap

prop_fmap :: Arr Int -> Bool
prop_fmap x = fmapT id x == x




newtype Arr r = Arr (Array U DIM1 r) deriving (Eq, Show)

instance (Arbitrary r, Unbox r) => Arbitrary (Arr r) where
    arbitrary = replM arbitrary
    shrink = shrinkNothing

replM :: (Unbox r, Monad mon) => mon r -> mon (Arr r)
replM = let n = 6
        in liftM (Arr . fromUnboxed (Z:.n)) . replicateM n

fmapT :: (Unbox a, Unbox b) => (a -> b) -> Arr a -> Arr b
fmapT f (Arr v) = Arr $ force' $ R.map f $ v

force' :: (Shape sh, Unbox r) => Array D sh r -> Array U sh r
force' = runIdentity . computeP

The exact error is:

Performing nested parallel computation sequentially.   You've probably
called the 'compute' or 'copy' function while another   instance was
already running. This can happen if the second version   was suspended
due to lazy evaluation. Use 'deepSeqArray' to ensure   that each array
is fully evaluated before you 'compute' the next one. 

I'm compiling with ghc Main -threaded and running with Main +RTS -N2. I've tried using deepSeqArray in the definition of fmapT, but it doesn't help. Since the tests are independent (aside from sequencing the randomness), it's not clear how nested parallelism is even possible in this example.

Interestingly, if I change main to just quickCheck prop_fmap, it successfully completes 100 tests. So it seems there's something going on with test-framework (probably a more general problem with monad sequencing) that is different from QuickCheck.

Any ideas on why I'm getting this error and how I can avoid it while still doing a parallel computation?

Note: using the Identity monad: ref1, ref2

Community
  • 1
  • 1
crockeea
  • 21,651
  • 10
  • 48
  • 101
  • Could this be related to [What's wrong with using Identity monad with mmultP when using repa?](http://stackoverflow.com/q/32981990/1333025) – Petr Jun 16 '16 at 09:10
  • @PetrPudlák I think the issue in this case is that `test-framework` is multi--threaded. This definitely leads to nested parallelism. Thanks for reminding me about this question! – crockeea Jun 16 '16 at 12:18

1 Answers1

1

The problem is that test-framework is implicitly multi-threaded (see this for example.)

The solution that works for me is to run defaultMainWithArgs with the option --threads=1.

crockeea
  • 21,651
  • 10
  • 48
  • 101
  • Does this mean that Repa can't be called from parallel threads? That seems to me strange, perhaps there is another way. – Petr Jun 16 '16 at 18:13
  • 1
    @PetrPudlák I asked Ben Lippmeier, "Is coarse-grained parallelism possible when using repa?" His response in a private communication: "The data parallel model assumes a single thread of control. It also assumes that there is enough work available to make use of all processors. Running two repa computations at once won’t make anything faster, and isn’t supported by the library. At best you’ll just suffer more scheduling overhead, which is why I added the warning [about nested parallelism]." – crockeea Jun 16 '16 at 20:06