4141import org .openjdk .jmh .annotations .Setup ;
4242import org .openjdk .jmh .annotations .State ;
4343import org .openjdk .jmh .annotations .TearDown ;
44+ import org .openjdk .jmh .annotations .Threads ;
4445import org .openjdk .jmh .annotations .Warmup ;
4546import org .openjdk .jmh .infra .Blackhole ;
4647import org .openjdk .jmh .profile .AsyncProfiler ;
5253import java .io .IOException ;
5354import java .nio .file .Files ;
5455import java .util .Random ;
56+ import java .util .concurrent .ExecutorService ;
57+ import java .util .concurrent .Executors ;
5558import java .util .concurrent .TimeUnit ;
5659
5760/**
98101 */
99102@ BenchmarkMode (Mode .SampleTime )
100103@ OutputTimeUnit (TimeUnit .MILLISECONDS )
101- @ State (Scope .Thread )
104+ @ State (Scope .Benchmark )
102105@ Fork (1 )
103- @ Warmup (iterations = 10 )
104- @ Measurement (iterations = 10 )
106+ @ Threads (1 )
107+ @ Warmup (iterations = 3 )
108+ @ Measurement (iterations = 5 )
105109public class DateFieldMapperDocValuesSkipperBenchmark {
106110
107111 public static void main (String [] args ) throws RunnerException {
@@ -112,18 +116,21 @@ public static void main(String[] args) throws RunnerException {
112116 new Runner (options ).run ();
113117 }
114118
115- @ Param ("1000000 " )
119+ @ Param ("1343120 " )
116120 private int numberOfDocuments ;
117121
118- @ Param ("10000" )
122+ @ Param ({ "1340" , "121300" } )
119123 private int batchSize ;
120124
121125 @ Param ("1000" )
122126 private int timestampIncrementMillis ;
123127
124- @ Param ({ "0.2 " })
128+ @ Param ({ "0.01" , "0.2" , "0.8 " })
125129 private double timestampRangeFraction ;
126130
131+ @ Param ({ "7390" , "398470" })
132+ private int commitEvery ;
133+
127134 @ Param ("42" )
128135 private int seed ;
129136
@@ -133,10 +140,9 @@ public static void main(String[] args) throws RunnerException {
133140
134141 private static final Sort QUERY_SORT = new Sort (new SortedNumericSortField (TIMESTAMP_FIELD , SortField .Type .LONG , true ));
135142
136- private Directory tempDirectoryWithoutDocValuesSkipper ;
137- private Directory tempDirectoryWithDocValuesSkipper ;
138143 private IndexSearcher indexSearcherWithoutDocValuesSkipper ;
139144 private IndexSearcher indexSearcherWithDocValuesSkipper ;
145+ private ExecutorService executorService ;
140146
141147 /**
142148 * Sets up the benchmark by creating Lucene indexes with and without doc values skipper.
@@ -145,22 +151,26 @@ public static void main(String[] args) throws RunnerException {
145151 */
146152 @ Setup (Level .Trial )
147153 public void setup () throws IOException {
148- tempDirectoryWithoutDocValuesSkipper = FSDirectory .open (Files .createTempDirectory ("temp1-" ));
149- tempDirectoryWithDocValuesSkipper = FSDirectory .open (Files .createTempDirectory ("temp2-" ));
154+ executorService = Executors .newSingleThreadExecutor ();
155+ Directory tempDirectoryWithoutDocValuesSkipper = FSDirectory .open (Files .createTempDirectory ("temp1-" ));
156+ Directory tempDirectoryWithDocValuesSkipper = FSDirectory .open (Files .createTempDirectory ("temp2-" ));
150157
151- indexSearcherWithoutDocValuesSkipper = createIndex (tempDirectoryWithoutDocValuesSkipper , false );
152- indexSearcherWithDocValuesSkipper = createIndex (tempDirectoryWithDocValuesSkipper , true );
158+ indexSearcherWithoutDocValuesSkipper = createIndex (tempDirectoryWithoutDocValuesSkipper , false , commitEvery );
159+ indexSearcherWithDocValuesSkipper = createIndex (tempDirectoryWithDocValuesSkipper , true , commitEvery );
153160 }
154161
155162 /**
156- * Creates an index with a specified sorting order and document structure.
163+ * Creates an {@link IndexSearcher} from a newly created {@link IndexWriter}. Documents
164+ * are added to the index and committed in batches of a specified size to generate multiple segments.
157165 *
158- * @param directory The Lucene directory to store the index.
159- * @param withDocValuesSkipper Whether to use a sparse doc values index.
160- * @return An IndexSearcher instance for querying the created index.
161- * @throws IOException if an error occurs during index writing.
166+ * @param directory the Lucene {@link Directory} where the index will be written
167+ * @param withDocValuesSkipper indicates whether certain fields should skip doc values
168+ * @param commitEvery the number of documents after which to force a commit
169+ * @return an {@link IndexSearcher} that can be used to query the newly created index
170+ * @throws IOException if an I/O error occurs during index writing or reading
162171 */
163- private IndexSearcher createIndex (final Directory directory , boolean withDocValuesSkipper ) throws IOException {
172+ private IndexSearcher createIndex (final Directory directory , final boolean withDocValuesSkipper , final int commitEvery )
173+ throws IOException {
164174 final IndexWriterConfig config = new IndexWriterConfig (new StandardAnalyzer ());
165175 config .setIndexSort (
166176 new Sort (
@@ -169,19 +179,29 @@ private IndexSearcher createIndex(final Directory directory, boolean withDocValu
169179 )
170180 );
171181
172- final IndexWriter indexWriter = new IndexWriter (directory , config );
173182 final Random random = new Random (seed );
183+ try (IndexWriter indexWriter = new IndexWriter (directory , config )) {
184+ int docCountSinceLastCommit = 0 ;
185+ for (int i = 0 ; i < numberOfDocuments ; i ++) {
186+ final Document doc = new Document ();
187+ addFieldsToDocument (doc , i , withDocValuesSkipper , random );
188+ indexWriter .addDocument (doc );
189+ docCountSinceLastCommit ++;
174190
175- for (int i = 0 ; i < numberOfDocuments ; i ++) {
176- final Document doc = new Document ();
177- addFieldsToDocument (doc , i , withDocValuesSkipper , random );
178- indexWriter .addDocument (doc );
179- }
191+ // NOTE: make sure we have multiple Lucene segments
192+ if (docCountSinceLastCommit >= commitEvery ) {
193+ indexWriter .commit ();
194+ docCountSinceLastCommit = 0 ;
195+ }
196+ }
180197
181- indexWriter .commit ();
182- final DirectoryReader reader = DirectoryReader .open (indexWriter );
183- indexWriter .close ();
184- return new IndexSearcher (reader );
198+ indexWriter .commit ();
199+ final DirectoryReader reader = DirectoryReader .open (indexWriter );
200+ // NOTE: internally Elasticsearch runs multiple search threads concurrently, (at least) one per Lucene segment.
201+ // Here we simplify the benchmark making sure we have a single-threaded search execution using a single thread
202+ // executor Service.
203+ return new IndexSearcher (reader , executorService );
204+ }
185205 }
186206
187207 private void addFieldsToDocument (final Document doc , int docIndex , boolean withDocValuesSkipper , final Random random ) {
@@ -243,10 +263,17 @@ private long rangeQuery(final IndexSearcher searcher, long rangeStartTimestamp,
243263 }
244264
245265 @ TearDown (Level .Trial )
246- public void tearDown () throws IOException {
247- indexSearcherWithoutDocValuesSkipper .getIndexReader ().close ();
248- indexSearcherWithDocValuesSkipper .getIndexReader ().close ();
249- tempDirectoryWithoutDocValuesSkipper .close ();
250- tempDirectoryWithDocValuesSkipper .close ();
266+ public void tearDown () {
267+ if (executorService != null ) {
268+ executorService .shutdown ();
269+ try {
270+ if (executorService .awaitTermination (30 , TimeUnit .SECONDS ) == false ) {
271+ executorService .shutdownNow ();
272+ }
273+ } catch (InterruptedException e ) {
274+ executorService .shutdownNow ();
275+ Thread .currentThread ().interrupt ();
276+ }
277+ }
251278 }
252279}
0 commit comments