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

ES|QL deserves a new hash table #98749

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

ES|QL deserves a new hash table #98749

wants to merge 2 commits into from

Conversation

nik9000
Copy link
Member

@nik9000 nik9000 commented Aug 22, 2023

We've been using LongHash and LongLongHash which are open addressed, linear probing hash tables that grow in place. They have served us well, but we need to add features to them to support all of ES|QL. It turns out that there've been a lot of advances in the hash space in the ten years since we wrote these hash tables! And they weren't the most "advanced" thing back then. This PR creates a new hash table implementation the borrows significantly from google's Swiss Tables. It's 25% to 49% faster in microbenchmarks:

 unique   longHash          ordinator64
      5   7.470 ± 0.033 ->   4.158 ± 0.037 ns/op 45% faster
   1000   9.657 ± 0.375 ->   4.907 ± 0.036 ns/op 49% faster
  10000  15.505 ± 0.051 ->  11.609 ± 0.062 ns/op 25% faster
 100000  20.948 ± 0.112 ->  13.413 ± 0.764 ns/op 35% fsater
1000000  48.507 ± 0.586 ->  36.306 ± 0.296 ns/op 25% faster

This also integrates the new table into ES|QL's grouping functions, though imperfectly at the moment.

We've been using `LongHash` and `LongLongHash` which are open addressed,
linear probing hash tables that grow in place. They have served us well,
but we need to add features to them to support all of ES|QL. It turns out
that there've been a lot of advances in the hash space in the ten years
since we wrote these hash tables! And they weren't the most "advanced"
thing back then. This PR creates a new hash table implementation the
borrows significantly from google's Swiss Tables. It's 25% to 49% faster
in microbenchmarks:

```
 unique   longHash          ordinator64
      5   7.470 ± 0.033 ->   4.158 ± 0.037 ns/op 45% faster
   1000   9.657 ± 0.375 ->   4.907 ± 0.036 ns/op 49% faster
  10000  15.505 ± 0.051 ->  11.609 ± 0.062 ns/op 25% faster
 100000  20.948 ± 0.112 ->  13.413 ± 0.764 ns/op 35% fsater
1000000  48.507 ± 0.586 ->  36.306 ± 0.296 ns/op 25% faster
```

This also integrates the new table into ES|QL's grouping functions,
though imperfectly at the moment.
@@ -58,14 +64,12 @@
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(1)
@Fork(value = 1, jvmArgsAppend = { "--enable-preview", "--add-modules", "jdk.incubator.vector" })
Copy link
Member Author

Choose a reason for hiding this comment

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

I've updated this to work with the new hash, but it doesn't produce the lovely performance numbers - yet. Partly that's because we're not integrating with the hash super well - the vector case needs to consume the array somehow. Or something similar. But that feels like something for another time.

Copy link
Member Author

Choose a reason for hiding this comment

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

The other reason this doesn't show the performance bump we expect is because we don't enable all of the other aggregations - and because we don't aggregate much larger groups. Either way, this benchmark is much better at showing the performance of aggs, not the groupings. At least not yet.

@@ -123,7 +123,7 @@ public static void configureCompile(Project project) {
// fail on all javac warnings.
// TODO Discuss moving compileOptions.getCompilerArgs() to use provider api with Gradle team.
List<String> compilerArgs = compileOptions.getCompilerArgs();
compilerArgs.add("-Werror");
// compilerArgs.add("-Werror"); NOCOMMIT add me back once we figure out how to not fail compiling with preview features
Copy link
Member Author

Choose a reason for hiding this comment

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

This'd be a huge problem to commit, but I can't figure out a good way around it. If I enable the vector API it'll emit the warning. I think Lucene has some kind of hack for accessing the vector API that I think would fix this. And we'd want to steal that.

it.properties = doubleProperties
it.inputFile = blockHashInputFile
it.outputFile = "org/elasticsearch/compute/aggregation/blockhash/DoubleBlockHash.java"
}
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm generating these so it's easier to keep them updated. I'll generate some more Ordinators at some point - at 32 and 128 bit one at least. But that's another follow up.

for (int i = 0; i < vector.getPositionCount(); i++) {
groups[i] = hashOrdToGroupNullReserved(longHash.add(Double.doubleToLongBits(vector.getDouble(i))));
groups[i] = ordinator.add(Double.doubleToLongBits(vector.getDouble(i)));
}
return new LongArrayVector(groups, groups.length);
Copy link
Member Author

Choose a reason for hiding this comment

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

We need to use an array style add to make this fast. It should be possible, but I'd like to wait for this in a follow up too.

* explosion of groups caused by multivalued fields
*/
public BlockHash build(List<HashAggregationOperator.GroupSpec> groups, int emitBatchSize) {
if (groups.size() == 1) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Here's a place where we can plug in detection of the vector API not being available. If we don't have the vector API we could always use PackadVauesBlockHash which we can make sure doesn't use the new classes.

Or, potentially, we could turn off all of ESQL if you don't have the vector API.

@nik9000
Copy link
Member Author

nik9000 commented Aug 22, 2023

run elasticsearch-ci/part-2

@dnhatn
Copy link
Member

dnhatn commented Aug 22, 2023

It's 25% to 49% faster in microbenchmarks

Wow - impressive results. I will have a look at this PR shortly today.

@ChrisHegarty
Copy link
Contributor

High-level, I love the idea.

There is a new-to-Elasticsearch dependency on the Panama Vector API. I think that this is fine, but will clearly need some rework and discussion about how it should be integrated.

FWIW, I think that we should adopt a similar strategy as that of what is currently done in Lucene. First, generate and checkin a JDK-version specific api jar containing the Vector API stubs - this can be used to compile against. Second, build the Vector dependent code into the MRJAR versioned section of the jar. Lastly, dynamically load either the SIMD'ized version or a fallback non-SIMD version of the code, at runtime, depending on the JDK runtime version.

@nik9000
Copy link
Member Author

nik9000 commented Aug 23, 2023

So what's the right next step here? I'd love to push this somehow, but am not entirely sure how.

@ChrisHegarty
Copy link
Contributor

So what's the right next step here? I'd love to push this somehow, but am not entirely sure how.

It's on me. I'll figure out a plan and file a GH issue for it.

nik9000 added a commit to nik9000/elasticsearch that referenced this pull request Sep 11, 2023
We have optimizations that kick in when aggregating on the following
pairs of field types:
* `long`, `long`
* `keyword`, `long`
* `long`, `keyword`

These optimizations don't have proper support for `null` valued fields
but will grow that after elastic#98749. In the mean time this disables them in
a way that prevents them from bit-rotting.
nik9000 added a commit that referenced this pull request Sep 11, 2023
* ESQL: Disable optimizations with bad null handling

We have optimizations that kick in when aggregating on the following
pairs of field types:
* `long`, `long`
* `keyword`, `long`
* `long`, `keyword`

These optimizations don't have proper support for `null` valued fields
but will grow that after #98749. In the mean time this disables them in
a way that prevents them from bit-rotting.

* Update docs/changelog/99434.yaml
@mattc58 mattc58 added v8.12.0 and removed v8.11.0 labels Oct 4, 2023
@ChrisHegarty
Copy link
Contributor

The GH issue that tracks adding Panama Vector API support, #101314

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.

7 participants