------------------------------------------------------------------------------- -- XXX I'm not entirely happy with the way Haskell has -- twisted my arm to arrange the modules, but there you go. {- OPTIONS_GHC -O2 #-} {-# LANGUAGE CPP #-} -- Later: This (now) has no effect when new grammar is in effect. -- This is temporary, so seqaid demo output remains compatible -- with the documentation already written around it. (The new -- shrinkPat has more finesse, but you have to run the demo -- longer to see the heap stabilise -- the demonstration is -- not nearly as effective. Also, the patterns have a lot -- of #'s in them which looks noisy and confusing, although -- it's natural enough.) We could attempt vertical alignment -- since flanking whitespace is now supported, and that would -- do more than anything to improve the presentation... -- However, with DEMO_MODE off, one would definitely prefer -- the new shrinkPat. This comment will probably be -- as writ on water... #define USE_OLD_SHRINK_PAT 0 -- XXX Should scour the code for "max_depth" etc. -- all those -- names which are specific to this DEMO_MODE hack!... And get -- them into CPP guards. And get something sane in the OTHER -- branches of those guards!!... -- (Switch now promoted to .cabal flag.) --- #define DEMO_MODE 1 {- LANGUAGE BangPatterns #-} -- for debugging only ------------------------------------------------------------------------------- -- | -- Module : Seqaid.Global -- Copyright : Andrew G. Seniuk 2014-2015 -- License : BSD-style (see the file LICENSE) -- -- Maintainer : Andrew Seniuk -- Stability : provisional -- Portability : GHC -- -- Collects 'IORef's used by the seqaid runtime. -- -- This will be substantially reorganised and clarified soon. module Seqaid.Global ( #if DEMO_MODE depth_ioref , pattern_ioref , snk_ioref , #endif #if ! DEMO_MODE patterns_ioref , #endif stats_query_idx_ioref , counter_ioref , next_sample_at_ioref , bytes_allocated_ioref , bytes_allocated_prev_ioref , current_bytes_used_ioref , update_bytes_allocated_ioref , update_current_bytes_used_ioref , SiteID , sample_period , max_depth , fixed_pat , fixed_pat_sequence , ) where ------------------------------------------------------------------------------- import Control.DeepSeq.Bounded import Data.Typeable ( Typeable ) -- Stuff for monitoring resource use (i.e. computing objective function): --import Data.Word ( Word64 ) import GHC.Int ( Int64 ) import Data.IORef import System.IO.Unsafe ( unsafePerformIO ) import System.Mem ( performGC ) import GHC.Stats ( GCStats(..), getGCStats ) --import Debug.Trace ( trace ) --import Data.Array #if ! DEMO_MODE import qualified Data.HashTable.IO as H #endif ------------------------------------------------------------------------------- #if ! DEMO_MODE type HashTable k v = H.CuckooHashTable k v {-# NOINLINE patterns_ioref #-} patterns_ioref :: IORef (HashTable Int Pattern) patterns_ioref = unsafePerformIO $ do ht <- H.new ioref <- newIORef ht return ioref #endif ------------------------------------------------------------------------------- -- This group is very temporary!) -- definitely temporary hack (uniform forcing depth regardless of SiteID) {-# NOINLINE depth_ioref #-} depth_ioref :: IORef Int depth_ioref = unsafePerformIO $ newIORef 0 -- definitely temporary hack (uniform Pattern regardless of SiteID) {-# NOINLINE pattern_ioref #-} pattern_ioref :: IORef Pattern pattern_ioref = unsafePerformIO $ newIORef $ emptyPat -- definitely temporary hack (uniform SeqNode regardless of SiteID) {-# NOINLINE snk_ioref #-} snk_ioref :: IORef SeqNode snk_ioref = unsafePerformIO $ newIORef Insulate ------------------------------------------------------------------------------- {-# NOINLINE stats_query_idx_ioref #-} stats_query_idx_ioref :: IORef Int --stats_query_idx_ioref :: IORef Int64 stats_query_idx_ioref = unsafePerformIO $ newIORef 0 {-# NOINLINE counter_ioref #-} counter_ioref :: IORef Int64 counter_ioref = unsafePerformIO $ newIORef 0 {-# NOINLINE next_sample_at_ioref #-} next_sample_at_ioref :: IORef Int64 next_sample_at_ioref = unsafePerformIO $ newIORef sample_period --next_sample_at_ioref = unsafePerformIO $ newIORef 0 {-# NOINLINE bytes_allocated_ioref #-} bytes_allocated_ioref :: IORef Int64 bytes_allocated_ioref = unsafePerformIO $ newIORef 0 {-# NOINLINE bytes_allocated_prev_ioref #-} bytes_allocated_prev_ioref :: IORef Int64 bytes_allocated_prev_ioref = unsafePerformIO $ newIORef 0 {-# NOINLINE current_bytes_used_ioref #-} current_bytes_used_ioref :: IORef Int64 current_bytes_used_ioref = unsafePerformIO $ newIORef 0 ------------------------------------------------------------------------------- {-# NOINLINE update_bytes_allocated_ioref #-} update_bytes_allocated_ioref :: IO Int64 update_bytes_allocated_ioref = do oldsize <- readIORef bytes_allocated_ioref writeIORef bytes_allocated_prev_ioref oldsize performGC gcstats <- getGCStats let newsize = bytesAllocated gcstats #if 0 putStrLn $ "\nsize="++show size i <- readIORef counter_ioref t <- readIORef next_sample_at_ioref putStrLn $ "i="++show i++"\nt="++show t #endif writeIORef bytes_allocated_ioref newsize return $! newsize - oldsize -- return newsize ------------------------------------------------------------------------------- {-# NOINLINE update_current_bytes_used_ioref #-} update_current_bytes_used_ioref :: IO Int64 update_current_bytes_used_ioref = do performGC gcstats <- getGCStats let cbu = currentBytesUsed gcstats writeIORef current_bytes_used_ioref cbu return cbu ------------------------------------------------------------------------------- sample_period :: Int64 --sample_period = 1 --sample_period = 10 --sample_period = 400 --sample_period = 1000 sample_period = 4000 -- the usual for leaky-full --sample_period = 10000 -- XXX quack! (6 is minimal; 5 is too small; the leak remains) max_depth = 7 :: Int fixed_pat_sequence = let shrink_pat | deepseq_bounded_flag__new_improved_pattern_grammar = shrinkPat #if USE_OLD_SHRINK_PAT | otherwise = shrinkPat_old #else | otherwise = shrinkPat #endif in ( map (\i -> compilePat ('*':show i)) [0,1..8] ) ++ ( reverse $ condenseEq shrink_pat fixed_pat ) -- [Debugging notes for recent changes to deepseq-bounded.] -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 0 Int\n()\n" -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 1 Int\n()\n" -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 3 Int\n()\n" -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 7 Int\n()\n" -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 12 Int\n()\n" -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 13 Int\n()\n" -- leaky: DeepSeqBounded_PingException "NFDataP: PING: 14 Int\n()\n" -- This is what we see. If wait (only see 0 repeating for a while). -- (And the system grinds to a crawl, although it appears to be -- a stable crawl.) ----- -- See cotemp (20150111130916) 000-readme for the labelling of the tree... -- This mystery is largely solved. As for "slowdown" well, it's no -- wonder if we're trying to print something for every one of the -- gizillion exceptions, but if it was a quick stats update or something, -- might actually be fine at full speed... -- Speaking of "speed", a threadDelay-type attribute would be useful... -- Too useful not to do it! (Except that threadDelay is known to be -- a bit flakey...). (But you gotta try...) #if 1 fixed_pat | deepseq_bounded_flag__new_improved_pattern_grammar = setPatternPatNodeUniqueIDs 0 $ -- compilePat "=>cab((!(!)(((^!).!(!))))!(^!(!)))" -- compilePat "=>cab((!(!)(((+!).!(!))))!(+!(!)))" -- compilePat "((!(!)(((+^/!).!(!))))!(!(!)))" -- compilePat "((!(!)(((+^!).!(!))))!(!(!)))" -- compilePat "+^(+^(+^!+^(+^/!)+^/(+^/(+^/(+^/!)+^/.+^/!+^/(+^/!))))+^/!+^/(+^/!+^/(+^/!)))" -- compilePat "+^/(+^/(+^/!+^/(+^/!)+^/(+^/(+^/(+^/!)+^/.+^/!+^/(+^/!))))+^/!+^/(+^/!+^/(+^/!)))" -- compilePat "+^(+^(+^!+^(+^!)+^(+^(+^(+^!)+^.+^!+^(+^!))))+^!+^(+^!+^(+^!)))" -- compilePat "((!(!)+(+(+(+!).!(!))))!(!(!)))" -- yes, it works (if you wait a wee bit) -- trying the full-blanket-+'d one again!!! -- compilePat "((!(!)(((+!).!(!))))!+(!(!)))" -- -- compilePat "((!(!)((+(+!).!(!))))!(!(!)))" -- ACTUALLY WORKS -- at FIRST you see only TC, then after the next stats line you see "4 TC / 3 Int" (or maybe the other way around, but I think this is the order...) -- compilePat "((!(!)(((+!).!(!))))!(!(!)))" -- still works -- XXX Oh, I see what happened: I made !(!) -> . at some point, -- as part of testing, and this got propagated and forgotten, -- and changed back . -> ! (instead of !(!)) -- but this is -- harmless actually?!!! -- compilePat "((!(!)(((+^!)+^/.!(!))))!(!(!)))" -- compilePat "((!+^/.(((!)+^/.!(!))))!(!(!)))" -- nothing. -- compilePat "+(+(+!+(+!)+(+(+(+!).!(!))))!(!(!)))" -- XXX XXX -- compilePat "(+(!+.(+((!)+.!(!))))!(!(!)))" ---- compilePat "((!(!)((+.(!)+.+!(!))))!(!(!)))" ----- compilePat "+(+(+!+!+(+(+(+!).!(!))))!(!(!)))" -- XXX XXX -- compilePat "+(+(+!+(+!)+(+(+(+!).!(!))))!(!(!)))" -- XXX XXX -- compilePat "((+!^(!)(+((!).+^!/^(+^!))))!(!(!)))" -- yep -- compilePat "((+!^(!)(+((!).+^!^(+^!))))!(!(!)))" -- all working -- compilePat "((!(+^/.)(((!)+^/.!+^/.)))!(!(!)))" -- nothing -- compilePat "((!(/.)(((!)/.!/.)))!(!(!)))" -- nothing --- compilePat "((!/.(((!)/.!(!))))!(!(!)))" -- nope, neither calls handleAttrs (incididentally, this leaks noticeably and monotonically, while the one with just . on the Blob, if not technically leak-free [? it seems to accumulate heap and then periodically discharge it, notwithstanding our frequent performGC calls...], at least not exhibiting net growth over longrunning...) This is a better test at the moment as the first . is of simple base nullary type Int. -- compilePat "((!(!)(((!)/.!(!))))!(!(!)))" -- the problem with this test is that the .'d node is of unusual type Blob, which has hack NFData instances etc... --- compilePat "((!+.(((!)+.+!(!))))+!(!(+!)))" --- compilePat "((+!+.((+(+!)+.!(!))))+!(!(+!)))" --- compilePat "((!+.(((!)+.!(!))))!(!(!)))" --- compilePat "((!.(((!)+.!(!))))!(!(!)))" --- compilePat "((!.(((!)+.!(!))))!(!(!)))" -- compilePat "((!(!)(((!)+.!(!))))!(!(!)))" -- compilePat "+(+(+!+(+!)+(+(+(+!)+.+!+(+!))))+!+(+!+(+!)))" -- this is working now for generic nodes, but NOT for any of the others; so we see it for exactly the OTHER 9 nodes not among the 8 listed below! ch!... [later: fixed GNFDataP] -- compilePat "^(^(^!^(^!)^(^(^(^!)^.^!^(^!))))^!^(^!^(^!)))" -- AMAZINGLY WORKS!!! See IDs 0 1 3 7 12 13 14 -- compilePat "((!(!)(((^!).!(!))))!(!(!)))" -- works -- compilePat "((!(!)(((!).^!(!))))!(!(!)))" -- NO WORKY: FREEZES (same) #if 0 -- (Reproducible serious bug!) -- compilePat "((!(!)(((!).+!(!))))!(!(!)))" -- NO WORKY: FREEZES -- AFTER "trc msg.": --- P (( ()((() + ()))) ( ())) duty 918272 8720160 TA --- P ((!(!)(((!) +!(!))))!(!(!))) duty 1660356 2185992 TA --- P ((!(!)(((!) +!(!))))!(!(!))) duty 2348988 2244528 TA --- P ((!(!)(((!) +!(!))))!(!(!))) duty 3037620 2240528 TA --- P ((!(!)(((!) +!(!))))!(!(!))) duty 3693484 2207760 TA --- trc msg. --- ^C #endif -- compilePat "((!(!)(((!)+.!(!))))!(!(!)))" -- NO WORKY -- compilePat "((!(!)(((+!).!(+!))))!(!(!)))" -- works! "(TRACE: 3 Int) (TRACE: 7 Int)" repeating -- compilePat "((!(!)(((!).!(+!))))!(!(!)))" -- works! "(TRACE: 7 Int)" -- compilePat "((!(!)(((!)+^/.!(!))))!(!(!)))" -- no worky! -- compilePat "(+^/(!(!)(((!).!(!))))!(!(!)))" -- no worky -- compilePat "(+(!(!)(((!).!(!))))!(!(!)))" -- no worky -- compilePat "(+(!(!)(((+!).!(!))))!(!(!)))" -- compilePat "((!(!)(((+!).!(!))))!(!(!)))" -- works! "(TRACE: 3 Int)" compilePat "((!(!)(((!).!(!))))!(!(!)))" | otherwise = compilePat ".{.{..{.}.{.{.{.}#..{.}}}}..{..{.}}}" #else fixed_pat = compilePat ".{.{..{.}.{.{.{.}#..{.}}}}..{..{.}}}" #endif ------------------------------------------------------------------------------- -- From SAI.Data.Generics.Shape.SYB.Filter: condenseEq :: Eq a => (a -> a) -> a -> [a] -- beware this can diverge condenseEq f z = condenseEq' $ iterate f z where condenseEq' (x:y:t) | x == y = [x] | otherwise = x : condenseEq' (y:t) -- no other cases needed -- we know the argument is infinite ------------------------------------------------------------------------------- -- XXX Using a triple (esp. the 3rd component) is probably not -- the most performant choice... optimisation pending... -- XXX Would prefer 1 <-> 2, but to do that would require more -- code change to test... -- XXX This is no doubt located here to avoid a cyclical import! -- 1 (Int) Contains index of forcing site in the AST of the binding. -- 2 (String) Contains site binding variable name. -- 3 (Int) Caches the "unique" Int hash of name extended by index. type SiteID = (Int,String,Int) -------------------------------------------------------------------------------