Skip to content

Commit

Permalink
Refactor common parts from the Rounding class into a separate 'round'…
Browse files Browse the repository at this point in the history
… package

Signed-off-by: Ketan Verma <ketan9495@gmail.com>
  • Loading branch information
ketanv3 committed Oct 31, 2023
1 parent a2febe9 commit 7cf6fc7
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 147 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Add telemetry tracer/metric enable flag and integ test. ([#10395](https://github.com/opensearch-project/OpenSearch/pull/10395))
- Add instrumentation for indexing in transport bulk action and transport shard bulk action. ([#10273](https://github.com/opensearch-project/OpenSearch/pull/10273))
- [BUG] Disable sort optimization for HALF_FLOAT ([#10999](https://github.com/opensearch-project/OpenSearch/pull/10999))
- Refactor common parts from the Rounding class into a separate 'round' package ([#11023](https://github.com/opensearch-project/OpenSearch/issues/11023))

### Deprecated

Expand All @@ -141,4 +142,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Security

[Unreleased 3.0]: https://github.com/opensearch-project/OpenSearch/compare/2.x...HEAD
[Unreleased 2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.12...2.x
[Unreleased 2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.12...2.x
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* compatible open source license.
*/

package org.opensearch.common;
package org.opensearch.common.round;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
Expand All @@ -27,13 +27,13 @@
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 1, time = 1)
@BenchmarkMode(Mode.Throughput)
public class ArrayRoundingBenchmark {
public class RoundableBenchmark {

@Benchmark
public void round(Blackhole bh, Options opts) {
Rounding.Prepared rounding = opts.supplier.get();
public void floor(Blackhole bh, Options opts) {
Roundable roundable = opts.supplier.get();
for (long key : opts.queries) {
bh.consume(rounding.round(key));
bh.consume(roundable.floor(key));
}
}

Expand Down Expand Up @@ -90,7 +90,7 @@ public static class Options {
public String distribution;

public long[] queries;
public Supplier<Rounding.Prepared> supplier;
public Supplier<Roundable> supplier;

@Setup
public void setup() {
Expand Down Expand Up @@ -130,10 +130,10 @@ public void setup() {

switch (type) {
case "binary":
supplier = () -> new Rounding.BinarySearchArrayRounding(values, size, null);
supplier = () -> new BinarySearcher(values, size);
break;
case "linear":
supplier = () -> new Rounding.BidirectionalLinearSearchArrayRounding(values, size, null);
supplier = () -> new BidirectionalLinearSearcher(values, size);
break;
default:
throw new IllegalArgumentException("invalid type: " + type);
Expand Down
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;

/**
* It uses linear search on a sorted array of pre-computed round-down points.
* For small inputs (&le; 64 elements), this can be much faster than binary search as it avoids the penalty of
* branch mispredictions and pipeline stalls, and accesses memory sequentially.
*
* <p>
* It uses "meet in the middle" linear search to avoid the worst case scenario when the desired element is present
* at either side of the array. This is helpful for time-series data where velocity increases over time, so more
* documents are likely to find a greater timestamp which is likely to be present on the right end of the array.
*
* @opensearch.internal
*/
@InternalApi
class BidirectionalLinearSearcher implements Roundable {
private final long[] ascending;
private final long[] descending;

public BidirectionalLinearSearcher(long[] values, int size) {
assert size > 0 : "at least one value must be present";

int len = (size + 1) >>> 1; // rounded-up to handle odd number of values
ascending = new long[len];
descending = new long[len];

for (int i = 0; i < len; i++) {
ascending[i] = values[i];
descending[i] = values[size - i - 1];
}
}

@Override
public long floor(long key) {
int i = 0;
for (; i < ascending.length; i++) {
if (descending[i] <= key) {
return descending[i];
}
if (ascending[i] > key) {
assert i > 0 : "key must be greater than or equal to " + ascending[0];
return ascending[i - 1];
}
}
return ascending[i - 1];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 java.util.Arrays;

/**
* It uses binary search on a sorted array of pre-computed round-down points.
*
* @opensearch.internal
*/
@InternalApi
class BinarySearcher implements Roundable {
private final long[] values;
private final int size;

public BinarySearcher(long[] values, int size) {
assert size > 0 : "at least one value must be present";

this.values = values;
this.size = size;
}

@Override
public long floor(long key) {
int idx = Arrays.binarySearch(values, 0, size, key);
assert idx != -1 : "key must be greater than or equal to " + values[0];
if (idx < 0) {
idx = -2 - idx;
}
return values[idx];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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;

/**
* Interface to round-off values.
*
* @opensearch.internal
*/
@InternalApi
@FunctionalInterface
public interface Roundable {
/**
* Returns the greatest lower bound of the given key.
* In other words, it returns the largest value such that {@code value <= key}.
* @param key to floor
* @return the floored value
*/
long floor(long key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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;

/**
* Factory class to create and return the fastest implementation of {@link Roundable}.
*/
public class RoundableFactory {
/**
* The maximum limit up to which linear search is used, otherwise binary 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;

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 {
return new BinarySearcher(values, size);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

/**
* Contains classes to round-off values.
*/
package org.opensearch.common.round;
Loading

0 comments on commit 7cf6fc7

Please sign in to comment.