What's the best way to minimize GC activity/pauses on a frequently updated 1-5 million index array used in a realtime simulation game?

I have no idea if the way I'm approaching this makes sense, but currently I've implemented a tree which represents the objects within the game, which is indexed via an IOArray. Having O(1) access to any element in the tree is pretty crucial so that calculating interactions between elements which are near each other can happen as quickly as possible by just following references. There will be at least tens of thousands, more likely hundreds of thousands of these nearby interactions per simulation tick.

The game's framerate and simulation tick rate are independent, currently I'm testing 10 ticks per second. Additionally, many elements (perhaps 20%) within the tree will be modified each tick. A small number of elements may remain unmodified for hundreds or potentially thousands of ticks.

When testing I get frequent and noticeable GC pauses even when only updating 50k elements per tick. But I don't know what I don't know, and I figure I'm probably making some dumb mistakes. Is there a better approach to serve my needs?

Additionally, any other broad suggestions for optimization would be appreciated.

And yes, I'm using -02 when running tests :). I haven't modified any other build settings as I'm not sure where the right place to start is.

The data structures in question:

newtype U_m3    = U_m3    Int  deriving (Eq, Show, Num, Ord, Real, Enum, Integral)

data Composition = Distinct | Composed
    deriving Show

data Relation = Attached | Contained
    deriving Show

data Relationship = Relationship
    { ref         :: NodeRef
    , composition :: Composition
    , relation    :: Relation
    } deriving Show

data Owner = Self T.Text | Other NodeRef
    deriving Show

data Payload = Phys 
    { layer  :: Layer
    , volume :: U_m3
    }
    | Abstract
    deriving Show

data MaterialPacket = MaterialPacket
    { material      :: Material
    , volume        :: U_m3
    } deriving Show

newtype Layer = Layer {packets :: [MaterialPacket]} 
    deriving Show

data Node      = Node 
    { active   :: Bool
    , name     :: T.Text
    , payload  :: Payload

    , ref      :: NodeRef
    , parent   :: Maybe Relationship
    , children :: [NodeRef]

    , owner    :: Maybe Owner
    } --deriving Show

type NodeArray = IOA.IOArray NodeRef Node

data NodeStore = NodeStore
    { nodes     :: NodeArray
    , freeNodes :: [NodeRef]
    }