[GHC] #650: Improve interaction between mutable arrays and GC
Simon Marlow
simonmarhaskell at gmail.com
Tue Jan 24 11:05:38 EST 2006
Jan-Willem Maessen wrote:
>
> On Jan 11, 2006, at 6:21 AM, GHC wrote:
>
>> #650: Improve interaction between mutable arrays and GC
>> -------------------------------
>> +--------------------------------------------
>> Reporter: simonmar | Owner:
>> Type: task | Status: new
>> Priority: normal | Milestone: 6.6
>> Component: Runtime System | Version: 6.4.1
>> Severity: normal | Keywords:
>> Os: Unknown | Difficulty: Moderate (1 day)
>> Architecture: Unknown |
>> -------------------------------
>> +--------------------------------------------
>> This is the result of a discussion between myself and Simon PJ a few
>> weeks
>> ago, inspired by recent discoveries of poor performance with mutable
>> arrays (eg. see Data.Hash discussion on glasgow-haskell-users around
>> October 2005).
>>
>> Note that all this applies to mutable arrays of pointers, i.e.
>> `IOArray`
>> and `STArray`, ''not'' to unboxed arrays, i.e. `IOUArray` and
>> `STUArray`.
>> ...
>> === The Problem ===
>>
>> The problem is that mutable arrays are always completely traversed on
>> every GC. To get around this, we can keep an array in a frozen
>> state and
>> thaw it just before writing, then freeze it again afterward. This is a
>> bit inconvenient, not to mention unsafe with multiple threads unless
>> extra
>> locking is used.
>
>
> Yes. I'd love it if "freeze" and "thaw" just affected types plus
> (perhaps) the ability for mutations to occur in the first place (so
> that I don't freeze in one thread and mutate in another).
>
> Given that virtually every object access in GHC involves a hidden read
> barrier (entering a thunk is effectively a read barrier which checks
> computedness), I'm not too worried about the costs of a write barrier
> for mutation in the IO monad. I'd welcome some up-front cost to avoid
> the GC problems.
I probably should have mentioned this to you, but we've implemented a
partial solution to this already:
http://www.haskell.org//pipermail/cvs-ghc/2006-January/028009.html
http://www.haskell.org//pipermail/cvs-ghc/2006-January/028010.html
> You should think seriously about keeping a write list for arrays as an
> alternative to cards or a full scan, as Bulat suggested. There are
> card/write list hybrids.
>
> If you put a card mark in the block table, there's no particular need
> to modify the array representation beyond adding a "dirty" bit. The
> write barrier would look something like this:
> * Set array's dirty bit
> * Set card mark on appropriate block
> * Store pointer into array
> Then during GC we scan the dirty blocks of dirty arrays.
I quite agree, and this is exactly what I'd planned to do (eventually).
Having a dirty bit per array in addition to the per-block bit has two
benefits: it means you don't have to look at every block descriptor in a
clean array, and secondly when there are multiple small arrays in a
single block we won't end up scanning all of them if one of them is
written to.
> Small arrays will fit in a block and will be scanned completely.
> Larger arrays will span more than one block and benefit from the card
> marks. But there's no need to actually distinguish these cases if
> you're using cards.
>
> Note also that we can keep multiple card flags in the block descriptor,
> so there's actually opportunity for tuning here. It's trading off
> up-front bit-twiddling for lower GC overhead, though.
>
> Do bear in mind that it's big arrays which really seem to be causing
> the trouble here. So I think some solution which does work
> proportional to the amount of mutation is in order.
>
> For IORefs the game is utterly different; I'm not sure what to do about
> them. You'd like to avoid traversing a list of all the IORefs at each
> collection. Very tricky. Here it might be worth scanning just the
> dirty blocks.
The IORef write barrier we implemented seems to work pretty well: when
an IORef is written to, it is put on the mutable list iff it was
previously clean. The write barrier didn't decrease performance
measurably for a program that did a lot of IORef manipulations, but the
GC load was much reduced (see nofib/gc_bench).
Cheers,
Simon
More information about the Glasgow-haskell-bugs
mailing list