Skip to content

Commit f066228

Browse files
authored
Merge branch 'main' into lucene/sorted-numeric-fixed-simd
2 parents b8f15a9 + 72182e3 commit f066228

24 files changed

Lines changed: 329 additions & 75 deletions

dev-tools/doap/lucene.rdf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@
6767
</maintainer>
6868

6969
<!-- NOTE: please insert releases in numeric order, NOT chronologically. -->
70+
<release>
71+
<Version>
72+
<name>lucene-10.5.0</name>
73+
<created>2026-06-25</created>
74+
<revision>10.5.0</revision>
75+
</Version>
76+
</release>
7077
<release>
7178
<Version>
7279
<name>lucene-10.4.0</name>

gradle/libs.versions.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ javacc = "7.0.13"
2727
# analysis/ tokenizer grammars
2828
jflex = "1.9.1"
2929
# build infra only (diff of changes from google java format tasks)
30-
jgit = "7.6.0.202603022253-r"
30+
jgit = "7.7.0.202606012155-r"
3131
# benchmark-jmh/ microbenchmarks
3232
jmh = "1.37"
3333
# spatial-extras/ support
@@ -40,7 +40,7 @@ minGradle = "9.6.0"
4040
# @keep This is the minimum required Java version.
4141
minJava = "25"
4242
# analysis/morfologik polish support
43-
morfologik = "2.1.9"
43+
morfologik = "2.2.0"
4444
# analysis/morfologik ukrainian support
4545
morfologik-ukrainian = "4.9.1"
4646
# benchmark/ html parsing
@@ -59,7 +59,7 @@ spatial4j = "0.8"
5959
# benchmark/ XML parsing
6060
xerces = "2.12.2"
6161
# external datasets decompression
62-
zstd = "1.5.7-10"
62+
zstd = "1.5.7-11"
6363

6464
[libraries]
6565
antlr-core = { module = "org.antlr:antlr4", version.ref = "antlr" }
@@ -110,6 +110,6 @@ errorprone = "net.ltgt.errorprone:5.1.0"
110110
forbiddenapis = "de.thetaphi.forbiddenapis:3.10"
111111
jacocolog = "org.barfuin.gradle.jacocolog:4.0.2"
112112
randomizedtesting = "com.carrotsearch.gradle.randomizedtesting:0.0.6"
113-
spotless = "com.diffplug.spotless:8.6.0"
113+
spotless = "com.diffplug.spotless:8.7.0"
114114
undercouch-download = "de.undercouch.download:5.7.0"
115115
versionCatalogUpdate = "nl.littlerobots.version-catalog-update:1.1.0"

lucene/CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ Improvements
291291

292292
Optimizations
293293
---------------------
294+
* GITHUG#16280: Single-pass writeString fast path for short strings in ByteBuffersDataOutput (neoremind)
294295
* GITHUB#16283: Use Panama Vector API to SIMD-evaluate fixed-cardinality sorted numeric range
295296
queries in rangeIntoBitSet. (Costin Leau)
296297

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

lucene/backward-codecs/src/test/org/apache/lucene/backward_index/versions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@
2626
10.3.1
2727
10.3.2
2828
10.4.0
29+
10.5.0
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.lucene.benchmark.jmh;
18+
19+
import java.nio.charset.StandardCharsets;
20+
import java.util.Random;
21+
import java.util.concurrent.TimeUnit;
22+
import java.util.function.Supplier;
23+
import org.apache.lucene.store.ByteBuffersDataOutput;
24+
import org.openjdk.jmh.annotations.Benchmark;
25+
import org.openjdk.jmh.annotations.BenchmarkMode;
26+
import org.openjdk.jmh.annotations.Fork;
27+
import org.openjdk.jmh.annotations.Level;
28+
import org.openjdk.jmh.annotations.Measurement;
29+
import org.openjdk.jmh.annotations.Mode;
30+
import org.openjdk.jmh.annotations.OutputTimeUnit;
31+
import org.openjdk.jmh.annotations.Param;
32+
import org.openjdk.jmh.annotations.Scope;
33+
import org.openjdk.jmh.annotations.Setup;
34+
import org.openjdk.jmh.annotations.State;
35+
import org.openjdk.jmh.annotations.Warmup;
36+
import org.openjdk.jmh.infra.Blackhole;
37+
38+
@BenchmarkMode(Mode.Throughput)
39+
@OutputTimeUnit(TimeUnit.SECONDS)
40+
@State(Scope.Benchmark)
41+
@Warmup(iterations = 5, time = 3)
42+
@Measurement(iterations = 5, time = 3)
43+
@Fork(
44+
value = 3,
45+
jvmArgsAppend = {"-Xmx1g", "-Xms1g", "-XX:+AlwaysPreTouch"})
46+
public class ByteBuffersDataOutputWriteStringBenchmark {
47+
48+
private static final int STRING_POOL_SIZE = 8192;
49+
50+
@Param({
51+
"ascii_1",
52+
"ascii_10",
53+
"ascii_20",
54+
"ascii_30",
55+
"ascii_40",
56+
"ascii_medium",
57+
"ascii_long",
58+
"ascii_vlarge",
59+
"cjk_1",
60+
"cjk_10",
61+
"cjk_20",
62+
"cjk_30",
63+
"cjk_40",
64+
"cjk_medium",
65+
"cjk_long",
66+
"cjk_vlarge",
67+
"latin_ext_1",
68+
"latin_ext_10",
69+
"latin_ext_20",
70+
"latin_ext_30",
71+
"latin_ext_40",
72+
"latin_ext_medium",
73+
"latin_ext_long",
74+
"latin_ext_vlarge",
75+
"mixed"
76+
})
77+
public String stringType;
78+
79+
/** Target bytes to write per invocation. */
80+
@Param({"81920", "491520", "2097152"})
81+
public int targetBytes;
82+
83+
/** Pre-generated strings to write. */
84+
private String[] testStrings;
85+
86+
/** Number of strings to write per invocation to reach targetBytes total output. */
87+
private int stringsPerInvocation;
88+
89+
private ByteBuffersDataOutput reusableOutput;
90+
91+
@Setup(Level.Trial)
92+
public void setup() {
93+
Random random = new Random(42);
94+
testStrings = new String[STRING_POOL_SIZE];
95+
96+
Supplier<String> generator =
97+
switch (stringType) {
98+
case "ascii_1" -> () -> randomAscii(random, 1);
99+
case "ascii_10" -> () -> randomAscii(random, 8 + random.nextInt(5));
100+
case "ascii_20" -> () -> randomAscii(random, 18 + random.nextInt(5));
101+
case "ascii_30" -> () -> randomAscii(random, 28 + random.nextInt(5));
102+
case "ascii_40" -> () -> randomAscii(random, 38 + random.nextInt(5));
103+
case "ascii_medium" -> () -> randomAscii(random, 50 + random.nextInt(100));
104+
case "ascii_long" -> () -> randomAscii(random, 900 + random.nextInt(250));
105+
case "ascii_vlarge" -> () -> randomAscii(random, 7000 + random.nextInt(2400));
106+
case "cjk_1" -> () -> randomCjk(random, 1);
107+
case "cjk_10" -> () -> randomCjk(random, 8 + random.nextInt(5));
108+
case "cjk_20" -> () -> randomCjk(random, 18 + random.nextInt(5));
109+
case "cjk_30" -> () -> randomCjk(random, 28 + random.nextInt(5));
110+
case "cjk_40" -> () -> randomCjk(random, 38 + random.nextInt(5));
111+
case "cjk_medium" -> () -> randomCjk(random, 50 + random.nextInt(100));
112+
case "cjk_long" -> () -> randomCjk(random, 400 + random.nextInt(200));
113+
case "cjk_vlarge" -> () -> randomCjk(random, 5500 + random.nextInt(1000));
114+
case "latin_ext_1" -> () -> randomLatinExtended(random, 1);
115+
case "latin_ext_10" -> () -> randomLatinExtended(random, 8 + random.nextInt(5));
116+
case "latin_ext_20" -> () -> randomLatinExtended(random, 18 + random.nextInt(5));
117+
case "latin_ext_30" -> () -> randomLatinExtended(random, 28 + random.nextInt(5));
118+
case "latin_ext_40" -> () -> randomLatinExtended(random, 38 + random.nextInt(5));
119+
case "latin_ext_medium" -> () -> randomLatinExtended(random, 50 + random.nextInt(100));
120+
case "latin_ext_long" -> () -> randomLatinExtended(random, 400 + random.nextInt(200));
121+
case "latin_ext_vlarge" -> () -> randomLatinExtended(random, 5500 + random.nextInt(1000));
122+
case "mixed" ->
123+
() -> {
124+
// Varying lengths
125+
int roll = random.nextInt(100);
126+
if (roll < 50) {
127+
return randomAscii(random, 3 + random.nextInt(30));
128+
} else if (roll < 65) {
129+
return randomAscii(random, 50 + random.nextInt(100));
130+
} else if (roll < 75) {
131+
return randomAscii(random, 500 + random.nextInt(500));
132+
} else if (roll < 85) {
133+
return randomCjk(random, 5 + random.nextInt(20));
134+
} else if (roll < 95) {
135+
return randomLatinExtended(random, 20 + random.nextInt(60));
136+
} else {
137+
return randomCjk(random, 200 + random.nextInt(300));
138+
}
139+
};
140+
default -> throw new IllegalArgumentException("Unknown stringType: " + stringType);
141+
};
142+
143+
long totalBytes = 0;
144+
for (int i = 0; i < STRING_POOL_SIZE; i++) {
145+
testStrings[i] = generator.get();
146+
totalBytes += testStrings[i].getBytes(StandardCharsets.UTF_8).length;
147+
}
148+
int avgBytesPerString = Math.max(1, (int) (totalBytes / STRING_POOL_SIZE));
149+
150+
stringsPerInvocation = targetBytes / avgBytesPerString;
151+
152+
reusableOutput = ByteBuffersDataOutput.newResettableInstance();
153+
}
154+
155+
private ByteBuffersDataOutput getOutput() {
156+
reusableOutput.reset();
157+
return reusableOutput;
158+
}
159+
160+
@Benchmark
161+
public void writeString(Blackhole bh) {
162+
ByteBuffersDataOutput output = getOutput();
163+
for (int i = 0; i < stringsPerInvocation; i++) {
164+
output.writeString(testStrings[i % STRING_POOL_SIZE]);
165+
}
166+
bh.consume(output.size());
167+
}
168+
169+
private String randomAscii(Random random, int length) {
170+
char[] chars = new char[length];
171+
for (int i = 0; i < length; i++) {
172+
chars[i] = (char) (32 + random.nextInt(95));
173+
}
174+
return new String(chars);
175+
}
176+
177+
/**
178+
* Generates realistic CJK text: ~90% CJK Unified Ideographs (3-byte UTF-8), ~9% ASCII digits
179+
* (1-byte), ~1% surrogate pairs (emoji, rare CJK-B characters, 4-byte UTF-8).
180+
*/
181+
private String randomCjk(Random random, int length) {
182+
char[] chars = new char[length + 1]; // +1 room for potential surrogate pair expansion
183+
int pos = 0;
184+
for (int i = 0; i < length && pos < chars.length - 1; i++) {
185+
int roll = random.nextInt(100);
186+
if (roll < 90) {
187+
// CJK Unified Ideographs: U+4E00–U+9FFF (3 bytes in UTF-8)
188+
chars[pos++] = (char) (0x4E00 + random.nextInt(0x9FFF - 0x4E00));
189+
} else if (roll < 99) {
190+
// ASCII digits
191+
chars[pos++] = (char) (0x30 + random.nextInt(10)); // 0-9
192+
} else {
193+
// Surrogate pair, 4 bytes UTF-8
194+
if (pos < chars.length - 1) {
195+
chars[pos++] = (char) (0xD800 + random.nextInt(0x400)); // high surrogate
196+
chars[pos++] = (char) (0xDC00 + random.nextInt(0x400)); // low surrogate
197+
} else {
198+
chars[pos++] = (char) (0x4E00 + random.nextInt(0x9FFF - 0x4E00));
199+
}
200+
}
201+
}
202+
return new String(chars, 0, pos);
203+
}
204+
205+
private String randomLatinExtended(Random random, int length) {
206+
char[] chars = new char[length];
207+
for (int i = 0; i < length; i++) {
208+
int roll = random.nextInt(100);
209+
if (roll < 80) {
210+
// 2-byte UTF-8
211+
chars[i] = (char) (0x0080 + random.nextInt(0x0700));
212+
} else if (roll < 95) {
213+
// ASCII
214+
chars[i] = (char) (0x41 + random.nextInt(26)); // A-Z
215+
} else {
216+
// 3-byte UTF-8
217+
chars[i] = (char) (0x2000 + random.nextInt(0xBFF));
218+
}
219+
}
220+
return new String(chars);
221+
}
222+
}

lucene/benchmark/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ def outputFileOption = buildOptions.addFileOption("standardOutput", "Path to the
6969

7070
tasks.register("run", JavaExec, {
7171
description = "Run a perf test (optional: -PtaskAlg=conf/your-algorithm-file -PmaxHeapSize=1G). Before" +
72-
" running this, you need to download the dataset the benchmark runs against (e.g., by getReuters" +
73-
" task). See dataset download tasks for more details."
72+
" running this, you need to download the dataset the benchmark runs against (e.g., by getReuters" +
73+
" task). See dataset download tasks for more details."
7474
group = "Utility launchers"
7575

7676
mainClass = 'org.apache.lucene.benchmark.byTask.Benchmark'

0 commit comments

Comments
 (0)