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

Improve performance of TSI Bloom Filter #8857

Merged
merged 4 commits into from
Sep 25, 2017
Merged

Improve performance of TSI Bloom Filter #8857

merged 4 commits into from
Sep 25, 2017

Conversation

e-dard
Copy link
Contributor

@e-dard e-dard commented Sep 20, 2017

Required for all non-trivial PRs
  • Rebased/mergable
  • Tests pass
  • CHANGELOG.md updated

This PR changes the hashing regime currently used in the TSI Bloom Filter (murmur3) to xxhash, and also, using the linear combination of two hashes trick, reduces the number of hashes generated per Contains/Insert call from two to one.

Further, allocations have been eradicated since xxhash is allocation-free. This allows us to remove the existing pool of hashers.

The overall improvement in performance is about 5x .

Note

Since this changes the hashing functions used to lookup the (possible) existence of series in TSI index files, any existing TSI files are no longer going to work. Anyone who has existing TSI indexes will need to remove them (which will put the shard back on the inmem index), and then recreate a TSI index using the influx_inspect inmem2tsi tool.

Benchmarks

edd@work:~|⇒  benchstat old_bloom.txt new_bloom.txt  
name                                     old time/op    new time/op    delta
Filter_Insert/m=100_k=4_n=1000-24           258µs ± 3%      53µs ± 2%   -79.47%  (p=0.000 n=10+10)
Filter_Insert/m=1000_k=4_n=1000-24          257µs ± 3%      53µs ± 1%   -79.50%  (p=0.000 n=10+10)
Filter_Insert/m=10000_k=4_n=1000-24         255µs ± 4%      52µs ± 2%   -79.54%  (p=0.000 n=10+10)
Filter_Insert/m=100000_k=4_n=1000-24        260µs ± 2%      57µs ± 1%   -78.22%  (p=0.000 n=10+10)
Filter_Insert/m=100_k=8_n=1000-24           273µs ± 3%      65µs ± 2%   -76.17%  (p=0.000 n=10+10)
Filter_Insert/m=1000_k=8_n=1000-24          275µs ± 2%      65µs ± 2%   -76.27%  (p=0.000 n=10+10)
Filter_Insert/m=10000_k=8_n=1000-24         274µs ± 3%      65µs ± 2%   -76.26%  (p=0.000 n=10+10)
Filter_Insert/m=100000_k=8_n=1000-24        274µs ± 2%      66µs ± 1%   -76.05%  (p=0.000 n=10+9)
Filter_Insert/m=100_k=20_n=1000-24          314µs ± 2%     113µs ± 1%   -64.09%  (p=0.000 n=10+9)
Filter_Insert/m=1000_k=20_n=1000-24         315µs ± 2%     113µs ± 1%   -64.08%  (p=0.000 n=10+9)
Filter_Insert/m=10000_k=20_n=1000-24        311µs ± 0%     112µs ± 2%   -64.16%  (p=0.000 n=6+10)
Filter_Insert/m=100000_k=20_n=1000-24       317µs ± 2%     111µs ± 2%   -65.11%  (p=0.000 n=9+10)
Filter_Contains/m=100_k=4_n=1000-24         503µs ± 3%      76µs ± 2%   -84.84%  (p=0.000 n=10+10)
Filter_Contains/m=1000_k=4_n=1000-24        507µs ± 2%      78µs ± 1%   -84.58%  (p=0.000 n=9+10)
Filter_Contains/m=10000_k=4_n=1000-24       529µs ± 4%      80µs ± 1%   -84.90%  (p=0.000 n=10+10)
Filter_Contains/m=100000_k=4_n=1000-24      510µs ± 4%      76µs ± 2%   -85.17%  (p=0.000 n=9+10)
Filter_Contains/m=100_k=8_n=1000-24         526µs ± 4%     101µs ± 2%   -80.81%  (p=0.000 n=10+10)
Filter_Contains/m=1000_k=8_n=1000-24        524µs ± 4%     101µs ± 1%   -80.63%  (p=0.000 n=10+10)
Filter_Contains/m=10000_k=8_n=1000-24       531µs ± 5%      97µs ± 1%   -81.78%  (p=0.000 n=10+10)
Filter_Contains/m=100000_k=8_n=1000-24      536µs ± 2%      86µs ± 1%   -84.04%  (p=0.000 n=10+10)
Filter_Contains/m=100_k=20_n=1000-24        604µs ± 4%     137µs ± 2%   -77.38%  (p=0.000 n=10+10)
Filter_Contains/m=1000_k=20_n=1000-24       596µs ± 3%     136µs ± 1%   -77.23%  (p=0.000 n=10+10)
Filter_Contains/m=10000_k=20_n=1000-24      580µs ± 4%     125µs ± 0%   -78.51%  (p=0.000 n=10+8)
Filter_Contains/m=100000_k=20_n=1000-24     560µs ± 5%     107µs ± 1%   -80.82%  (p=0.000 n=10+8)
Filter_Merge/m=100_k=4_n=1000-24            411ns ± 3%     406ns ± 1%      ~     (p=0.265 n=10+9)
Filter_Merge/m=1000_k=4_n=1000-24           648ns ± 1%     604ns ± 1%    -6.79%  (p=0.000 n=10+10)
Filter_Merge/m=10000_k=4_n=1000-24         4.45µs ± 8%    3.95µs ± 6%   -11.31%  (p=0.000 n=10+10)
Filter_Merge/m=100000_k=4_n=1000-24        23.8µs ±21%    21.9µs ±17%      ~     (p=0.123 n=10+10)
Filter_Merge/m=100_k=8_n=1000-24            422ns ± 1%     411ns ± 1%    -2.50%  (p=0.000 n=9+9)
Filter_Merge/m=1000_k=8_n=1000-24           625ns ± 2%     614ns ± 1%    -1.78%  (p=0.001 n=10+10)
Filter_Merge/m=10000_k=8_n=1000-24         4.48µs ± 7%    3.78µs ± 5%   -15.57%  (p=0.000 n=10+10)
Filter_Merge/m=100000_k=8_n=1000-24        23.1µs ±20%    27.0µs ±11%   +16.76%  (p=0.002 n=10+10)
Filter_Merge/m=100_k=20_n=1000-24           421ns ± 1%     412ns ± 1%    -2.02%  (p=0.000 n=10+10)
Filter_Merge/m=1000_k=20_n=1000-24          625ns ± 2%     612ns ± 2%    -2.10%  (p=0.000 n=10+10)
Filter_Merge/m=10000_k=20_n=1000-24        3.51µs ± 3%    3.62µs ± 3%    +2.92%  (p=0.012 n=8+10)
Filter_Merge/m=100000_k=20_n=1000-24       26.4µs ±10%    27.0µs ±19%      ~     (p=0.549 n=9+10)

name                                     old alloc/op   new alloc/op   delta
Filter_Insert/m=100_k=4_n=1000-24          1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=9+10)
Filter_Insert/m=1000_k=4_n=1000-24         1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=10000_k=4_n=1000-24        1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=100000_k=4_n=1000-24       1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=100_k=8_n=1000-24          1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=1000_k=8_n=1000-24         1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=10000_k=8_n=1000-24        1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=100000_k=8_n=1000-24       1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=100_k=20_n=1000-24         1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=1000_k=20_n=1000-24        1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=10000_k=20_n=1000-24       1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Insert/m=100000_k=20_n=1000-24      1.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=100_k=4_n=1000-24        2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=1000_k=4_n=1000-24       2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=10000_k=4_n=1000-24      2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=100000_k=4_n=1000-24     2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=100_k=8_n=1000-24        2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=1000_k=8_n=1000-24       2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=10000_k=8_n=1000-24      2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=100000_k=8_n=1000-24     2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=100_k=20_n=1000-24       2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=1000_k=20_n=1000-24      2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=10000_k=20_n=1000-24     2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Contains/m=100000_k=20_n=1000-24    2.00kB ± 0%    0.00kB       -100.00%  (p=0.000 n=10+10)
Filter_Merge/m=100_k=4_n=1000-24             416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=1000_k=4_n=1000-24            416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=10000_k=4_n=1000-24           416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=100000_k=4_n=1000-24          416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=100_k=8_n=1000-24             416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=1000_k=8_n=1000-24            416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=10000_k=8_n=1000-24           416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=100000_k=8_n=1000-24          416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=100_k=20_n=1000-24            416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=1000_k=20_n=1000-24           416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=10000_k=20_n=1000-24          416B ± 0%      416B ± 0%      ~     (all equal)
Filter_Merge/m=100000_k=20_n=1000-24         416B ± 0%      416B ± 0%      ~     (all equal)

@e-dard e-dard added this to the 1.4.0 milestone Sep 20, 2017
@e-dard
Copy link
Contributor Author

e-dard commented Sep 20, 2017

/cc @pauldix regarding destructive nature of this PR on existing TSI indexes.

@pauldix
Copy link
Member

pauldix commented Sep 20, 2017

@e-dard if they happen to still have old TSI files, will it show an error in the logs and ignore them?

@jwilder
Copy link
Contributor

jwilder commented Sep 20, 2017

I noticed Contains is using a div and mod. If you apply this:

diff --git a/pkg/bloom/bloom.go b/pkg/bloom/bloom.go
index 44031cb..22bad7a 100644
--- a/pkg/bloom/bloom.go
+++ b/pkg/bloom/bloom.go
@@ -70,7 +70,7 @@ func (f *Filter) Contains(v []byte) bool {
        h := f.hash(v)
        for i := uint64(0); i < f.k; i++ {
                loc := f.location(h, i)
-               if f.b[loc/8]&(1<<(loc%8)) == 0 {
+               if f.b[loc>>3]&(1<<(loc&7)) == 0 {
                        return false
                }
        }

You get a little bump in perf:

benchmark                                           old ns/op     new ns/op     delta
BenchmarkFilter_Insert/m=100_k=4_n=1000-8           46424         45067         -2.92%
BenchmarkFilter_Insert/m=1000_k=4_n=1000-8          46080         44299         -3.87%
BenchmarkFilter_Insert/m=10000_k=4_n=1000-8         47096         44519         -5.47%
BenchmarkFilter_Insert/m=100000_k=4_n=1000-8        45176         44210         -2.14%
BenchmarkFilter_Insert/m=100_k=8_n=1000-8           55427         53717         -3.09%
BenchmarkFilter_Insert/m=1000_k=8_n=1000-8          54040         55203         +2.15%
BenchmarkFilter_Insert/m=10000_k=8_n=1000-8         55154         55613         +0.83%
BenchmarkFilter_Insert/m=100000_k=8_n=1000-8        53395         55187         +3.36%
BenchmarkFilter_Insert/m=100_k=20_n=1000-8          83666         83027         -0.76%
BenchmarkFilter_Insert/m=1000_k=20_n=1000-8         81250         80591         -0.81%
BenchmarkFilter_Insert/m=10000_k=20_n=1000-8        83647         79664         -4.76%
BenchmarkFilter_Insert/m=100000_k=20_n=1000-8       82659         80717         -2.35%
BenchmarkFilter_Contains/m=100_k=4_n=1000-8         93512         89954         -3.80%
BenchmarkFilter_Contains/m=1000_k=4_n=1000-8        89847         89260         -0.65%
BenchmarkFilter_Contains/m=10000_k=4_n=1000-8       88685         82138         -7.38%
BenchmarkFilter_Contains/m=100000_k=4_n=1000-8      80133         81099         +1.21%
BenchmarkFilter_Contains/m=100_k=8_n=1000-8         108504        108895        +0.36%
BenchmarkFilter_Contains/m=1000_k=8_n=1000-8        111096        104599        -5.85%
BenchmarkFilter_Contains/m=10000_k=8_n=1000-8       98540         97190         -1.37%
BenchmarkFilter_Contains/m=100000_k=8_n=1000-8      91293         87978         -3.63%
BenchmarkFilter_Contains/m=100_k=20_n=1000-8        163548        153404        -6.20%
BenchmarkFilter_Contains/m=1000_k=20_n=1000-8       159283        154785        -2.82%
BenchmarkFilter_Contains/m=10000_k=20_n=1000-8      138598        130379        -5.93%
BenchmarkFilter_Contains/m=100000_k=20_n=1000-8     120709        112999        -6.39%
BenchmarkFilter_Merge/m=100_k=4_n=1000-8            66.1          65.1          -1.51%
BenchmarkFilter_Merge/m=1000_k=4_n=1000-8           201           198           -1.49%
BenchmarkFilter_Merge/m=10000_k=4_n=1000-8          2388          2282          -4.44%
BenchmarkFilter_Merge/m=100000_k=4_n=1000-8         19320         18153         -6.04%
BenchmarkFilter_Merge/m=100_k=8_n=1000-8            65.8          64.9          -1.37%
BenchmarkFilter_Merge/m=1000_k=8_n=1000-8           203           198           -2.46%
BenchmarkFilter_Merge/m=10000_k=8_n=1000-8          2466          2311          -6.29%
BenchmarkFilter_Merge/m=100000_k=8_n=1000-8         19216         18118         -5.71%
BenchmarkFilter_Merge/m=100_k=20_n=1000-8           66.8          65.8          -1.50%
BenchmarkFilter_Merge/m=1000_k=20_n=1000-8          203           198           -2.46%
BenchmarkFilter_Merge/m=10000_k=20_n=1000-8         2570          2350          -8.56%
BenchmarkFilter_Merge/m=100000_k=20_n=1000-8        19193         18533         -3.44%

Copy link
Contributor

@jwilder jwilder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit, but LGTM. Nice speedup!

return murmur3.New128()
}),
}
return &Filter{k: k, b: make([]byte, m/8), mask: m - 1}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of m/8, you can use m >> 3 which is a little more efficient.

@e-dard
Copy link
Contributor Author

e-dard commented Sep 21, 2017

@jwilder see e4013f6

@jwilder @benbjohnson see be45245 for a tsi1 versioning scheme. The idea is we'll add a version field the the index's MANIFEST (since all TSI indexes must have a MANIFEST). The current version will be set to 1. When a new TSI index is created it will write out a MANIFEST with "version": 1. For existing indexes they will be deemed incompatible (since their versions will be 0 due to the absence of a version field on the MANIFEST.

Attempting to open an incompatible index results in Open returning an error, which is propagated up to the shard level, which itself will refuse to open. There will be plenty of log noise.

@rbetts FYI - I think it's important we make a reminder somewhere to add something to the release notes for whatever version ships TSI. We'll need to describe the index conversion steps for any users who have been running TSI on the nightlies prior to this PR change.

}
return nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually think of Err as the error type instead of the validation function. Can you do something like:

// ErrIncompatibleVersion is returned when attempting to read from an
// incompatible tsi1 manifest file.
var ErrIncompatibleVersion = errors.New("incompatible tsi1 index MANIFEST")

// ErrIncompatibleVersion returns an IncompatibleVersionError if the provided
// manifest is incompatible with the current tsi1 package.
func (m *Manifest) Validate() error {
	// If we don't have an explicit version in the manifest file then we know
	// it's not compatible with the latest tsi1 Index.
	if m.Version != Version {
		return ErrIncompatibleVersion
	}
	return nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I should add that error types should end in ...Error and error values should begin with Err... but I know what you mean, and in this case an explicit type was overkill and I never checked it anywhere.

This commit replaces the previous hashing algorithm used by the pkg.Filter with
one based on xxhash. Further, taking from the hashing literature, we can
represent k hashes with only two hash function, where previously Filter was using
four.

Further, unlike `murmur3`, `xxhash` is allocation-free, so allocations have
dramatically reduced when inserting and checking for hashes.
This commit adds a basic TSI versioning scheme, by adding a Version field
to an index's MANIFEST file.

Existing TSI indexes will not have this field present in their MANIFEST
files, and thus will be deemed incomatible with the current version.

Users with existing TSI indexes will be able to remove them, and convert the
resulting inmem indexes to the current version of a TSI index using the
influx_inspect tooling.
@bong0
Copy link

bong0 commented Oct 13, 2017

Thank you very much for adding this improvement, I experienced a multi-gigabyte drop in RSS memory due to the change from murmur to xxhash! (Previously I inspected influx with pprof and recognized that the major portion of heap memory was allocated to 'objects' from the bloom pkg)

As you stated, I dropped the old indexes and recreated them and installed 1.3.7rc0 which includes this PR here :)

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

Successfully merging this pull request may close these issues.

None yet

5 participants