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

Towards unlifted HashMaps #463

Open
sjakobi opened this issue May 12, 2022 · 9 comments
Open

Towards unlifted HashMaps #463

sjakobi opened this issue May 12, 2022 · 9 comments

Comments

@sjakobi
Copy link
Member

sjakobi commented May 12, 2022

This compiles with GHC-9.4-alpha1:

{-# language UnliftedDatatypes, MagicHash #-}

module M where

import Data.Kind (Type)
import GHC.Exts

data Leaf k v :: UnliftedType where
  L :: !k -> v -> Leaf k v

type HashMap' :: Type -> Type -> UnliftedType
data HashMap' k v
    = Empty
    | BitmapIndexed !Word !(SmallArray# (HashMap' k v))
    | Leaf !Word !(Leaf k v)
    | Full !(SmallArray# (HashMap' k v))
    | Collision !Word !(SmallArray# (Leaf k v))

data HashMap k v = HM !(HashMap' k v)

If the ergonomics work out, maybe we could start using a similar representation around GHC 8.8.

The main benefit should be better performance due to the removal of heap checks.

@treeowl
Copy link
Collaborator

treeowl commented May 12, 2022

The only likely performance trouble will be with lazy maps and traversals. Otherwise, I think it should reduce code size a bit, which is good, and maybe take stress off branch prediction.

@sjakobi
Copy link
Member Author

sjakobi commented May 12, 2022

The only likely performance trouble will be with lazy maps and traversals.

It would be great if you could give some concrete examples where you're expecting trouble!

@konsumlamm
Copy link
Contributor

data HashMap k v = HM !(HashMap' k v)

As far as I can tell, this would introduce an additional indirection for HashMap (a pointer to the HashMap + a pointer to the HashMap' vs just a pointer to the HashMap). I'm not sure if this would noticably impact performance, but it's something to keep in mind.

@treeowl
Copy link
Collaborator

treeowl commented May 12, 2022

@konsumlamm ohh yeah. That is going to be a problem. We really need to get the GHC folks to give us a way to have free wrappers. This might kill the whole idea.

@sjakobi
Copy link
Member Author

sjakobi commented May 23, 2022

I have opened https://gitlab.haskell.org/ghc/ghc/-/issues/21617 to request "free lifted wrappers".

@sjakobi
Copy link
Member Author

sjakobi commented May 24, 2022

If we end up accepting the pointer indirection, we could consider splitting out the Empty constructor:

type Tree :: Type -> Type -> UnliftedType
data Tree k v
    = BitmapIndexed !Word !(SmallArray# (HashMap' k v))
    | Leaf !Word !(Leaf k v)
    | Full !(SmallArray# (HashMap' k v))
    | Collision !Word !(SmallArray# (Leaf k v))

data HashMap k v = Empty | Tree !(Tree k v)

(Previous discussion: #161 (comment))

@AndreasPK
Copy link

I'm fairly confident that https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8445 will work out.

It will allow one to write data HashMap k v = HM !(HashMap' k v) with GHC eliminating the HM wrapper constructor at runtime.

But it won't happen before 9.6

@AndreasPK
Copy link

Out of interest has anyone done further work on this? Or run benchmarks in regards to the benefits of this approach?

@treeowl
Copy link
Collaborator

treeowl commented Oct 26, 2022

Not that I know of.

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

4 participants