Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: cespare/xxhash
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.1.0
Choose a base ref
...
head repository: cespare/xxhash
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.0.0
Choose a head ref
  • 16 commits
  • 15 files changed
  • 3 contributors

Commits on Oct 17, 2018

  1. Updating README.md with reference to freecache

    * Freecache recently changed to use this hashing strategy
    deckarep authored and cespare committed Oct 17, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    096ff4a View commit details

Commits on Nov 29, 2018

  1. Add Travis CI

    Fixes #7.
    cespare committed Nov 29, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f177858 View commit details
  2. Add build status badge

    cespare committed Nov 29, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9573aff View commit details

Commits on Dec 31, 2018

  1. Change writeBlocks signature to avoid making xxh escape

    Thanks to @ongardie for the suggestion.
    cespare committed Dec 31, 2018
    Copy the full SHA
    6e60139 View commit details

Commits on Jan 1, 2019

  1. Add more benchmarks using New(), etc

    The current 100B hash results on my MacBook Pro with Go v1.11.2 are:
    
    name                                     time/op
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4       16.3ns ± 2%
    Hashes/xxhash,direct,string,n=100_B-4      18.7ns ± 2%
    Hashes/xxhash,digest,bytes,n=100_B-4       51.8ns ± 1%
    Hashes/xxhash,digest,string,n=100_B-4       100ns ±26%
    Hashes/OneOfOne,direct,bytes,n=100_B-4     22.5ns ± 1%
    Hashes/OneOfOne,direct,string,n=100_B-4    24.3ns ± 1%
    Hashes/OneOfOne,digest,bytes,n=100_B-4     38.2ns ± 1%
    Hashes/OneOfOne,digest,string,n=100_B-4    40.1ns ± 2%
    Hashes/murmur3,direct,bytes,n=100_B-4      33.9ns ± 1%
    Hashes/murmur3,direct,string,n=100_B-4     72.9ns ± 1%
    Hashes/murmur3,digest,bytes,n=100_B-4      85.1ns ± 1%
    Hashes/murmur3,digest,string,n=100_B-4      125ns ± 1%
    Hashes/CRC-32,direct,bytes,n=100_B-4       36.5ns ± 2%
    Hashes/CRC-32,direct,string,n=100_B-4      81.6ns ± 8%
    Hashes/CRC-32,digest,bytes,n=100_B-4       72.8ns ± 1%
    Hashes/CRC-32,digest,string,n=100_B-4       110ns ± 2%
    Hashes/fnv1a,digest,bytes,n=100_B-4         122ns ± 1%
    Hashes/fnv1a,digest,string,n=100_B-4        158ns ± 1%
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4       17.6ns ± 2%
    Hashes/xxhash,direct,string,n=100_B-4      19.9ns ± 1%
    Hashes/xxhash,digest,bytes,n=100_B-4       58.9ns ± 1%
    Hashes/xxhash,digest,string,n=100_B-4      96.7ns ± 1%
    
    name                                     speed
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4     6.14GB/s ± 2%
    Hashes/xxhash,direct,string,n=100_B-4    5.35GB/s ± 2%
    Hashes/xxhash,digest,bytes,n=100_B-4     1.93GB/s ± 1%
    Hashes/xxhash,digest,string,n=100_B-4    1.01GB/s ±22%
    Hashes/OneOfOne,direct,bytes,n=100_B-4   4.44GB/s ± 2%
    Hashes/OneOfOne,direct,string,n=100_B-4  4.11GB/s ± 1%
    Hashes/OneOfOne,digest,bytes,n=100_B-4   2.62GB/s ± 1%
    Hashes/OneOfOne,digest,string,n=100_B-4  2.50GB/s ± 1%
    Hashes/murmur3,direct,bytes,n=100_B-4    2.95GB/s ± 1%
    Hashes/murmur3,direct,string,n=100_B-4   1.37GB/s ± 1%
    Hashes/murmur3,digest,bytes,n=100_B-4    1.18GB/s ± 1%
    Hashes/murmur3,digest,string,n=100_B-4    798MB/s ± 2%
    Hashes/CRC-32,direct,bytes,n=100_B-4     2.74GB/s ± 2%
    Hashes/CRC-32,direct,string,n=100_B-4    1.23GB/s ± 8%
    Hashes/CRC-32,digest,bytes,n=100_B-4     1.37GB/s ± 1%
    Hashes/CRC-32,digest,string,n=100_B-4     906MB/s ± 2%
    Hashes/fnv1a,digest,bytes,n=100_B-4       816MB/s ± 1%
    Hashes/fnv1a,digest,string,n=100_B-4      630MB/s ± 2%
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4     5.68GB/s ± 2%
    Hashes/xxhash,direct,string,n=100_B-4    5.02GB/s ± 1%
    Hashes/xxhash,digest,bytes,n=100_B-4     1.70GB/s ± 1%
    Hashes/xxhash,digest,string,n=100_B-4    1.03GB/s ± 1%
    
    name                                     alloc/op
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4        0.00B
    Hashes/xxhash,direct,string,n=100_B-4       0.00B
    Hashes/xxhash,digest,bytes,n=100_B-4        80.0B ± 0%
    Hashes/xxhash,digest,string,n=100_B-4        192B ± 0%
    Hashes/OneOfOne,direct,bytes,n=100_B-4      0.00B
    Hashes/OneOfOne,direct,string,n=100_B-4     0.00B
    Hashes/OneOfOne,digest,bytes,n=100_B-4      0.00B
    Hashes/OneOfOne,digest,string,n=100_B-4     0.00B
    Hashes/murmur3,direct,bytes,n=100_B-4       0.00B
    Hashes/murmur3,direct,string,n=100_B-4       112B ± 0%
    Hashes/murmur3,digest,bytes,n=100_B-4       96.0B ± 0%
    Hashes/murmur3,digest,string,n=100_B-4       208B ± 0%
    Hashes/CRC-32,direct,bytes,n=100_B-4        0.00B
    Hashes/CRC-32,direct,string,n=100_B-4        112B ± 0%
    Hashes/CRC-32,digest,bytes,n=100_B-4        16.0B ± 0%
    Hashes/CRC-32,digest,string,n=100_B-4        128B ± 0%
    Hashes/fnv1a,digest,bytes,n=100_B-4         8.00B ± 0%
    Hashes/fnv1a,digest,string,n=100_B-4         120B ± 0%
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4        0.00B
    Hashes/xxhash,direct,string,n=100_B-4       0.00B
    Hashes/xxhash,digest,bytes,n=100_B-4        80.0B ± 0%
    Hashes/xxhash,digest,string,n=100_B-4        192B ± 0%
    
    name                                     allocs/op
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4         0.00
    Hashes/xxhash,direct,string,n=100_B-4        0.00
    Hashes/xxhash,digest,bytes,n=100_B-4         1.00 ± 0%
    Hashes/xxhash,digest,string,n=100_B-4        2.00 ± 0%
    Hashes/OneOfOne,direct,bytes,n=100_B-4       0.00
    Hashes/OneOfOne,direct,string,n=100_B-4      0.00
    Hashes/OneOfOne,digest,bytes,n=100_B-4       0.00
    Hashes/OneOfOne,digest,string,n=100_B-4      0.00
    Hashes/murmur3,direct,bytes,n=100_B-4        0.00
    Hashes/murmur3,direct,string,n=100_B-4       1.00 ± 0%
    Hashes/murmur3,digest,bytes,n=100_B-4        1.00 ± 0%
    Hashes/murmur3,digest,string,n=100_B-4       2.00 ± 0%
    Hashes/CRC-32,direct,bytes,n=100_B-4         0.00
    Hashes/CRC-32,direct,string,n=100_B-4        1.00 ± 0%
    Hashes/CRC-32,digest,bytes,n=100_B-4         1.00 ± 0%
    Hashes/CRC-32,digest,string,n=100_B-4        2.00 ± 0%
    Hashes/fnv1a,digest,bytes,n=100_B-4          1.00 ± 0%
    Hashes/fnv1a,digest,string,n=100_B-4         2.00 ± 0%
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4         0.00
    Hashes/xxhash,direct,string,n=100_B-4        0.00
    Hashes/xxhash,digest,bytes,n=100_B-4         1.00 ± 0%
    Hashes/xxhash,digest,string,n=100_B-4        2.00 ± 0%
    
    Note that the memory allocations currently dominate the xxhash,digest
    benchmarks, as reported in #13
    
    Benchmark taken with:
    (
      echo 'tags: !purego'
      go test -benchtime 3s -count 7 -benchmem -bench '/.*n=100_B'
      echo 'tags: purego'
      go test -benchtime 3s -count 7 -benchmem -tags purego -bench '/xx.*n=100_B'
    ) > base.txt
    benchstat -split tags base.txt
    ongardie-ebay authored and cespare committed Jan 1, 2019
    Copy the full SHA
    15c3e70 View commit details
  2. Update New() to return *Digest (breaking change)

    New used to return a hash.Hash64. This resulted in unnecessary memory
    allocations for the caller, and it prevented the returned value from exposing
    additional methods like WriteString. Digest still implements hash.Hash64, so
    callers may continue to use it as before.
    
    This is a breaking change that may affect some users. Specifically, the
    following code will no longer work:
        var myHashFunc func() hash.Hash64
        myHashFunc = xxhash.New
    
    See #13.
    
    This commit also changes the formatting on the README slightly. The * in
    *Digest caused it to render poorly when simply indenting the godoc output, so
    this commit switches it to ``` instead.
    
    Here is the delta with the previous commit as a baseline. Note the redution in
    memory allocations.
    
    name                                   old time/op    new time/op    delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4     16.3ns ± 2%    16.7ns ± 1%    +2.37%  (p=0.001 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4    18.7ns ± 2%    19.0ns ± 1%    +1.87%  (p=0.005 n=7+6)
    Hashes/xxhash,digest,bytes,n=100_B-4     51.8ns ± 1%    29.4ns ± 1%   -43.28%  (p=0.001 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4     100ns ±26%      67ns ± 1%   -33.57%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4     17.6ns ± 2%    17.8ns ± 2%    +1.22%  (p=0.038 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4    19.9ns ± 1%    20.1ns ± 2%      ~     (p=0.266 n=6+7)
    Hashes/xxhash,digest,bytes,n=100_B-4     58.9ns ± 1%    38.4ns ± 2%   -34.80%  (p=0.000 n=7+6)
    Hashes/xxhash,digest,string,n=100_B-4    96.7ns ± 1%    92.3ns ±28%      ~     (p=0.209 n=7+7)
    
    name                                   old speed      new speed      delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4   6.14GB/s ± 2%  6.00GB/s ± 1%    -2.28%  (p=0.001 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4  5.35GB/s ± 2%  5.24GB/s ± 1%    -2.02%  (p=0.002 n=7+6)
    Hashes/xxhash,digest,bytes,n=100_B-4   1.93GB/s ± 1%  3.40GB/s ± 1%   +76.20%  (p=0.001 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4  1.01GB/s ±22%  1.50GB/s ± 1%   +48.36%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4   5.68GB/s ± 2%  5.61GB/s ± 1%    -1.27%  (p=0.026 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4  5.02GB/s ± 1%  4.99GB/s ± 2%      ~     (p=0.234 n=6+7)
    Hashes/xxhash,digest,bytes,n=100_B-4   1.70GB/s ± 1%  2.60GB/s ± 2%   +53.35%  (p=0.001 n=7+6)
    Hashes/xxhash,digest,string,n=100_B-4  1.03GB/s ± 1%  1.10GB/s ±23%      ~     (p=0.209 n=7+7)
    
    name                                   old alloc/op   new alloc/op   delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4      0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4     0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4      80.0B ± 0%      0.0B       -100.00%  (p=0.001 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4      192B ± 0%      112B ± 0%   -41.67%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4      0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4     0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4      80.0B ± 0%      0.0B       -100.00%  (p=0.001 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4      192B ± 0%      112B ± 0%   -41.67%  (p=0.001 n=7+7)
    
    name                                   old allocs/op  new allocs/op  delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4       0.00           0.00           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4      0.00           0.00           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4       1.00 ± 0%      0.00       -100.00%  (p=0.001 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4      2.00 ± 0%      1.00 ± 0%   -50.00%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4       0.00           0.00           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4      0.00           0.00           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4       1.00 ± 0%      0.00       -100.00%  (p=0.001 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4      2.00 ± 0%      1.00 ± 0%   -50.00%  (p=0.001 n=7+7)
    ongardie-ebay authored and cespare committed Jan 1, 2019
    Copy the full SHA
    9db2993 View commit details
  3. Add Digest.WriteString()

    This helps avoid a memory allocations when hashing strings using a Digest. See
    #13.
    
    Here's the delta with the previous commit, showing how WriteString avoids the
    last alllocation.
    
    name                                   old time/op    new time/op    delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4     16.7ns ± 1%    17.5ns ±11%      ~     (p=0.500 n=7+6)
    Hashes/xxhash,direct,string,n=100_B-4    19.0ns ± 1%    19.1ns ± 2%      ~     (p=0.925 n=6+7)
    Hashes/xxhash,digest,bytes,n=100_B-4     29.4ns ± 1%    29.5ns ± 2%      ~     (p=0.730 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4    66.6ns ± 1%    32.5ns ± 1%   -51.27%  (p=0.001 n=7+6)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4     17.8ns ± 2%    17.7ns ± 1%      ~     (p=0.461 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4    20.1ns ± 2%    20.0ns ± 1%      ~     (p=0.867 n=7+7)
    Hashes/xxhash,digest,bytes,n=100_B-4     38.4ns ± 2%    38.1ns ± 2%      ~     (p=0.133 n=6+7)
    Hashes/xxhash,digest,string,n=100_B-4    92.3ns ±28%    41.2ns ± 1%   -55.34%  (p=0.001 n=7+7)
    
    name                                   old speed      new speed      delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4   6.00GB/s ± 1%  5.50GB/s ±26%      ~     (p=0.383 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4  5.24GB/s ± 1%  5.25GB/s ± 2%      ~     (p=0.945 n=6+7)
    Hashes/xxhash,digest,bytes,n=100_B-4   3.40GB/s ± 1%  3.39GB/s ± 2%      ~     (p=1.000 n=7+7)
    Hashes/xxhash,digest,string,n=100_B-4  1.50GB/s ± 1%  3.07GB/s ± 2%  +104.55%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4   5.61GB/s ± 1%  5.64GB/s ± 1%      ~     (p=0.259 n=7+7)
    Hashes/xxhash,direct,string,n=100_B-4  4.99GB/s ± 2%  4.99GB/s ± 1%      ~     (p=0.710 n=7+7)
    Hashes/xxhash,digest,bytes,n=100_B-4   2.60GB/s ± 2%  2.62GB/s ± 2%      ~     (p=0.181 n=6+7)
    Hashes/xxhash,digest,string,n=100_B-4  1.10GB/s ±23%  2.43GB/s ± 1%  +120.31%  (p=0.001 n=7+7)
    
    name                                   old alloc/op   new alloc/op   delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4      0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4     0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4      0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,digest,string,n=100_B-4      112B ± 0%        0B       -100.00%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4      0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4     0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4      0.00B          0.00B           ~     (all equal)
    Hashes/xxhash,digest,string,n=100_B-4      112B ± 0%        0B       -100.00%  (p=0.001 n=7+7)
    
    name                                   old allocs/op  new allocs/op  delta
    tags:!purego
    Hashes/xxhash,direct,bytes,n=100_B-4       0.00           0.00           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4      0.00           0.00           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4       0.00           0.00           ~     (all equal)
    Hashes/xxhash,digest,string,n=100_B-4      1.00 ± 0%      0.00       -100.00%  (p=0.001 n=7+7)
    tags:purego
    Hashes/xxhash,direct,bytes,n=100_B-4       0.00           0.00           ~     (all equal)
    Hashes/xxhash,direct,string,n=100_B-4      0.00           0.00           ~     (all equal)
    Hashes/xxhash,digest,bytes,n=100_B-4       0.00           0.00           ~     (all equal)
    Hashes/xxhash,digest,string,n=100_B-4      1.00 ± 0%      0.00       -100.00%  (p=0.001 n=7+7)
    ongardie-ebay authored and cespare committed Jan 1, 2019
    Copy the full SHA
    f728abc View commit details
  4. Copy the full SHA
    9d72dfe View commit details
  5. Tweak doc comments

    cespare committed Jan 1, 2019
    Copy the full SHA
    bf43f18 View commit details
  6. Copy the full SHA
    2631520 View commit details
  7. Copy the full SHA
    fd51581 View commit details

Commits on Jan 4, 2019

  1. Tidy up the README

    cespare committed Jan 4, 2019
    Copy the full SHA
    b821ea8 View commit details
  2. Delete commented-out test

    No point in keeping this around. Running automated tests with various
    build tags is good enough.
    cespare committed Jan 4, 2019
    Copy the full SHA
    733af8e View commit details
  3. Remove pre-Go1.9 fallback code

    Go 1.12beta1 is out and even Appengine's oldest runtime is Go 1.9,
    so get rid of pre-1.9 fallback code and always use math/bits.
    cespare committed Jan 4, 2019
    Copy the full SHA
    7052e46 View commit details
  4. Copy the full SHA
    30d0e5b View commit details
  5. Copy the full SHA
    3b82fb7 View commit details
Showing with 475 additions and 275 deletions.
  1. +8 −0 .travis.yml
  2. +26 −21 README.md
  3. +1 −1 go.mod
  4. +0 −14 rotate.go
  5. +0 −14 rotate19.go
  6. +113 −45 xxhash.go
  7. +2 −1 xxhash_amd64.go
  8. +9 −27 xxhash_amd64.s
  9. +0 −41 xxhash_amd64_test.go
  10. +8 −7 xxhash_other.go
  11. +5 −0 xxhash_safe.go
  12. +254 −95 xxhash_test.go
  13. +24 −8 xxhash_unsafe.go
  14. +24 −0 xxhash_unsafe_test.go
  15. +1 −1 xxhsum/xxhsum.go
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language: go
go:
- "1.x"
- master
env:
- TAGS=""
- TAGS="-tags purego"
script: go test $TAGS -v ./...
47 changes: 26 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,55 @@
# xxhash

[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash)
[![Build Status](https://travis-ci.org/cespare/xxhash.svg?branch=master)](https://travis-ci.org/cespare/xxhash)

xxhash is a Go implementation of the 64-bit
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
high-quality hashing algorithm that is much faster than anything in the Go
standard library.

The API is very small, taking its cue from the other hashing packages in the
standard library:
This package provides a straightforward API:

$ go doc github.com/cespare/xxhash !
package xxhash // import "github.com/cespare/xxhash"
```
func Sum64(b []byte) uint64
func Sum64String(s string) uint64
type Digest struct{ ... }
func New() *Digest
```

Package xxhash implements the 64-bit variant of xxHash (XXH64) as described
at http://cyan4973.github.io/xxHash/.
The `Digest` type implements hash.Hash64. Its key methods are:

func New() hash.Hash64
func Sum64(b []byte) uint64
func Sum64String(s string) uint64
```
func (*Digest) Write([]byte) (int, error)
func (*Digest) WriteString(string) (int, error)
func (*Digest) Sum64() uint64
```

This implementation provides a fast pure-Go implementation and an even faster
assembly implementation for amd64.

## Benchmarks

Here are some quick benchmarks comparing the pure-Go and assembly
implementations of Sum64 against another popular Go XXH64 implementation,
[github.com/OneOfOne/xxhash](https://github.com/OneOfOne/xxhash):
implementations of Sum64.

| input size | OneOfOne | cespare (purego) | cespare |
| --- | --- | --- | --- |
| 5 B | 416 MB/s | 720 MB/s | 872 MB/s |
| 100 B | 3980 MB/s | 5013 MB/s | 5252 MB/s |
| 4 KB | 12727 MB/s | 12999 MB/s | 13026 MB/s |
| 10 MB | 9879 MB/s | 10775 MB/s | 10913 MB/s |
| input size | purego | asm |
| --- | --- | --- |
| 5 B | 979.66 MB/s | 1291.17 MB/s |
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |

These numbers were generated with:
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
the following commands under Go 1.11.2:

```
$ go test -benchtime 10s -bench '/OneOfOne,'
$ go test -tags purego -benchtime 10s -bench '/xxhash,'
$ go test -benchtime 10s -bench '/xxhash,'
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
```

## Projects using this package

- [InfluxDB](https://github.com/influxdata/influxdb)
- [Prometheus](https://github.com/prometheus/prometheus)
- [FreeCache](https://github.com/coocood/freecache)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/cespare/xxhash
module github.com/cespare/xxhash/v2

require (
github.com/OneOfOne/xxhash v1.2.2
14 changes: 0 additions & 14 deletions rotate.go

This file was deleted.

14 changes: 0 additions & 14 deletions rotate19.go

This file was deleted.

158 changes: 113 additions & 45 deletions xxhash.go
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@ package xxhash

import (
"encoding/binary"
"hash"
"errors"
"math/bits"
)

const (
@@ -29,72 +30,79 @@ var (
prime5v = prime5
)

type xxh struct {
// Digest implements hash.Hash64.
type Digest struct {
v1 uint64
v2 uint64
v3 uint64
v4 uint64
total int
total uint64
mem [32]byte
n int // how much of mem is used
}

// New creates a new hash.Hash64 that implements the 64-bit xxHash algorithm.
func New() hash.Hash64 {
var x xxh
x.Reset()
return &x
// New creates a new Digest that computes the 64-bit xxHash algorithm.
func New() *Digest {
var d Digest
d.Reset()
return &d
}

func (x *xxh) Reset() {
x.n = 0
x.total = 0
x.v1 = prime1v + prime2
x.v2 = prime2
x.v3 = 0
x.v4 = -prime1v
// Reset clears the Digest's state so that it can be reused.
func (d *Digest) Reset() {
d.v1 = prime1v + prime2
d.v2 = prime2
d.v3 = 0
d.v4 = -prime1v
d.total = 0
d.n = 0
}

func (x *xxh) Size() int { return 8 }
func (x *xxh) BlockSize() int { return 32 }
// Size always returns 8 bytes.
func (d *Digest) Size() int { return 8 }

// Write adds more data to x. It always returns len(b), nil.
func (x *xxh) Write(b []byte) (n int, err error) {
// BlockSize always returns 32 bytes.
func (d *Digest) BlockSize() int { return 32 }

// Write adds more data to d. It always returns len(b), nil.
func (d *Digest) Write(b []byte) (n int, err error) {
n = len(b)
x.total += len(b)
d.total += uint64(n)

if x.n+len(b) < 32 {
if d.n+n < 32 {
// This new data doesn't even fill the current block.
copy(x.mem[x.n:], b)
x.n += len(b)
copy(d.mem[d.n:], b)
d.n += n
return
}

if x.n > 0 {
if d.n > 0 {
// Finish off the partial block.
copy(x.mem[x.n:], b)
x.v1 = round(x.v1, u64(x.mem[0:8]))
x.v2 = round(x.v2, u64(x.mem[8:16]))
x.v3 = round(x.v3, u64(x.mem[16:24]))
x.v4 = round(x.v4, u64(x.mem[24:32]))
b = b[32-x.n:]
x.n = 0
copy(d.mem[d.n:], b)
d.v1 = round(d.v1, u64(d.mem[0:8]))
d.v2 = round(d.v2, u64(d.mem[8:16]))
d.v3 = round(d.v3, u64(d.mem[16:24]))
d.v4 = round(d.v4, u64(d.mem[24:32]))
b = b[32-d.n:]
d.n = 0
}

if len(b) >= 32 {
// One or more full blocks left.
b = writeBlocks(x, b)
nw := writeBlocks(d, b)
b = b[nw:]
}

// Store any remaining partial block.
copy(x.mem[:], b)
x.n = len(b)
copy(d.mem[:], b)
d.n = len(b)

return
}

func (x *xxh) Sum(b []byte) []byte {
s := x.Sum64()
// Sum appends the current hash to b and returns the resulting slice.
func (d *Digest) Sum(b []byte) []byte {
s := d.Sum64()
return append(
b,
byte(s>>56),
@@ -108,35 +116,36 @@ func (x *xxh) Sum(b []byte) []byte {
)
}

func (x *xxh) Sum64() uint64 {
// Sum64 returns the current hash.
func (d *Digest) Sum64() uint64 {
var h uint64

if x.total >= 32 {
v1, v2, v3, v4 := x.v1, x.v2, x.v3, x.v4
if d.total >= 32 {
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
h = mergeRound(h, v1)
h = mergeRound(h, v2)
h = mergeRound(h, v3)
h = mergeRound(h, v4)
} else {
h = x.v3 + prime5
h = d.v3 + prime5
}

h += uint64(x.total)
h += d.total

i, end := 0, x.n
i, end := 0, d.n
for ; i+8 <= end; i += 8 {
k1 := round(0, u64(x.mem[i:i+8]))
k1 := round(0, u64(d.mem[i:i+8]))
h ^= k1
h = rol27(h)*prime1 + prime4
}
if i+4 <= end {
h ^= uint64(u32(x.mem[i:i+4])) * prime1
h ^= uint64(u32(d.mem[i:i+4])) * prime1
h = rol23(h)*prime2 + prime3
i += 4
}
for i < end {
h ^= uint64(x.mem[i]) * prime5
h ^= uint64(d.mem[i]) * prime5
h = rol11(h) * prime1
i++
}
@@ -150,6 +159,56 @@ func (x *xxh) Sum64() uint64 {
return h
}

const (
magic = "xxh\x06"
marshaledSize = len(magic) + 8*5 + 32
)

// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (d *Digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = appendUint64(b, d.v1)
b = appendUint64(b, d.v2)
b = appendUint64(b, d.v3)
b = appendUint64(b, d.v4)
b = appendUint64(b, d.total)
b = append(b, d.mem[:d.n]...)
b = b[:len(b)+len(d.mem)-d.n]
return b, nil
}

// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (d *Digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("xxhash: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("xxhash: invalid hash state size")
}
b = b[len(magic):]
b, d.v1 = consumeUint64(b)
b, d.v2 = consumeUint64(b)
b, d.v3 = consumeUint64(b)
b, d.v4 = consumeUint64(b)
b, d.total = consumeUint64(b)
copy(d.mem[:], b)
b = b[len(d.mem):]
d.n = int(d.total % uint64(len(d.mem)))
return nil
}

func appendUint64(b []byte, x uint64) []byte {
var a [8]byte
binary.LittleEndian.PutUint64(a[:], x)
return append(b, a[:]...)
}

func consumeUint64(b []byte) ([]byte, uint64) {
x := u64(b)
return b[8:], x
}

func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }
func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }

@@ -166,3 +225,12 @@ func mergeRound(acc, val uint64) uint64 {
acc = acc*prime1 + prime4
return acc
}

func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) }
func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) }
func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) }
func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) }
func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) }
func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) }
func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) }
func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) }
3 changes: 2 additions & 1 deletion xxhash_amd64.go
Original file line number Diff line number Diff line change
@@ -9,4 +9,5 @@ package xxhash
//go:noescape
func Sum64(b []byte) uint64

func writeBlocks(x *xxh, b []byte) []byte
//go:noescape
func writeBlocks(*Digest, []byte) int
Loading