apache / apache/lucene

Optimize readInts24 performance for DocIdsWriter

Open
#12,527 11 comments 0 reactions 0 assignees View on GitHub
type:enhancement
Dominant language
Java
Stars
3.6k
Forks
1.4k
Avg merge
2d 11h
Merged PRs (30d)
88

Description

### Description

While recently [working on numeric range queries](https://github.com/opensearch-project/OpenSearch/issues/9541), I noticed readInts24 to be consuming significant CPU cycles. When I looked into the code, I noticed [multiple consecutive invocations of readLong](https://github.com/apache/lucene/blob/main/lucene/core/src/java/org/apache/lucene/util/bkd/DocIdsWriter.java#L330).

Initially, it seems that the overhead from multiple syscalls should not be as much, but I tried quick patch by reading all the longs together and it seemed to help. Sharing the patch and numbers below (nyc_taxis range query):

```
diff --git a/lucene/core/src/java/org/apache/lucene/util/bkd/DocIdsWriter.java b/lucene/core/src/java/org/apache/lucene/util/bkd/DocIdsWriter.java
index 40db4c0069d..40ee7a1c968 100644
--- a/lucene/core/src/java/org/apache/lucene/util/bkd/DocIdsWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/util/bkd/DocIdsWriter.java
@@ -325,11 +325,14 @@ final class DocIdsWriter {

private static void readInts24(IndexInput in, int count, IntersectVisitor visitor)
throws IOException {
+ long[] scratchLong = new long[(count/8) * 3];
+ in.readLongs(scratchLong, 0, (count/8) * 3);
int i;
for (i = 0; i < count - 7; i += 8) {
- long l1 = in.readLong();
- long l2 = in.readLong();
- long l3 = in.readLong();
+ int li = (i/8) * 3;
+ long l1 = scratchLong[li];
+ long l2 = scratchLong[li+1];
+ long l3 = scratchLong[li+2];
visitor.visit((int) (l1 >>> 40));
visitor.visit((int) (l1 >>> 16) & 0xffffff);
visitor.visit((int) (((l1 & 0xffff) << 8) | (l2 >>> 56)));
```

Without this change:

```
| Max Throughput | range | 0.71 | ops/s |
| 50th percentile latency | range | 245.533 | ms |
| 90th percentile latency | range | 248.005 | ms |
| 99th percentile latency | range | 254.824 | ms |
| 100th percentile latency | range | 256.902 | ms |
| 50th percentile service time | range | 243.585 | ms |
| 90th percentile service time | range | 246.178 | ms |
| 99th percentile service time | range | 252.672 | ms |
| 100th percentile service time | range | 255.072 | ms |
| error rate | range | 0 | % |

```

With this change:

```
| Median Throughput | range | 0.7 | ops/s |
| Max Throughput | range | 0.71 | ops/s |
| 50th percentile latency | range | 207.554 | ms |
| 90th percentile latency | range | 209.392 | ms |
| 99th percentile latency | range | 213.157 | ms |
| 100th percentile latency | range | 219.398 | ms |
| 50th percentile service time | range | 205.421 | ms |
| 90th percentile service time | range | 207.361 | ms |
| 99th percentile service time | range | 211.164 | ms |
| 100th percentile service time | range | 217.787 | ms |
| error rate | range | 0 | % |
```

Contributor guide

Open the contributing guide

Research direction

Start in lucene/core/src/java/org/apache/lucene/util/bkd/DocIdsWriter.java at readInts24 and review how consecutive readLong calls process the encoded doc IDs. Reproduce the nyc_taxis numeric range-query comparison described in the issue, then verify that the optimized path preserves decoding behavior and improves latency without errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
performance, search
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.