packman 0.4.0 → 0.5.0
raw patch · 9 files changed
+244/−8 lines, 9 filesdep ~base
Dependency ranges changed: base
Files
- GHC/Packing/Type.hs +1/−1
- cbits/BeginPrivate.h +0/−0
- cbits/EndPrivate.h +0/−0
- cbits/GHCFunctions.h +7/−0
- cbits/GhcHeapAlloc.h +227/−0
- cbits/Pack.c +1/−1
- cbits/Types.h +1/−1
- pack.old binary
- packman.cabal +7/−5
GHC/Packing/Type.hs view
@@ -49,7 +49,7 @@ -- for dynamic type checks when parsing import Data.Typeable (Typeable(..), typeOf) #if MIN_VERSION_base(4,8,0)-import Data.Typeable.Internal (TypeRep(..), typeRepFingerprint)+import Data.Typeable (typeRepFingerprint) #else import Data.Typeable.Internal (TypeRep(..)) #endif
+ cbits/BeginPrivate.h view
+ cbits/EndPrivate.h view
cbits/GHCFunctions.h view
@@ -31,3 +31,10 @@ #ifdef DEBUG extern void checkClosure(StgClosure*); #endif++#if __GLASGOW_HASKELL__ >= 801+// we have to bring the internal HEAP_ALLOCED macro in scope+// to be able to deal with CONSTR_NOCAF (to detect static ones)+// This header file is taken from GHC 8.2 source code directly+# include "GhcHeapAlloc.h"+#endif
+ cbits/GhcHeapAlloc.h view
@@ -0,0 +1,227 @@+/* -----------------------------------------------------------------------------+ *+ * (c) The University of Glasgow 2006-2008+ *+ * The HEAP_ALLOCED() test.+ *+ * ---------------------------------------------------------------------------*/++#ifndef SM_HEAP_ALLOC_H+#define SM_HEAP_ALLOC_H++#include "BeginPrivate.h"++/* -----------------------------------------------------------------------------+ The HEAP_ALLOCED() test.++ HEAP_ALLOCED is called FOR EVERY SINGLE CLOSURE during GC.+ It needs to be FAST.++ See wiki commentary at+ http://ghc.haskell.org/trac/ghc/wiki/Commentary/HeapAlloced++ Implementation of HEAP_ALLOCED+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++ Since heap is allocated in chunks of megablocks (MBLOCK_SIZE), we+ can just use a table to record which megablocks in the address+ space belong to the heap. On a 32-bit machine, with 1Mb+ megablocks, using 8 bits for each entry in the table, the table+ requires 4k. Lookups during GC will be fast, because the table+ will be quickly cached (indeed, performance measurements showed no+ measurable difference between doing the table lookup and using a+ constant comparison).++ On 64-bit machines, we have two possibilities. One is to request+ a single chunk of address space that we deem "large enough"+ (currently 1TB or the ulimit size, whichever is smaller, although this could+ easily be extended to, say 16TB or more). Memory from that chunk is GC+ memory, everything else is not. This case is tricky in that it requires+ support from the OS to allocate address space without allocating memory (in+ practice, all modern OSes do this). It's also tricky in that it is the only+ case where a successful HEAP_ALLOCED(p) check can trigger a segfault when+ accessing p (and for debugging purposes, it will).++ Alternatively, the older implementation caches one 12-bit block map+ that describes 4096 megablocks or 4GB of memory. If HEAP_ALLOCED is+ called for an address that is not in the cache, it calls+ slowIsHeapAlloced (see MBlock.c) which will find the block map for+ the 4GB block in question.+ -------------------------------------------------------------------------- */++#ifdef USE_LARGE_ADDRESS_SPACE++struct mblock_address_range {+ W_ begin, end;+ W_ padding[6]; // ensure nothing else inhabits this cache line+} ATTRIBUTE_ALIGNED(64);+extern struct mblock_address_range mblock_address_space;++# define HEAP_ALLOCED(p) ((W_)(p) >= mblock_address_space.begin && \+ (W_)(p) < (mblock_address_space.end))+# define HEAP_ALLOCED_GC(p) HEAP_ALLOCED(p)++#elif SIZEOF_VOID_P == 4+extern StgWord8 mblock_map[];++/* On a 32-bit machine a 4KB table is always sufficient */+# define MBLOCK_MAP_SIZE 4096+# define MBLOCK_MAP_ENTRY(p) ((StgWord)(p) >> MBLOCK_SHIFT)+# define HEAP_ALLOCED(p) mblock_map[MBLOCK_MAP_ENTRY(p)]+# define HEAP_ALLOCED_GC(p) HEAP_ALLOCED(p)++/* -----------------------------------------------------------------------------+ HEAP_ALLOCED for 64-bit machines (without LARGE_ADDRESS_SPACE).++ Here are some cache layout options:++ [1]+ 16KB cache of 16-bit entries, 1MB lines (capacity 8GB)+ mblock size = 20 bits+ entries = 8192 13 bits+ line size = 0 bits (1 bit of value)+ tag size = 15 bits+ = 48 bits++ [2]+ 32KB cache of 16-bit entries, 4MB lines (capacity 32GB)+ mblock size = 20 bits+ entries = 16384 14 bits+ line size = 2 bits (4 bits of value)+ tag size = 12 bits+ = 48 bits++ [3]+ 16KB cache of 16-bit entries, 2MB lines (capacity 16GB)+ mblock size = 20 bits+ entries = 8192 13 bits+ line size = 1 bits (2 bits of value)+ tag size = 14 bits+ = 48 bits++ [4]+ 4KB cache of 32-bit entries, 16MB lines (capacity 16GB)+ mblock size = 20 bits+ entries = 1024 10 bits+ line size = 4 bits (16 bits of value)+ tag size = 14 bits+ = 48 bits++ [5]+ 4KB cache of 64-bit entries, 32MB lines (capacity 16GB)+ mblock size = 20 bits+ entries = 512 9 bits+ line size = 5 bits (32 bits of value)+ tag size = 14 bits+ = 48 bits++ We actually use none of the above. After much experimentation it was+ found that optimising the lookup is the most important factor,+ followed by reducing the number of misses. To that end, we use a+ variant of [1] in which each cache entry is ((mblock << 1) + value)+ where value is 0 for non-heap and 1 for heap. The cache entries can+ be 32 bits, since the mblock number is 48-20 = 28 bits, and we need+ 1 bit for the value. The cache can be as big as we like, but+ currently we use 8k entries, giving us 8GB capacity.++ ---------------------------------------------------------------------------- */++#elif SIZEOF_VOID_P == 8++#define MBC_LINE_BITS 0+#define MBC_TAG_BITS 15++#if x86_64_HOST_ARCH+// 32bits are enough for 'entry' as modern amd64 boxes have+// only 48bit sized virtual addres.+typedef StgWord32 MbcCacheLine;+#else+// 32bits is not enough here as some arches (like ia64) use+// upper address bits to distinct memory areas.+typedef StgWord64 MbcCacheLine;+#endif++typedef StgWord8 MBlockMapLine;++#define MBLOCK_MAP_LINE(p) (((StgWord)p & 0xffffffff) >> (MBLOCK_SHIFT + MBC_LINE_BITS))++#define MBC_LINE_SIZE (1<<MBC_LINE_BITS)+#define MBC_SHIFT (48 - MBLOCK_SHIFT - MBC_LINE_BITS - MBC_TAG_BITS)+#define MBC_ENTRIES (1<<MBC_SHIFT)++extern MbcCacheLine mblock_cache[];++#define MBC_LINE(p) ((StgWord)p >> (MBLOCK_SHIFT + MBC_LINE_BITS))++#define MBLOCK_MAP_ENTRIES (1 << (32 - MBLOCK_SHIFT - MBC_LINE_BITS))++typedef struct {+ StgWord32 addrHigh32;+ MBlockMapLine lines[MBLOCK_MAP_ENTRIES];+} MBlockMap;++extern W_ mpc_misses;++StgBool HEAP_ALLOCED_miss(StgWord mblock, const void *p);++INLINE_HEADER+StgBool HEAP_ALLOCED(const void *p)+{+ StgWord mblock;+ uint32_t entry_no;+ MbcCacheLine entry, value;++ mblock = (StgWord)p >> MBLOCK_SHIFT;+ entry_no = mblock & (MBC_ENTRIES-1);+ entry = mblock_cache[entry_no];+ value = entry ^ (mblock << 1);+ // this formulation coaxes gcc into prioritising the value==1+ // case, which we expect to be the most common.+ // __builtin_expect() didn't have any useful effect (gcc-4.3.0).+ if (value == 1) {+ return 1;+ } else if (value == 0) {+ return 0;+ } else {+ // putting the rest out of line turned out to be a slight+ // performance improvement:+ return HEAP_ALLOCED_miss(mblock,p);+ }+}++// In the parallel GC, the cache itself is safe to *read*, and can be+// updated atomically, but we need to place a lock around operations+// that touch the MBlock map.+INLINE_HEADER+StgBool HEAP_ALLOCED_GC(void *p)+{+ StgWord mblock;+ uint32_t entry_no;+ MbcCacheLine entry, value;+ StgBool b;++ mblock = (StgWord)p >> MBLOCK_SHIFT;+ entry_no = mblock & (MBC_ENTRIES-1);+ entry = mblock_cache[entry_no];+ value = entry ^ (mblock << 1);+ if (value == 1) {+ return 1;+ } else if (value == 0) {+ return 0;+ } else {+ // putting the rest out of line turned out to be a slight+ // performance improvement:+ ACQUIRE_SPIN_LOCK(&gc_alloc_block_sync);+ b = HEAP_ALLOCED_miss(mblock,p);+ RELEASE_SPIN_LOCK(&gc_alloc_block_sync);+ return b;+ }+}++#else+# error HEAP_ALLOCED not defined+#endif++#include "EndPrivate.h"++#endif /* SM_HEAP_ALLOC_H */
cbits/Pack.c view
@@ -2028,7 +2028,7 @@ default: // invalid markers (not OFFSET, PLC, CLOSURE) are caught here errorBelch("unpackClosure: Found invalid marker %" FMT_Word ".\n",- (long) **bufptrP);+ **bufptrP); return (StgClosure *) NULL; }
cbits/Types.h view
@@ -10,7 +10,7 @@ // for data messages only, StgInt /* nat */ size; StgInt /* nat */ unpacked_size;- StgWord buffer[FLEXIBLE_ARRAY];+ StgWord buffer[]; } pmPackBuffer;
− pack.old
binary file changed (168 → absent bytes)
packman.cabal view
@@ -1,5 +1,5 @@ name: packman-version: 0.4.0+version: 0.5.0 synopsis: Serialization library for GHC description:@@ -49,13 +49,15 @@ maintainer: jost.berthold@gmail.com build-type: Simple cabal-version: >= 1.18-tested-with: GHC==7.8.2, GHC==7.8.3, GHC==7.10.2+tested-with: GHC==7.8.2, GHC==7.8.3, GHC==7.10.2, GHC==8.0.2, GHC==8.2.1, GHC==8.2.2 extra-source-files: cbits/Wrapper.cmm cbits/Pack.c cbits/Errors.h cbits/Types.h cbits/GHCFunctions.h- pack.old+ cbits/GhcHeapAlloc.h+ cbits/BeginPrivate.h+ cbits/EndPrivate.h flag debug manual: True@@ -68,7 +70,7 @@ GHC.Packing.PackException GHC.Packing.Type GHC.Packing.Core- build-depends: base >= 4.7 && < 4.10,+ build-depends: base >= 4.7 && < 5, ghc-prim >= 0.3, array >= 0.5, binary >= 0.7,@@ -177,4 +179,4 @@ source-repository head type: git- location: git://github.com/jberthold/packman.git+ location: https://github.com/jberthold/packman.git