diff options
author | Simon Marlow <marlowsd@gmail.com> | 2016-07-29 14:11:03 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2016-12-07 10:59:35 +0000 |
commit | 7036fde9df61b6eae9719c7f6c656778c756bec9 (patch) | |
tree | a9d8eeaaf0d611dc7f29f2d5734b5be8218f32fc /rts/PrimOps.cmm | |
parent | 4dd6b37fd540ad0243057f4aa29a93590d98de88 (diff) | |
download | haskell-7036fde9df61b6eae9719c7f6c656778c756bec9.tar.gz |
Overhaul of Compact Regions (#12455)
Summary:
This commit makes various improvements and addresses some issues with
Compact Regions (aka Compact Normal Forms).
This was the most important thing I wanted to fix. Compaction
previously prevented GC from running until it was complete, which
would be a problem in a multicore setting. Now, we compact using a
hand-written Cmm routine that can be interrupted at any point. When a
GC is triggered during a sharing-enabled compaction, the GC has to
traverse and update the hash table, so this hash table is now stored
in the StgCompactNFData object.
Previously, compaction consisted of a deepseq using the NFData class,
followed by a traversal in C code to copy the data. This is now done
in a single pass with hand-written Cmm (see rts/Compact.cmm). We no
longer use the NFData instances, instead the Cmm routine evaluates
components directly as it compacts.
The new compaction is about 50% faster than the old one with no
sharing, and a little faster on average with sharing (the cost of the
hash table dominates when we're doing sharing).
Static objects that don't (transitively) refer to any CAFs don't need
to be copied into the compact region. In particular this means we
often avoid copying Char values and small Int values, because these
are static closures in the runtime.
Each Compact# object can support a single compactAdd# operation at any
given time, so the Data.Compact library now enforces mutual exclusion
using an MVar stored in the Compact object.
We now get exceptions rather than killing everything with a barf()
when we encounter an object that cannot be compacted (a function, or a
mutable object). We now also detect pinned objects, which can't be
compacted either.
The Data.Compact API has been refactored and cleaned up. A new
compactSize operation returns the size (in bytes) of the compact
object.
Most of the documentation is in the Haddock docs for the compact
library, which I've expanded and improved here.
Various comments in the code have been improved, especially the main
Note [Compact Normal Forms] in rts/sm/CNF.c.
I've added a few tests, and expanded a few of the tests that were
there. We now also run the tests with GHCi, and in a new test way
that enables sanity checking (+RTS -DS).
There's a benchmark in libraries/compact/tests/compact_bench.hs for
measuring compaction speed and comparing sharing vs. no sharing.
The field totalDataW in StgCompactNFData was unnecessary.
Test Plan:
* new unit tests
* validate
* tested manually that we can compact Data.Aeson data
Reviewers: gcampax, bgamari, ezyang, austin, niteria, hvr, erikd
Subscribers: thomie, simonpj
Differential Revision: https://phabricator.haskell.org/D2751
GHC Trac Issues: #12455
Diffstat (limited to 'rts/PrimOps.cmm')
-rw-r--r-- | rts/PrimOps.cmm | 131 |
1 files changed, 0 insertions, 131 deletions
diff --git a/rts/PrimOps.cmm b/rts/PrimOps.cmm index 4cc0dccbbc..d6cdb3d535 100644 --- a/rts/PrimOps.cmm +++ b/rts/PrimOps.cmm @@ -1925,137 +1925,6 @@ stg_deRefStablePtrzh ( P_ sp ) } /* ----------------------------------------------------------------------------- - CompactNFData primitives - - See Note [Compact Normal Forms] - ------------------------------------------------------------------------- */ - -stg_compactNewzh ( W_ size ) -{ - P_ str; - - again: MAYBE_GC(again); - - ("ptr" str) = ccall compactNew(MyCapability() "ptr", size); - return (str); -} - -stg_compactAppendzh ( P_ str, P_ val , W_ share) -{ - P_ root; - - again: MAYBE_GC(again); - - ("ptr" root) = ccall compactAppend(MyCapability() "ptr", str "ptr", val "ptr", share); - return (root); -} - -stg_compactResizzezh ( P_ str, W_ new_size ) -{ - again: MAYBE_GC(again); - - ccall compactResize(MyCapability() "ptr", str "ptr", new_size); - return (); -} - -stg_compactContainszh ( P_ str, P_ val ) -{ - W_ rval; - - (rval) = ccall compactContains(str "ptr", val "ptr"); - return (rval); -} - -stg_compactContainsAnyzh ( P_ val ) -{ - W_ rval; - - (rval) = ccall compactContains(0 "ptr", val "ptr"); - return (rval); -} - -stg_compactGetFirstBlockzh ( P_ str ) -{ - /* W_, not P_, because it is not a gc pointer */ - W_ block; - W_ bd; - W_ size; - - block = str - SIZEOF_StgCompactNFDataBlock::W_; - ASSERT (StgCompactNFDataBlock_owner(block) == str); - - bd = Bdescr(str); - size = bdescr_free(bd) - bdescr_start(bd); - ASSERT (size <= TO_W_(bdescr_blocks(bd)) * BLOCK_SIZE); - - return (block, size); -} - -stg_compactGetNextBlockzh ( P_ str, W_ block ) -{ - /* str is a pointer to the closure holding the Compact# - it is there primarily to keep everything reachable from - the GC: by having it on the stack of type P_, the GC will - see all the blocks as live (any pointer in the Compact# - keeps it alive), and will not collect the block - We don't run a GC inside this primop, but it could - happen right after, or we could be preempted. - - str is also useful for debugging, as it can be casted - to a useful C struct from the gdb command line and all - blocks can be inspected - */ - W_ bd; - W_ next_block; - W_ size; - - next_block = StgCompactNFDataBlock_next(block); - - if (next_block == 0::W_) { - return (0::W_, 0::W_); - } - - ASSERT (StgCompactNFDataBlock_owner(next_block) == str || - StgCompactNFDataBlock_owner(next_block) == NULL); - - bd = Bdescr(next_block); - size = bdescr_free(bd) - bdescr_start(bd); - ASSERT (size <= TO_W_(bdescr_blocks(bd)) * BLOCK_SIZE); - - return (next_block, size); -} - -stg_compactAllocateBlockzh ( W_ size, W_ previous ) -{ - W_ actual_block; - - again: MAYBE_GC(again); - - ("ptr" actual_block) = ccall compactAllocateBlock(MyCapability(), - size, - previous "ptr"); - - return (actual_block); -} - -stg_compactFixupPointerszh ( W_ first_block, W_ root ) -{ - W_ str; - P_ gcstr; - W_ ok; - - str = first_block + SIZEOF_StgCompactNFDataBlock::W_; - (ok) = ccall compactFixupPointers (str "ptr", root "ptr"); - - // Now we can let the GC know about str, because it was linked - // into the generation list and the book-keeping pointers are - // guaranteed to be valid - // (this is true even if the fixup phase failed) - gcstr = str; - return (gcstr, ok); -} - -/* ----------------------------------------------------------------------------- Bytecode object primitives ------------------------------------------------------------------------- */ |