content: import Gauge
  import Gauge.Main
  import Control.Concurrent (threadDelay)
  import UnliftIO
  import qualified Control.Concurrent.Async as A
  import Data.List (foldl')
  import Control.Applicative (liftA2, (<|>), empty)
  
  sizes :: (Int -> [Benchmark]) -> [Benchmark]
  sizes f = map
    (\size -> bgroup (show size) (f size))
    [1, 2, 10, 100, 1000, 10000, 100000]
  
  sum' :: [Int] -> Int
  sum' = foldl' (+) 0
  {-# INLINE sum' #-}
  
  replicateA_ :: Applicative f => Int -> f () -> f ()
  replicateA_ cnt0 f =
      let go 1 = f
          go i = f *> go (i - 1)
       in go cnt0
  {-# INLINE replicateA_ #-}
  
  main :: IO ()
  main = defaultMain
    [ bgroup "concurrently, minimal work" $ sizes $ \size ->
      [ bench "A.replicateConcurrently_" $ whnfIO $ do
          ref <- newIORef (0 :: Int)
          A.replicateConcurrently_ size $ atomicModifyIORef' ref $ \i -> (i + 1, ())
      , bench "replicateConcurrently_" $ whnfIO $ do
          ref <- newIORef (0 :: Int)
          replicateConcurrently_ size $ atomicModifyIORef' ref $ \i -> (i + 1, ())
      , bench "Conc" $ whnfIO $ do
          ref <- newIORef (0 :: Int)
          runConc $ replicateA_ size $ conc $ atomicModifyIORef' ref $ \i -> (i + 1, ())
      ]
    , bgroup "concurrently, no results" $ sizes $ \size ->
      [ bench "A.replicateConcurrently_" $ whnfIO $ A.replicateConcurrently_ size (pure ())
      , bench "replicateConcurrently_" $ whnfIO $ replicateConcurrently_ size (pure ())
      , bench "Conc" $ whnfIO $ runConc $ replicateA_ size $ conc $ pure ()
      , bench "Conc, cheating" $ whnfIO $ runConc $ replicateA_ size $ pure ()
      ]
    , bgroup "concurrently, with results" $ sizes $ \size ->
        [ bench "A.mapConcurrently" $ whnfIO $ fmap sum' $ A.mapConcurrently pure [1..size]
        , bench "mapConcurrently" $ whnfIO $ fmap sum' $ mapConcurrently pure [1..size]
        , bench "Conc" $ whnfIO $ runConc $
            let go i
                  | i == size = conc (pure i)
                  | otherwise = liftA2 (+) (conc (pure i)) (go (i + 1))
             in go 1
        -- This is cheating, since it's using our Pure data constructor
        , bench "Conc, cheating" $ whnfIO $ runConc $
            let go i
                  | i == size = pure i
                  | otherwise = liftA2 (+) (pure i) (go (i + 1))
             in go 1
        ]
    , bgroup "race" $ sizes $ \size ->
      [ bench "A.Concurrently" $ whnfIO $
          A.runConcurrently $
          foldr (<|>) empty (replicate size (pure ()))
      , bench "Concurrently" $ whnfIO $
          runConcurrently $
          foldr (<|>) empty (replicate size (pure ()))
      , bench "Conc" $ whnfIO $
          runConc $
          foldr (<|>) empty (replicate size (conc (pure ())))
      -- This is cheating, since it's using our Pure data constructor
      , bench "Conc, cheating" $ whnfIO $
          runConc $
          foldr (<|>) empty (replicate size (pure ()))
      ]
    , bgroup "race (with result)" $
        sizes $ \size ->
          [ bench "Concurrently" $
            whnfIO $
            runConcurrently $
            let go i
                  | i == size = Concurrently (pure i)
                  | otherwise = liftA2 (+) (Concurrently (pure i)) (go (i + 1))
             in (Concurrently $ threadDelay maxBound >> return 0) <|> (go 1) <|>
                (Concurrently $ threadDelay maxBound >> return 0)
          , bench "Conc" $
            whnfIO $
            runConc $
            let go i
                  | i == size = conc (pure i)
                  | otherwise = liftA2 (+) (conc (pure i)) (go (i + 1))
             in (conc $ threadDelay maxBound >> return 0) <|> (go 1) <|>
                (conc $ threadDelay maxBound >> return 0)
          , bench "Conc, cheating" $
            whnfIO $
            runConc $
            let go i
                  | i == size = conc (pure i)
                  | otherwise = liftA2 (+) (pure i) (go (i + 1))
             in (conc $ threadDelay maxBound >> return 0) <|> (go 1) <|>
                (conc $ threadDelay maxBound >> return 0)
          ]
      , let size = 10
         in bgroup
              "race (nested)"
              [ bench "Concurrently" $
                whnfIO $
                runConcurrently $
                let go i
                      | i == size = Concurrently (pure i)
                      | i `mod` 2 == 0 =
                        (liftA2 (+) (Concurrently (pure i)) (go (i + 1))) <|>
                        (liftA2 (+) (Concurrently (pure i)) (go (i + 2)))
                      | otherwise =
                        liftA2 (+) (Concurrently (pure i)) (go (i + 1))
                 in go 1
              , bench "Conc" $
                whnfIO $
                runConc $
                let go i
                      | i == size = conc (pure i)
                      | i `mod` 2 == 0 =
                        (liftA2 (+) (conc (pure i)) (go (i + 1))) <|>
                        (liftA2 (+) (conc (pure i)) (go (i + 2)))
                      | otherwise = liftA2 (+) (conc (pure i)) (go (i + 1))
                 in go 1
              , bench "Conc, cheating" $
                whnfIO $
                runConc $
                let go i
                      | i == size = conc (pure i)
                      | i `mod` 2 == 0 =
                        (liftA2 (+) (pure i) (go (i + 1))) <|>
                        (liftA2 (+) (pure i) (go (i + 2)))
                      | otherwise = liftA2 (+) (pure i) (go (i + 1))
                 in go 1
              ]
    ]
  