atomic-primops 0.2.2.1 → 0.3
raw patch · 11 files changed
+334/−94 lines, 11 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Atomics.Counter.IORef: incrCounter :: Int -> AtomicCounter -> IO Int
+ Data.Atomics.Counter.Reference: incrCounter :: Int -> AtomicCounter -> IO Int
- Data.Atomics.Counter.Foreign: incrCounter :: AtomicCounter -> IO Int
+ Data.Atomics.Counter.Foreign: incrCounter :: Int -> AtomicCounter -> IO Int
- Data.Atomics.Counter.Foreign: newCounter :: IO AtomicCounter
+ Data.Atomics.Counter.Foreign: newCounter :: Int -> IO AtomicCounter
- Data.Atomics.Counter.IORef: newCounter :: IO AtomicCounter
+ Data.Atomics.Counter.IORef: newCounter :: Int -> IO AtomicCounter
- Data.Atomics.Counter.Reference: newCounter :: IO AtomicCounter
+ Data.Atomics.Counter.Reference: newCounter :: Int -> IO AtomicCounter
Files
- DEVLOG.md +96/−0
- Data/Atomics/Counter.hs +4/−2
- Data/Atomics/Counter/Foreign.hs +22/−11
- Data/Atomics/Counter/IORef.hs +30/−7
- Data/Atomics/Counter/Reference.hs +29/−8
- atomic-primops.cabal +14/−7
- cbits/RtsDup.c +92/−0
- cbits/primops.cmm +15/−13
- testing/Test.hs +11/−21
- testing/runTest.hs +0/−15
- testing/test-atomic-primops.cabal +21/−10
DEVLOG.md view
@@ -151,6 +151,7 @@ [2013.04.20] {Debugging}+------------------------ I fixed a major bug in the primop. I also verified that GC is what is causing all_hammer_one to observe fewer successes than ideal.@@ -197,4 +198,99 @@ cabal-dev install "-v --enable-library-profiling --ghc-options=-prof" -s cabal-dev/ghc-7.6.2_prof --disable-documentation --with-ghc=ghc-7.6.2 --program-suffix=_ghc-7.6.2 . How did that cause it to compile and yet run with an internal failure?++++[2013.07.18] {Timing atomic Counter ops}+================================================================================++The foreign/unboxed counter is the fasted for single thread repeated incr:++ Laptop, Macbook Retina (4 physical core w/ hyperthreading):+ + $ ./dist/build/test-atomic-primops/test-atomic-primops -j 1 +RTS -N4 -RTS -t single+ + Timing readIORef/writeIORef on one thread+ SELFTIMED: 0.137 sec+ Final value: 10000000+ RAW_single_thread_repeat_incr: [OK] ++ Timing CAS increments on one thread without retries+ SELFTIMED: 0.220 sec+ Final value: 10000000+ CAS_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.510 sec+ CounterReference_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.187 sec+ CounterIORef_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.119 sec+ CounterForeign_single_thread_repeat_incr: [OK]++ Desktop, veronica westmere 3.1 ghz, 4 core no-HT:++ SELFTIMED: 0.137 sec+ Final value: 10000000+ Timing CAS increments on one thread without retries+ RAW_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.228 sec+ Final value: 10000000+ CAS_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.638 sec+ CounterReference_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.242 sec+ CounterIORef_single_thread_repeat_incr: [OK]+ + SELFTIMED: 0.135 sec+ CounterForeign_single_thread_repeat_incr: [OK]++The numbers for boolean-flipping (nots) are different than adds:++ Timing readIORef/writeIORef on one thread+ SELFTIMED: 0.097 sec+ Timing CAS boolean flips on one thread without retries+ RAW_single_thread_repeat_flip: [OK]+ + SELFTIMED: 0.136 sec+ Final value: True+ Timing readIORef/writeIORef on one thread+ CAS_single_thread_repeat_flip: [OK]+++--------------------------------------------------++With max contention on four threads (all threads hammering counter),+the atomicModifyIORef version falls apart. E.g. for 1M total+increments here are the numbers:++ Laptop, Macbook Retina (4 physical core w/ hyperthreading):+ SELFTIMED: 45.768 sec+ CounterReference_concurrent_repeat_incr: [OK]+ + SELFTIMED: 0.348 sec+ CounterIORef_concurrent_repeat_incr: [OK]++ SELFTIMED: 0.060 sec+ CounterForeign_concurrent_repeat_incr: [OK]++ Desktop, veronica westmere 3.1 ghz, 4 core no-HT:++ SELFTIMED: 14.260 sec+ CounterReference_concurrent_repeat_incr: [OK]+ + SELFTIMED: 0.426 sec+ CounterIORef_concurrent_repeat_incr: [OK]+ + SELFTIMED: 0.066 sec+ CounterForeign_concurrent_repeat_incr: [OK]+ +MASSIVE difference between the foreign fetchAndAdd version and the+atomicModifyIORef. But the difference is MUCH worse on my laptop.+The laptop has hyperthreading, but in the benchmark we only use four+unpinned threads, not eight....
Data/Atomics/Counter.hs view
@@ -2,7 +2,9 @@ module Data.Atomics.Counter (- module Data.Atomics.Counter.IORef+-- module Data.Atomics.Counter.IORef+ module Data.Atomics.Counter.Foreign ) where -import Data.Atomics.Counter.IORef+--import Data.Atomics.Counter.IORef+import Data.Atomics.Counter.Foreign
Data/Atomics/Counter/Foreign.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE BangPatterns #-} --- | This implementation stores an unboxed counter and uses FFI--- operations to modify its contents.+-- | This implementation stores an unboxed counter and uses FFI operations to modify+-- its contents. It has the advantage that it can use true fetch-and-add operations.+-- It has the disadvantage of extra overhead due to FFI calls. module Data.Atomics.Counter.Foreign where@@ -15,23 +16,32 @@ type CTicket = Int --- | Create a new counter initialized to zero.-newCounter :: IO AtomicCounter-newCounter = do x <- mallocForeignPtr- writeCounter x 0- return x+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter n = do x <- mallocForeignPtr+ writeCounter x n+ -- Do we need a write barrier here?+ return x --- | Try repeatedly until we successfully increment the counter.--- Returns the original value before the increment.-incrCounter :: AtomicCounter -> IO Int-incrCounter r = withForeignPtr r$ \r' -> fetchAndAdd r' 1 +-- | Increment the counter by a given amount.+-- Returns the original value before the increment.+-- +-- Note that UNLIKE with boxed implementations of counters, where increment is+-- based on CAS, this increment is /O(1)/. Fetch-and-add does not require a retry+-- loop like CAS.+incrCounter :: Int -> AtomicCounter -> IO Int+incrCounter bump r = withForeignPtr r$ \r' -> fetchAndAdd r' bump +-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations. readCounterForCAS :: AtomicCounter -> IO CTicket readCounterForCAS = readCounter +-- | Opaque tickets cannot be constructed, but they can be destructed into values. peekCTicket :: CTicket -> Int peekCTicket x = x +-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`. readCounter :: AtomicCounter -> IO Int readCounter r = withForeignPtr r peek @@ -39,6 +49,7 @@ writeCounter :: AtomicCounter -> Int -> IO () writeCounter r !new = withForeignPtr r $ \r' -> poke r' new +-- | Compare and swap for the counter ADT. casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket) casCounter r !tick !new = withForeignPtr r $ \r' -> do b <- compareAndSwap r' tick new
Data/Atomics/Counter/IORef.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE BangPatterns #-} --- | This version uses a boxed IORef representation, but it can be--- somewhat cheaper because it uses raw CAS rather than full atomicModifyIORef.+-- | This version uses a boxed IORef representation, but it can be somewhat cheaper+-- than the Refence version because it uses raw CAS rather than full+-- atomicModifyIORef. module Data.Atomics.Counter.IORef where@@ -16,25 +17,47 @@ type CTicket = Ticket Int --- | Create a new counter initialized to zero.-newCounter :: IO AtomicCounter-newCounter = fmap AtomicCounter $ newIORef 0+{-# INLINE newCounter #-}+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter n = fmap AtomicCounter $ newIORef n --- | Try repeatedly until we successfully increment the counter.--- incrCounter =+{-# INLINE incrCounter #-}+-- | Try repeatedly until we successfully increment the counter by a given amount.+-- Returns the original value of the counter (pre-increment).+incrCounter :: Int -> AtomicCounter -> IO Int+-- <DUPLICATED CODE FROM Reference.hs>+incrCounter bump cntr =+ loop =<< readCounterForCAS cntr+ where+ loop tick = do+ (b,tick') <- casCounter cntr tick (peekCTicket tick + bump)+ if b then return (peekCTicket tick')+ else loop tick'+-- </DUPLICATED CODE FROM Reference.hs> +{-# INLINE readCounterForCAS #-}+-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations. readCounterForCAS :: AtomicCounter -> IO CTicket readCounterForCAS (AtomicCounter r) = readForCAS r +{-# INLINE peekCTicket #-}+-- | Opaque tickets cannot be constructed, but they can be destructed into values. peekCTicket :: CTicket -> Int peekCTicket = peekTicket +{-# INLINE readCounter #-}+-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`. readCounter :: AtomicCounter -> IO Int readCounter (AtomicCounter r) = readIORef r +{-# INLINE writeCounter #-} -- | Make a non-atomic write to the counter. No memory-barrier. writeCounter :: AtomicCounter -> Int -> IO () writeCounter (AtomicCounter r) !new = writeIORef r new +{-# INLINE casCounter #-}+-- | Compare and swap for the counter ADT. casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket) casCounter (AtomicCounter r) tick !new = casIORef r tick new
Data/Atomics/Counter/Reference.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE BangPatterns #-} -- | This reference version is implemented with atomicModifyIORef and can be a useful--- fallback if one of the other implementations needs to be debugged.+-- fallback if one of the other implementations needs to be debugged for a given+-- architecture. module Data.Atomics.Counter.Reference where @@ -16,26 +17,46 @@ type CTicket = Int --- | Create a new counter initialized to zero.-newCounter :: IO AtomicCounter-newCounter = fmap AtomicCounter $ newIORef 0---- | Try repeatedly until we successfully increment the counter.--- incrCounter =+{-# INLINE newCounter #-}+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter !n = fmap AtomicCounter $ newIORef n +{-# INLINE incrCounter #-}+-- | Try repeatedly until we successfully increment the counter by a given amount.+-- Returns the original value of the counter (pre-increment).+incrCounter :: Int -> AtomicCounter -> IO Int+incrCounter !bump !cntr =+ loop =<< readCounterForCAS cntr+ where+ loop tick = do+ (b,tick') <- casCounter cntr tick (peekCTicket tick + bump)+ if b then return (peekCTicket tick')+ else loop tick'+ +{-# INLINE readCounterForCAS #-}+-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations. readCounterForCAS :: AtomicCounter -> IO CTicket readCounterForCAS = readCounter +{-# INLINE peekCTicket #-}+-- | Opaque tickets cannot be constructed, but they can be destructed into values. peekCTicket :: CTicket -> Int-peekCTicket x = x+peekCTicket !x = x +{-# INLINE readCounter #-}+-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`. readCounter :: AtomicCounter -> IO Int readCounter (AtomicCounter r) = readIORef r +{-# INLINE writeCounter #-} -- | Make a non-atomic write to the counter. No memory-barrier. writeCounter :: AtomicCounter -> Int -> IO () writeCounter (AtomicCounter r) !new = writeIORef r new +{-# INLINE casCounter #-}+-- | Compare and swap for the counter ADT. casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket) casCounter (AtomicCounter r) oldT !new =
atomic-primops.cabal view
@@ -1,5 +1,5 @@ Name: atomic-primops-Version: 0.2.2.1+Version: 0.3 License: BSD3 License-file: LICENSE Author: Ryan Newton@@ -19,15 +19,12 @@ -- 0.2 -- Critical bugfix and add Counter. -- 0.2.2 -- Add more counters -- 0.2.2.1 -- Minor, add warning.+-- 0.3 -- Major internal change. Duplicate GHC RTS barriers and support non -threaded. Synopsis: A safe approach to CAS and other atomic ops in Haskell. Description: - WARNING: Your program MUST be built with "ghc -threaded" if you depend on this library.- The library depends on threaded Haskell RTS, which is only conditionally linked.- If you don't do this you will see an error at link time about "cas" being missing.- . After GHC 7.4 a new `casMutVar#` primop became available, but it's difficult to use safely, because pointer equality is a highly unstable property in Haskell. This library provides a safer method@@ -36,9 +33,15 @@ Also, this library uses the "foreign primop" capability of GHC to add access to other variants that may be of interest, specifically, compare and swap inside an array.+ .+ Changes in 0.3:+ .+ * Major internal change. Duplicate the barrier code from the GHC RTS and thus + enable support for executables that are NOT built with '-threaded'. + Extra-Source-Files: DEVLOG.md,- testing/runTest.hs, testing/Test.hs, testing/test-atomic-primops.cabal+ testing/Test.hs, testing/test-atomic-primops.cabal -- Makefile, Test.hs, README.md Flag debug@@ -65,7 +68,7 @@ Include-Dirs: cbits C-Sources: cbits/primops.cmm- CC-Options: -Wall+ CC-Options: -Wall ghc-prof-options: -DGHC_PROFILING_ON -- if( cabal-version < 1.17 ) { -- ghc-prof-options: ERROR_DO_NOT_BUILD_THIS_WITH_PROFILING_YET__SEE_CABAL_ISSUE_1284@@ -73,6 +76,10 @@ if flag(debug) cpp-options: -DDEBUG_ATOMICS++ -- Duplicate RTS functionality: + C-Sources: cbits/RtsDup.c+ -- -- [2013.04.08] This isn't working presently: -- -- I'm having problems with building it along with the library; see DEVLOG.
+ cbits/RtsDup.c view
@@ -0,0 +1,92 @@+#define THREADED_RTS+#undef KEEP_INLINES++// Stg.h will pull in SMP.h:+// #include "Stg.h"+// Also having some problems with Regs.h.+// This is probably too big a chunk to suck in.++//--------------------------------------------------------------------------------+// #define EXTERN_INLINE inline+#define EXTERN_INLINE ++#include "MachDeps.h"+#include "stg/Types.h"++// Force the GHC RTS code to provide the desired symbols:+// #define IN_STGCRUN 1++// If I pull this in I get duplicated symbols:+// #include "stg/SMP.h"+//--------------------------------------------------------------------------------++/*+ * We need to tell both the compiler AND the CPU about the barriers.+ * It's no good preventing the CPU from reordering the operations if+ * the compiler has already done so - hence the "memory" restriction+ * on each of the barriers below.+ */+EXTERN_INLINE void+DUP_write_barrier(void) {+#if i386_HOST_ARCH || x86_64_HOST_ARCH+ __asm__ __volatile__ ("" : : : "memory");+#elif powerpc_HOST_ARCH+ __asm__ __volatile__ ("lwsync" : : : "memory");+#elif sparc_HOST_ARCH+ /* Sparc in TSO mode does not require store/store barriers. */+ __asm__ __volatile__ ("" : : : "memory");+#elif arm_HOST_ARCH && defined(arm_HOST_ARCH_PRE_ARMv7)+ __asm__ __volatile__ ("" : : : "memory");+#elif arm_HOST_ARCH && !defined(arm_HOST_ARCH_PRE_ARMv7)+ __asm__ __volatile__ ("dmb st" : : : "memory");+#elif !defined(WITHSMP)+ return;+#else+#error memory barriers unimplemented on this architecture+#endif+}++EXTERN_INLINE void+DUP_store_load_barrier(void) {+#if i386_HOST_ARCH+ __asm__ __volatile__ ("lock; addl $0,0(%%esp)" : : : "memory");+#elif x86_64_HOST_ARCH+ __asm__ __volatile__ ("lock; addq $0,0(%%rsp)" : : : "memory");+#elif powerpc_HOST_ARCH+ __asm__ __volatile__ ("sync" : : : "memory");+#elif sparc_HOST_ARCH+ __asm__ __volatile__ ("membar #StoreLoad" : : : "memory");+#elif arm_HOST_ARCH && !defined(arm_HOST_ARCH_PRE_ARMv7)+ __asm__ __volatile__ ("dmb" : : : "memory");+#elif !defined(WITHSMP)+ return;+#else+#error memory barriers unimplemented on this architecture+#endif+}++EXTERN_INLINE void+DUP_load_load_barrier(void) {+#if i386_HOST_ARCH+ __asm__ __volatile__ ("" : : : "memory");+#elif x86_64_HOST_ARCH+ __asm__ __volatile__ ("" : : : "memory");+#elif powerpc_HOST_ARCH+ __asm__ __volatile__ ("lwsync" : : : "memory");+#elif sparc_HOST_ARCH+ /* Sparc in TSO mode does not require load/load barriers. */+ __asm__ __volatile__ ("" : : : "memory");+#elif arm_HOST_ARCH && !defined(arm_HOST_ARCH_PRE_ARMv7)+ __asm__ __volatile__ ("dmb" : : : "memory");+#elif !defined(WITHSMP)+ return;+#else+#error memory barriers unimplemented on this architecture+#endif+}++// Load a pointer from a memory location that might be being modified+// concurrently. This prevents the compiler from optimising away+// multiple loads of the memory location, as it might otherwise do in+// a busy wait loop for example.+// #define VOLATILE_LOAD(p) (*((StgVolatilePtr)(p)))
cbits/primops.cmm view
@@ -1,16 +1,18 @@ #include "Cmm.h" -// Duplicate some of the RTS here (CAS instruction).-// #include "RtsDup.h"+#warning "Duplicating functionality from the GHC RTS..."+#define WHICH_CAS cas+#define WHICH_SLBARRIER DUP_store_load_barrier+#define WHICH_LLBARRIER DUP_load_load_barrier+#define WHICH_WBARRIER DUP_write_barrier -// #include "Cmm.h"-// Problems if we try this:-// #include "stg/SMP.h"-// #include "Rts.h"+/* #define WHICH_CAS cas */+/* #define WHICH_SLBARRIER store_load_barrier */+/* #define WHICH_LLBARRIER load_load_barrier */+/* #define WHICH_WBARRIER write_barrier */ -// Defined in SMP.h: -// EXTERN_INLINE StgWord cas(StgVolatilePtr p, StgWord o, StgWord n);+// ================================================================================ add1Op /* Int# -> Int# */@@ -31,7 +33,7 @@ new = R4; p = arr + SIZEOF_StgMutArrPtrs + WDS(ind);- (h) = foreign "C" cas(p, old, new) [];+ (h) = foreign "C" WHICH_CAS(p, old, new) []; if (h != old) { // Failure, return what was there instead of 'old':@@ -61,7 +63,7 @@ // The "cas" function from the C runtime abstracts over // platform/architecture differences. It returns the old value, // which, if equal to "old", means success.- (h) = foreign "C" cas(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var,+ (h) = foreign "C" WHICH_CAS(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var, old, new) []; if (h != old) { // Failure:@@ -95,16 +97,16 @@ // handles the complexity of architecture-portability. We just need // to expose it here. stg_store_load_barrier {- foreign "C" store_load_barrier();+ foreign "C" WHICH_SLBARRIER(); RET_N(0); } stg_load_load_barrier {- foreign "C" load_load_barrier();+ foreign "C" WHICH_LLBARRIER(); RET_N(0); } stg_write_barrier {- foreign "C" write_barrier();+ foreign "C" WHICH_WBARRIER(); RET_N(0); }
testing/Test.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE MagicHash, UnboxedTuples, BangPatterns, ScopedTypeVariables, NamedFieldPuns, CPP #-}--- {-# LANGUAGE TemplateHaskell #-} -- | This test has three different modes which can be toggled via the -- C preprocessor. Any subset of the three may be activated.@@ -12,16 +11,11 @@ import Data.IORef (modifyIORef') import Data.Int import Data.Time.Clock--- import System.Mem.StableName--- import GHC.IO (unsafePerformIO) import Text.Printf--- import qualified GHC.Prim as P--- import GHC.ST import GHC.STRef import GHC.IORef import GHC.Stats (getGCStats, GCStats(..)) import Data.Primitive.Array--- import Control.Monad import Data.Word import qualified Data.Set as S import System.Random (randomIO, randomRIO)@@ -33,8 +27,6 @@ import Test.Framework (Test, defaultMain, testGroup) import Test.Framework.Providers.HUnit (testCase) --- import Text.Printf(fprintf)- import GHC.IO (unsafePerformIO) import System.Mem (performGC) import System.Mem.StableName (makeStableName, hashStableName)@@ -42,8 +34,6 @@ import System.IO (stdout, stderr, hPutStrLn, hFlush) import Debug.Trace (trace) --- import Test.Framework.TH (defaultMainGenerator)- import CommonTesting import CounterTests (counterTests) @@ -57,10 +47,15 @@ do GCStats{numGcs} <- getGCStats return numGcs | otherwise = return 0-#if 1--- main = $(defaultMainGenerator)+ main :: IO ()-main = +main = do+ -- TEMP: Fixing this at four processors because it takes a REALLY long time at larger numbers:+ -- It does 248 test cases and takes 55s at -N16...+ -- numcap <- getNumProcessors+ let numcap = 4+ setNumCapabilities numcap+ defaultMain $ [ testCase "casTicket1" case_casTicket1 , testCase "create_and_read" case_create_and_read@@ -80,7 +75,7 @@ -- Test several configurations of this one: [ testCase ("test_all_hammer_one_"++show threads++"_"++show iters ++":") (test_all_hammer_one threads iters (0::Int))- | threads <- [1 .. 2*numCapabilities]+ | threads <- [1 .. 2*numcap] , iters <- [1, 10, 100, 1000, 10000, 100000, 500000]] ++ [ testCase ("test_hammer_many_threads_1000_10000:") (test_all_hammer_one 1000 10000 (0::Int)) ] ++@@ -89,19 +84,14 @@ [ testCase ("test_random_array_comm_"++show threads++"_"++show size++"_"++show iters ++":") (test_random_array_comm threads size iters) | threads <- filter (>0) $ setify $- [1, numCapabilities `quot` 2, numCapabilities, 2*numCapabilities]+ [1, numcap `quot` 2, numcap, 2*numcap] , size <- [1, 10, 100] , iters <- [10000]] ++ counterTests- + setify :: [Int] -> [Int] setify = S.toList . S.fromList -#else-main = do- test_all_hammer_one 1 10000 (0::Int)- putStrLn "Test Done!"-#endif ------------------------------------------------------------------------ {-# NOINLINE mynum #-} mynum :: Int
− testing/runTest.hs
@@ -1,15 +0,0 @@--import GHC.Conc-import System.Process-import System.Exit---- Run the testing executable with different thread settings.-main = do--- p <- getNumProcessors- putStrLn "[TestHarness] Calling compiled test-atomic-primops executable..."- ExitSuccess <- system "./dist/build/test-atomic-primops/test-atomic-primops +RTS -N1"- -- TEMP: Fixing this at four processors because it takes a REALLY long time at larger numbers:- ExitSuccess <- system$"./dist/build/test-atomic-primops/test-atomic-primops +RTS -N4"- -- It does 248 test cases and takes 55s at -N16...--- ExitSuccess <- system$"./dist/build/test-atomic-primops/test-atomic-primops +RTS -N"++show p- return ()
testing/test-atomic-primops.cabal view
@@ -6,17 +6,28 @@ Build-type: Simple Cabal-version: >=1.8 -Executable test-atomic-primops+Flag opt+ Description: Enable GHC optimization.+ Default: True++Flag threaded+ Description: Enable GHC threaded RTS.+ Default: True++Test-Suite test-atomic-primops+ type: exitcode-stdio-1.0 main-is: Test.hs- ghc-options: -O2 -threaded -rtsopts+ ghc-options: -rtsopts - build-depends: base, ghc-prim, primitive, containers, random,- -- For Testing:- atomic-primops,+ if flag(opt)+ ghc-options: -O2 -funbox-strict-fields+ if flag(threaded)+ ghc-options: -threaded + + build-depends: base, ghc-prim, primitive, containers, random, atomic-primops,+ -- For Testing: time, HUnit, test-framework, test-framework-hunit--- test-framework-th -Test-Suite test-atomic-primops-runner- type: exitcode-stdio-1.0- main-is: runTest.hs- build-depends: base, process+Executable hello-world-atomic-primops+ main-is: hello.hs+ build-depends: base >= 4.5, atomic-primops