-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Add experimental SIMD implementation of B-tree to round down dates #11194
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
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
550422c
Add experimental SIMD implementation of B-tree to round down dates
ketanv3 8afaa1e
Use system properties in favor of feature flags to remove dependency …
ketanv3 e54ec1f
Merge remote-tracking branch 'origin/main' into simd/rounding
ketanv3 5501806
Removed Java 20 test sources to simplify builds
ketanv3 2cb97a5
Remove the use of forbidden APIs in unit-tests
ketanv3 1e455aa
Migrate to the recommended usage for custom test task classpath
ketanv3 323c8c7
Merge remote-tracking branch 'origin/main' into simd/rounding
ketanv3 2969070
Switch benchmarks module to multi-release one
reta 2286af1
Add JMH annotation processing for JDK-20+ sources
reta b7cb42a
Make JMH annotations consistent across sources
ketanv3 2e82d0a
Improve execution of Roundable unit-tests
ketanv3 0edb311
Merge remote-tracking branch 'origin/main' into simd/rounding
ketanv3 ef85c47
Revert "Improve execution of Roundable unit-tests"
ketanv3 42eac9a
Add 'forced' as a possible feature flag value to simplify the executi…
ketanv3 88b45a3
Merge branch 'main' into simd/rounding
andrross File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
benchmarks/src/main/java/org/opensearch/common/round/RoundableSupplier.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * The OpenSearch Contributors require contributions made to | ||
| * this file be licensed under the Apache-2.0 license or a | ||
| * compatible open source license. | ||
| */ | ||
|
|
||
| package org.opensearch.common.round; | ||
|
|
||
| import java.util.function.Supplier; | ||
|
|
||
| public class RoundableSupplier implements Supplier<Roundable> { | ||
| private final Supplier<Roundable> delegate; | ||
|
|
||
| RoundableSupplier(String type, long[] values, int size) throws ClassNotFoundException { | ||
| switch (type) { | ||
| case "binary": | ||
| delegate = () -> new BinarySearcher(values, size); | ||
| break; | ||
| case "linear": | ||
| delegate = () -> new BidirectionalLinearSearcher(values, size); | ||
| break; | ||
| case "btree": | ||
| throw new ClassNotFoundException("BtreeSearcher is not supported below JDK 20"); | ||
| default: | ||
| throw new IllegalArgumentException("invalid type: " + type); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Roundable get() { | ||
| return delegate.get(); | ||
| } | ||
| } |
36 changes: 36 additions & 0 deletions
36
benchmarks/src/main/java20/org/opensearch/common/round/RoundableSupplier.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * The OpenSearch Contributors require contributions made to | ||
| * this file be licensed under the Apache-2.0 license or a | ||
| * compatible open source license. | ||
| */ | ||
|
|
||
| package org.opensearch.common.round; | ||
|
|
||
| import java.util.function.Supplier; | ||
|
|
||
| public class RoundableSupplier implements Supplier<Roundable> { | ||
| private final Supplier<Roundable> delegate; | ||
|
|
||
| RoundableSupplier(String type, long[] values, int size) { | ||
| switch (type) { | ||
| case "binary": | ||
| delegate = () -> new BinarySearcher(values, size); | ||
| break; | ||
| case "linear": | ||
| delegate = () -> new BidirectionalLinearSearcher(values, size); | ||
| break; | ||
| case "btree": | ||
| delegate = () -> new BtreeSearcher(values, size); | ||
| break; | ||
| default: | ||
| throw new IllegalArgumentException("invalid type: " + type); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Roundable get() { | ||
| return delegate.get(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
libs/common/src/main/java20/org/opensearch/common/round/BtreeSearcher.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * The OpenSearch Contributors require contributions made to | ||
| * this file be licensed under the Apache-2.0 license or a | ||
| * compatible open source license. | ||
| */ | ||
|
|
||
| package org.opensearch.common.round; | ||
|
|
||
| import org.opensearch.common.annotation.InternalApi; | ||
|
|
||
| import jdk.incubator.vector.LongVector; | ||
| import jdk.incubator.vector.Vector; | ||
| import jdk.incubator.vector.VectorOperators; | ||
| import jdk.incubator.vector.VectorSpecies; | ||
|
|
||
| /** | ||
| * It uses vectorized B-tree search to find the round-down point. | ||
| * | ||
| * @opensearch.internal | ||
| */ | ||
| @InternalApi | ||
| class BtreeSearcher implements Roundable { | ||
reta marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private static final VectorSpecies<Long> LONG_VECTOR_SPECIES = LongVector.SPECIES_PREFERRED; | ||
| private static final int LANES = LONG_VECTOR_SPECIES.length(); | ||
| private static final int SHIFT = log2(LANES); | ||
|
|
||
| private final long[] values; | ||
| private final long minValue; | ||
|
|
||
| BtreeSearcher(long[] values, int size) { | ||
| if (size <= 0) { | ||
| throw new IllegalArgumentException("at least one value must be present"); | ||
| } | ||
|
|
||
| int blocks = (size + LANES - 1) / LANES; // number of blocks | ||
| int length = 1 + blocks * LANES; // size of the backing array (1-indexed) | ||
|
|
||
| this.minValue = values[0]; | ||
| this.values = new long[length]; | ||
| build(values, 0, size, this.values, 1); | ||
| } | ||
|
|
||
| /** | ||
| * Builds the B-tree memory layout. | ||
| * It builds the tree recursively, following an in-order traversal. | ||
| * | ||
| * <p> | ||
| * Each block stores 'lanes' values at indices {@code i, i + 1, ..., i + lanes - 1} where {@code i} is the | ||
| * starting offset. The starting offset of the root block is 1. The branching factor is (1 + lanes) so each | ||
| * block can have these many children. Given the starting offset {@code i} of a block, the starting offset | ||
| * of its k-th child (ranging from {@code 0, 1, ..., k}) can be computed as {@code i + ((i + k) << shift)}. | ||
| * | ||
| * @param src is the sorted input array | ||
| * @param i is the index in the input array to read the value from | ||
| * @param size the number of values in the input array | ||
| * @param dst is the output array | ||
| * @param j is the index in the output array to write the value to | ||
| * @return the next index 'i' | ||
| */ | ||
| private static int build(long[] src, int i, int size, long[] dst, int j) { | ||
| if (j < dst.length) { | ||
| for (int k = 0; k < LANES; k++) { | ||
| i = build(src, i, size, dst, j + ((j + k) << SHIFT)); | ||
|
|
||
| // Fills the B-tree as a complete tree, i.e., all levels are completely filled, | ||
| // except the last level which is filled from left to right. | ||
| // The trick is to fill the destination array between indices 1...size (inclusive / 1-indexed) | ||
| // and pad the remaining array with +infinity. | ||
| dst[j + k] = (j + k <= size) ? src[i++] : Long.MAX_VALUE; | ||
| } | ||
| i = build(src, i, size, dst, j + ((j + LANES) << SHIFT)); | ||
| } | ||
| return i; | ||
| } | ||
|
|
||
| @Override | ||
| public long floor(long key) { | ||
| Vector<Long> keyVector = LongVector.broadcast(LONG_VECTOR_SPECIES, key); | ||
| int i = 1, result = 1; | ||
|
|
||
| while (i < values.length) { | ||
| Vector<Long> valuesVector = LongVector.fromArray(LONG_VECTOR_SPECIES, values, i); | ||
| int j = i + valuesVector.compare(VectorOperators.GT, keyVector).firstTrue(); | ||
| result = (j > i) ? j : result; | ||
| i += (j << SHIFT); | ||
| } | ||
|
|
||
| assert result > 1 : "key must be greater than or equal to " + minValue; | ||
| return values[result - 1]; | ||
| } | ||
|
|
||
| private static int log2(int num) { | ||
| if ((num <= 0) || ((num & (num - 1)) != 0)) { | ||
| throw new IllegalArgumentException(num + " is not a positive power of 2"); | ||
| } | ||
| return 32 - Integer.numberOfLeadingZeros(num - 1); | ||
| } | ||
| } | ||
57 changes: 57 additions & 0 deletions
57
libs/common/src/main/java20/org/opensearch/common/round/RoundableFactory.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * The OpenSearch Contributors require contributions made to | ||
| * this file be licensed under the Apache-2.0 license or a | ||
| * compatible open source license. | ||
| */ | ||
|
|
||
| package org.opensearch.common.round; | ||
|
|
||
| import org.opensearch.common.annotation.InternalApi; | ||
|
|
||
| import jdk.incubator.vector.LongVector; | ||
|
|
||
| /** | ||
| * Factory class to create and return the fastest implementation of {@link Roundable}. | ||
| * | ||
| * @opensearch.internal | ||
| */ | ||
| @InternalApi | ||
| public final class RoundableFactory { | ||
ketanv3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * The maximum limit up to which linear search is used, otherwise binary or B-tree search is used. | ||
| * This is because linear search is much faster on small arrays. | ||
| * Benchmark results: <a href="https://github.com/opensearch-project/OpenSearch/pull/9727">PR #9727</a> | ||
| */ | ||
| private static final int LINEAR_SEARCH_MAX_SIZE = 64; | ||
|
|
||
| /** | ||
| * Indicates whether the vectorized (SIMD) B-tree search implementation is to be used. | ||
| * It is true when either: | ||
| * 1. The feature flag is set to "forced", or | ||
| * 2. The platform has a minimum of 4 long vector lanes and the feature flag is set to "true". | ||
| */ | ||
| private static final boolean USE_BTREE_SEARCHER; | ||
|
|
||
| static { | ||
| String simdRoundingFeatureFlag = System.getProperty("opensearch.experimental.feature.simd.rounding.enabled"); | ||
reta marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| USE_BTREE_SEARCHER = "forced".equalsIgnoreCase(simdRoundingFeatureFlag) | ||
| || (LongVector.SPECIES_PREFERRED.length() >= 4 && "true".equalsIgnoreCase(simdRoundingFeatureFlag)); | ||
| } | ||
|
|
||
| private RoundableFactory() {} | ||
|
|
||
| /** | ||
| * Creates and returns the fastest implementation of {@link Roundable}. | ||
| */ | ||
| public static Roundable create(long[] values, int size) { | ||
| if (size <= LINEAR_SEARCH_MAX_SIZE) { | ||
| return new BidirectionalLinearSearcher(values, size); | ||
| } else if (USE_BTREE_SEARCHER) { | ||
| return new BtreeSearcher(values, size); | ||
| } else { | ||
| return new BinarySearcher(values, size); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.