tweag / tweag/linear-base

Performance issues with linear hashmap

Open
#280 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Haskell
Stars
359
Forks
45
PR merge metrics
No merged PRs in 30d

Description

The hashmap in Data.HashMap.Mutable.Linear is orders of magnitude slower than Data.HashMap.Lazy for large inputs.

To Reproduce
I implemented the same code with both Data.HashMap.Mutable.Linear and Data.HashMap.Lazy in this gist. On my computer, I get:

$ hyperfine ./lin-mut ./imm
Benchmark #1: ./lin-mut
  Time (mean ± σ):      87.6 ms ±   2.0 ms    [User: 85.6 ms, System: 2.0 ms]
  Range (min … max):    85.1 ms …  94.1 ms    33 runs
 
Benchmark #2: ./imm
  Time (mean ± σ):       9.2 ms ±   0.5 ms    [User: 6.7 ms, System: 2.6 ms]
  Range (min … max):     8.5 ms …  13.2 ms    251 runs

Here the mutable linear version is 10x slower. Changing the stopping point from 30_000 to 300_000 makes it 100x slower:

$ hyperfine ./lin-mut ./imm
Benchmark #1: ./lin-mut
  Time (mean ± σ):     10.156 s ±  0.162 s    [User: 10.118 s, System: 0.028 s]
  Range (min … max):   10.026 s … 10.509 s    10 runs
 
Benchmark #2: ./imm
  Time (mean ± σ):      90.0 ms ±   1.7 ms    [User: 83.5 ms, System: 6.3 ms]
  Range (min … max):    88.0 ms …  94.2 ms    31 runs

All programs were compiled with ghc --make -O2 -fllvm <input>.hs -o <output>.

Expected behavior
I would expect the (nominally O(1)) mutable linear hashmap to have better asymptotics than the O(log n) persistent immutable hashmap.

Environment

  • OS name + version: Linux 64-bit. I'm not using Stack or Nix.
  • Version of the code: GHC HEAD (92377c27e1a48d0d3776f65c7074dfeb122b46db), linear-base master (69d2d3331673fc0b637767127ca8f5cb1bdb70f7). In case it's relevant, I had to make a few minor changes to the primitive library to get it to compile with GHC HEAD.

Additional context
I was unable to figure out how to use unordered-containers with the custom-built GHC, so the faster times are built with GHC 8.10.2. Changing to Data.Map.Lazy produces times only slightly slower (20%) than Data.HashMap.Lazy, and is consistent across compiler versions, so I'm not too worried about it.

This feels like a laziness issue -- I haven't looked into the internals of Data.HashMap.Mutable.Linear, so I'm not really sure where it might be coming from.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked reproduction and the Data.HashMap.Mutable.Linear module, then compare its benchmark with Data.HashMap.Lazy using the reported GHC optimization command. Done means identifying and addressing the cause of the mutable linear hashmap's unexpectedly poor scaling, with benchmarks showing behavior consistent with its expected asymptotics.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell
Domain
performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.