Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small optimization idea for alter[F] #457

Open
sjakobi opened this issue May 7, 2022 · 0 comments
Open

Small optimization idea for alter[F] #457

sjakobi opened this issue May 7, 2022 · 0 comments

Comments

@sjakobi
Copy link
Member

sjakobi commented May 7, 2022

alter and alterF rely on lookupRecordCollision to gather information about the location of a key in the map. This info is stored in a LookupRes:

-- The result of a lookup, keeping track of if a hash collision occured.
-- If a collision did not occur then it will have the Int value (-1).
data LookupRes a = Absent | Present a !Int

I think we could slightly reduce the work that a subsequent insertKeyExists or deleteKeyExists has to perform by enhancing this location information with the sequence of array indices that lead to the relevant Leaf or Collision node:

type IndexPath = Word

data LookupRes a = Absent | Present a !IndexPath !Int

This IndexPath is basically a Hash with the difference that even for BitmapIndexed nodes, the array index can be computed with the simple index function instead of the more involved sparseIndex. We won't even need index and the Shift it requires, though, if we simply right-shift the IndexPath at each step.

I don't expect a large speedup from this, but maybe something like 2, 3%?!

-- | This is the default version of alterF that we use in most non-trivial
-- cases. It's called "eager" because it looks up the given key in the map
-- eagerly, whether or not the given function requires that information.
alterFEager :: (Functor f, Eq k, Hashable k)
=> (Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
alterFEager f !k m = (<$> f mv) $ \case
------------------------------
-- Delete the key from the map.
Nothing -> case lookupRes of
-- Key did not exist in the map to begin with, no-op
Absent -> m
-- Key did exist
Present _ collPos -> deleteKeyExists collPos h k m
------------------------------
-- Update value
Just v' -> case lookupRes of
-- Key did not exist before, insert v' under a new key
Absent -> insertNewKey h k v' m
-- Key existed before
Present v collPos ->
if v `ptrEq` v'
-- If the value is identical, no-op
then m
-- If the value changed, update the value.
else insertKeyExists collPos h k v' m
where !h = hash k
!lookupRes = lookupRecordCollision h k m
!mv = case lookupRes of
Absent -> Nothing
Present v _ -> Just v
{-# INLINABLE alterFEager #-}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant