Giter VIP home page Giter VIP logo

fastdictionarytest's Introduction

Fast Dictionary in F#

This is a repo of my experimentation with writing a Static Dictionary (i.e. ReadOnlyDictionary) with lookup performance that is as fast as possible, with reasonable memory usage. The current fastest version is a Byte-List Separate Chaining with Robin Hood hashing to attempt to keep Keys that hash to the same bucket are as close as possible.

Most of the ideas used here come from a CppCon talk given in 2018 by Malte Skarupke titled C++Now 2018: You Can Do Better than std::unordered_map: New Improvements to Hash Table Performance. This repo is my attempt to implement several of the ideas in F# to see what performance could be achieved.

Results

Here are the results for the current best performing Dictionary version.

// * Summary *

BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22621.1702)
13th Gen Intel Core i9-13900K, 1 CPU, 32 logical and 24 physical cores
.NET SDK=8.0.100-preview.3.23178.7
  [Host]     : .NET 8.0.0 (8.0.23.17408), X64 RyuJIT AVX2 DEBUG
  DefaultJob : .NET 8.0.0 (8.0.23.17408), X64 RyuJIT AVX2
Method KeyType KeyCount Mean Error StdDev Code Size BranchInstructions/Op CacheMisses/Op BranchMispredictions/Op Allocated
Dictionary Int 10 55.26 us 0.584 us 0.546 us 1,566 B 113,966 86 2,983 -
'Frozen Dictionary' Int 10 111.99 us 0.294 us 0.260 us 1,247 B 338,890 25 8,779 -
'Static Dict' Int 10 21.47 us 0.147 us 0.130 us 1,129 B 75,423 20 974 -
Dictionary Int 100 66.01 us 0.505 us 0.473 us 1,566 B 115,166 39 3,580 -
'Frozen Dictionary' Int 100 23.14 us 0.241 us 0.201 us 1,247 B 111,234 4 344 -
'Static Dict' Int 100 16.45 us 0.314 us 0.374 us 1,129 B 69,543 3 470 -
Dictionary Int 1_000 66.22 us 0.205 us 0.182 us 1,566 B 114,626 38 3,454 -
'Frozen Dictionary' Int 1_000 33.35 us 0.368 us 0.326 us 1,247 B 114,353 33 880 -
'Static Dict' Int 1_000 21.54 us 0.132 us 0.117 us 1,129 B 72,319 10 823 -
Dictionary Int 10_000 109.51 us 1.246 us 1.104 us 1,566 B 116,149 2,081 3,761 -
'Frozen Dictionary' Int 10_000 67.62 us 1.309 us 1.161 us 1,247 B 114,929 2,527 778 -
'Static Dict' Int 10_000 44.55 us 0.871 us 0.855 us 1,129 B 74,966 945 1,476 -
Dictionary String 10 170.37 us 1.455 us 1.215 us 1,551 B 444,723 27 8,670 -
'Frozen Dictionary' String 10 130.26 us 1.043 us 0.925 us 1,247 B 417,279 21 7,581 -
'Static Dict' String 10 90.23 us 0.687 us 0.536 us 1,129 B 325,809 15 3,841 -
Dictionary String 100 185.48 us 2.765 us 2.587 us 1,551 B 446,424 77 9,919 -
'Frozen Dictionary' String 100 98.59 us 1.119 us 1.047 us 1,247 B 376,587 59 3,083 -
'Static Dict' String 100 92.69 us 1.137 us 1.063 us 1,129 B 320,451 29 3,539 -
Dictionary String 1_000 247.58 us 3.099 us 2.898 us 1,551 B 447,108 1,523 9,563 -
'Frozen Dictionary' String 1_000 153.22 us 1.506 us 1.335 us 1,247 B 357,688 1,861 2,888 -
'Static Dict' String 1_000 137.71 us 1.561 us 1.460 us 1,129 B 325,975 646 3,944 -
Dictionary String 10_000 365.60 us 6.803 us 6.031 us 1,551 B 450,305 6,617 10,426 -
'Frozen Dictionary' String 10_000 245.21 us 2.819 us 2.354 us 1,247 B 383,241 7,225 4,193 -
'Static Dict' String 10_000 197.43 us 1.163 us 1.031 us 1,129 B 332,395 6,683 5,179 -

Running Benchmarks

To run the benchmakrs for yourself, open a Terminal instance with Admin privledges so that you can get the hardware counter results. Navigate your terminal to the FastDictionaryTest.Benchmarks direction. The following command you will want to use is:

dotnet run -c Release --task benchmark --suite <Suite of Interest>

The possible values for <Suite of Interest> are:

  • Baseline
  • Naive
  • ZeroAllocation
  • FibonacciHashing
  • CacheEquality
  • FastTypeBranch
  • EmbeddedHead
  • LinearProbing
  • RobinHood
  • RobinhoodEviction
  • ByteList
  • ByteListRobinHood
  • ByteListRobinHoodVec128
  • ByteListRobinHoodInline
  • CStyle
  • SOA

fastdictionarytest's People

Contributors

cannero avatar dadhi avatar matthewcrews avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

fastdictionarytest's Issues

Robin Hood - bug when inserting into tombstones

Hey there!

I'm trying to implement hash dicts in Elm based on your repo and videos (and the awesome C++Now 2018 talk by Malte).

I've just got round to Robin Hood, and I have a property based test suite set up checking that outwardly my dict behaves exactly the same as the "known to be correct" stdlib Dict.

The test suite has found an issue with your approach to inserting an item into a Tombstone. Consider this scenario:

  1. Start with empty table (4 buckets)
  2. Insert "A" (let's say it hashes to bucket index 3) -> will end up in bucket 3 with offset 0
  3. Insert "B" (hashes to bucket index 3) -> will end up in bucket 0 with offset 1
  4. Remove "A" -> now bucket 3 is a Tombstone
  5. Insert "B" -> it sees a tombstone in bucket 3, and gets inserted there
  6. Convert to list -> you get two Bs instead of just one!

That's because after step 5 the array looks like:

idx offset key
0 1 "B"
1 -1 Empty
2 -1 Empty
3 0 "B"

instead of

idx offset key
0 1 "B"
1 -1 Empty
2 -1 Empty
3 0 Tombstone

On the other hand, if in step 5 you continued to the next index after seeing a tombstone, you'd find the B in bucket 0 and would replace its value / end there.

AccessViolationException in stringComparer

Hi, Matthew. First, thanks for doing this. I've sped up a lot of code with your work.

I don't have a whole lot of information to share about this issue, but here's what I know. In a recent project, I've really ramped up usage of dictionaries -- based on ByteListRobinHoodVec128. I have about 7 of them, some with about 50k elements, some with about half a million.

On my first "big" test run, I received a System.AccessViolationException in the implementation of GetHashCode for the stringComparer. (function startes line 68 in ByteListRobinHoodVec128.fs)

I don't have a lot more information than that. I can tell you that the dictionary had about 50k elements, that the keys were all string representations of int64 values, and the values were structs of about 1k each. I'm not sure any of that is relevant :)

I've stared at the GetHashCode implementation for a long while and I'll be darned if I can see a problem. If I replace it with one of the StringComparer implementations in System the problem disappears. The exception does say that "this is often and indication that other memory is corrupt", but I'm not sure where that would be. All the rest of the code is either standard .NET 6.0 stuff or MongoDB queries.

If you have any insight, I'd love to hear it.

Infinite loop when dictionary is empty

I started using the implementation in ByteListRobinHoodInline.fs per your suggestion in the previous issue I raised.

When the dictionary is empty, Bucket.IsLast never seems to evaluate to true. As a result, lookups in an empty dictionary cause an infinite loop.

I added a check for count = 0 where it checks IsLast in the main body of the Item function and that seems to have sorted it. But obviously that adds another CMP instruction and that's not optimal, is it? :) I was wondering if you knew of another way to do it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.