Skip to content

Commit 8785380

Browse files
IGNITE-17764 Fix performance drop on index find operation - Fixes #10269.
Signed-off-by: Aleksey Plekhanov <plehanov.alex@gmail.com> (cherry picked from commit 3b0b5ed)
1 parent 31324c5 commit 8785380

31 files changed

Lines changed: 264 additions & 152 deletions
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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+
18+
package org.apache.ignite.internal.benchmarks.jmh.tree;
19+
20+
import java.util.concurrent.ThreadLocalRandom;
21+
import java.util.concurrent.TimeUnit;
22+
import org.apache.ignite.IgniteCheckedException;
23+
import org.apache.ignite.IgniteDataStreamer;
24+
import org.apache.ignite.Ignition;
25+
import org.apache.ignite.cache.query.annotations.QuerySqlField;
26+
import org.apache.ignite.configuration.CacheConfiguration;
27+
import org.apache.ignite.configuration.IgniteConfiguration;
28+
import org.apache.ignite.internal.IgniteEx;
29+
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
30+
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
31+
import org.apache.ignite.internal.cache.query.index.sorted.IndexSearchRowImpl;
32+
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
33+
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
34+
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKeyFactory;
35+
import org.apache.ignite.internal.util.lang.GridCursor;
36+
import org.openjdk.jmh.annotations.Benchmark;
37+
import org.openjdk.jmh.annotations.BenchmarkMode;
38+
import org.openjdk.jmh.annotations.Fork;
39+
import org.openjdk.jmh.annotations.Level;
40+
import org.openjdk.jmh.annotations.Measurement;
41+
import org.openjdk.jmh.annotations.Mode;
42+
import org.openjdk.jmh.annotations.OutputTimeUnit;
43+
import org.openjdk.jmh.annotations.Scope;
44+
import org.openjdk.jmh.annotations.Setup;
45+
import org.openjdk.jmh.annotations.State;
46+
import org.openjdk.jmh.annotations.TearDown;
47+
import org.openjdk.jmh.annotations.Warmup;
48+
import org.openjdk.jmh.runner.Runner;
49+
import org.openjdk.jmh.runner.options.Options;
50+
import org.openjdk.jmh.runner.options.OptionsBuilder;
51+
52+
/**
53+
* Index find benchmark.
54+
*/
55+
@State(Scope.Benchmark)
56+
@Fork(1)
57+
@BenchmarkMode(Mode.Throughput)
58+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
59+
@Warmup(iterations = 3, time = 3)
60+
@Measurement(iterations = 5, time = 10)
61+
public class IndexFindBenchmark {
62+
/** Items count. */
63+
private static final int CNT = 1_000_000;
64+
65+
/** Items in each range. */
66+
private static final int RANGE = 1;
67+
68+
/** Cache name. */
69+
private static final String CACHE_NAME = "cache";
70+
71+
/** Ignite. */
72+
private IgniteEx ignite;
73+
74+
/** */
75+
InlineIndex idxId;
76+
77+
/** */
78+
InlineIndex idxName;
79+
80+
/** */
81+
InlineIndex idxSalary;
82+
83+
/** */
84+
@Benchmark
85+
public void findOneIndex() {
86+
int key = ThreadLocalRandom.current().nextInt(CNT - RANGE);
87+
88+
find(idxSalary, searchRowSalary(key), searchRowSalary(key + RANGE));
89+
}
90+
91+
/** */
92+
@Benchmark
93+
public void findThreeIndexes() {
94+
int key = ThreadLocalRandom.current().nextInt(CNT);
95+
96+
find(idxId, searchRowId(key), searchRowId(key + RANGE));
97+
find(idxName, searchRowName(key), searchRowName(key + RANGE));
98+
find(idxSalary, searchRowSalary(key), searchRowSalary(key + RANGE));
99+
}
100+
101+
/** */
102+
private static void find(InlineIndex idx, IndexRow lower, IndexRow upper) {
103+
try {
104+
GridCursor<IndexRow> cur = idx.find(lower, upper, true, false, 0, null);
105+
106+
int cnt = 0;
107+
108+
while (cur.next())
109+
cnt++;
110+
111+
assert cnt == RANGE;
112+
}
113+
catch (IgniteCheckedException e) {
114+
throw new AssertionError(e);
115+
}
116+
}
117+
118+
/** */
119+
private static IndexRow searchRow(Object key, IndexKeyType type) {
120+
IndexKey[] keys = new IndexKey[] { IndexKeyFactory.wrap(key, type, null, null), null };
121+
return new IndexSearchRowImpl(keys, null);
122+
}
123+
124+
/** */
125+
private static IndexRow searchRowId(int key) {
126+
return searchRow(key, IndexKeyType.INT);
127+
}
128+
129+
/** */
130+
private static IndexRow searchRowName(int key) {
131+
return searchRow("name" + String.format("%07d", key), IndexKeyType.STRING);
132+
}
133+
134+
/** */
135+
private static IndexRow searchRowSalary(int key) {
136+
return searchRow(key * 1_000d, IndexKeyType.DOUBLE);
137+
}
138+
139+
/**
140+
* Initiate Ignite and caches.
141+
*/
142+
@Setup(Level.Trial)
143+
public void setup() {
144+
ignite = (IgniteEx)Ignition.start(new IgniteConfiguration().setIgniteInstanceName("test"));
145+
146+
CacheConfiguration<Integer, Person> cfg = new CacheConfiguration<>(CACHE_NAME);
147+
cfg.setIndexedTypes(Integer.class, Person.class);
148+
149+
ignite.getOrCreateCache(cfg);
150+
151+
try (IgniteDataStreamer<Integer, Person> dataLdr = ignite.dataStreamer(CACHE_NAME)) {
152+
for (int i = 0; i < CNT; i++)
153+
dataLdr.addData(i, new Person(i, "name" + String.format("%07d", i), i * 1_000d));
154+
}
155+
156+
for (InlineIndex idx : ignite.context().indexProcessor().treeIndexes(CACHE_NAME, true)) {
157+
if (idx.name().contains("_ID_"))
158+
idxId = idx;
159+
else if (idx.name().contains("_NAME_"))
160+
idxName = idx;
161+
else if (idx.name().contains("_SALARY_"))
162+
idxSalary = idx;
163+
}
164+
}
165+
166+
/**
167+
* Stop Ignite instance.
168+
*/
169+
@TearDown
170+
public void tearDown() {
171+
ignite.close();
172+
}
173+
174+
/**
175+
* Run benchmarks.
176+
*
177+
* @param args Args.
178+
* @throws Exception Exception.
179+
*/
180+
public static void main(String[] args) throws Exception {
181+
final Options options = new OptionsBuilder()
182+
.include(IndexFindBenchmark.class.getSimpleName())
183+
.build();
184+
185+
new Runner(options).run();
186+
}
187+
188+
/** */
189+
private static class Person {
190+
/** */
191+
@QuerySqlField(index = true)
192+
private final int id;
193+
194+
/** */
195+
@QuerySqlField(index = true)
196+
private final String name;
197+
198+
/** */
199+
@QuerySqlField(index = true)
200+
private final double salary;
201+
202+
/** */
203+
private Person(int id, String name, double salary) {
204+
this.id = id;
205+
this.name = name;
206+
this.salary = salary;
207+
}
208+
}
209+
}

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexKeyType.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.Comparator;
3333
import org.apache.ignite.IgniteException;
3434
import org.apache.ignite.internal.processors.query.QueryUtils;
35-
import org.apache.ignite.internal.util.typedef.internal.A;
3635
import org.apache.ignite.internal.util.typedef.internal.U;
3736

3837
/**
@@ -187,8 +186,8 @@ public static IndexKeyType forCode(int code) {
187186
if (code == UNKNOWN.code)
188187
return UNKNOWN;
189188

190-
A.ensure(code >= 0 && code < keyTypesByCode.length,
191-
"code >= 0 && code < keyTypesByCode.length [code=" + code + ']');
189+
if (code < 0 || code >= keyTypesByCode.length)
190+
throw new IllegalArgumentException("Argument is invalid: " + code);
192191

193192
return keyTypesByCode[code];
194193
}

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRowCompartorImpl.java renamed to modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRowComparatorImpl.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,26 @@
3333
* 1. NULL is the least value.
3434
* 2. Comparison of different types is not supported.
3535
*/
36-
public class IndexRowCompartorImpl implements IndexRowComparator {
36+
public class IndexRowComparatorImpl implements IndexRowComparator {
3737
/** Key type settings for this index. */
3838
protected final IndexKeyTypeSettings keyTypeSettings;
3939

4040
/** */
41-
public IndexRowCompartorImpl(IndexKeyTypeSettings settings) {
41+
public IndexRowComparatorImpl(IndexKeyTypeSettings settings) {
4242
keyTypeSettings = settings;
4343
}
4444

4545
/** {@inheritDoc} */
4646
@Override public int compareKey(long pageAddr, int off, int maxSize, IndexKey key, InlineIndexKeyType type) {
47-
if (type.type() == IndexKeyType.UNKNOWN)
47+
IndexKeyType inlineKeyType = type.type();
48+
49+
if (inlineKeyType == IndexKeyType.UNKNOWN)
4850
return CANT_BE_COMPARE;
4951

5052
// Value can be set up by user in query with different data type. Don't compare uncomparable types in this comparator.
51-
if (isInlineComparable(type, key)) {
53+
if (inlineKeyType == key.type() || isInlineComparable(type, key)) {
5254
// If inlining of POJO is not supported then don't compare it here.
53-
if (type.type() != IndexKeyType.JAVA_OBJECT || keyTypeSettings.inlineObjSupported())
55+
if (inlineKeyType != IndexKeyType.JAVA_OBJECT || keyTypeSettings.inlineObjSupported())
5456
return type.compare(pageAddr, off, maxSize, key);
5557
else
5658
return CANT_BE_COMPARE;
@@ -88,7 +90,7 @@ private int compare(IndexKey lkey, IndexKey rkey) throws IgniteCheckedException
8890
else if (rkey == NullIndexKey.INSTANCE)
8991
return 1;
9092

91-
if (lkey.isComparableTo(rkey))
93+
if (lkey.type() == rkey.type() || lkey.isComparableTo(rkey))
9294
return lkey.compare(rkey);
9395
else if (rkey.isComparableTo(lkey))
9496
return -rkey.compare(lkey);

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/QueryIndexDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public QueryIndexDefinition(
8989
this.isAffinity = isAffinity;
9090
this.keyDefs = keyDefs;
9191
this.keyTypeSettings = keyTypeSettings;
92-
this.rowComparator = new IndexRowCompartorImpl(keyTypeSettings);
92+
this.rowComparator = new IndexRowComparatorImpl(keyTypeSettings);
9393
}
9494

9595
/** {@inheritDoc} */

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/QueryIndexRowHandler.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
package org.apache.ignite.internal.cache.query.index.sorted;
1919

2020
import java.util.ArrayList;
21-
import java.util.Collections;
2221
import java.util.LinkedHashMap;
2322
import java.util.List;
2423
import org.apache.ignite.IgniteCheckedException;
@@ -60,8 +59,8 @@ public QueryIndexRowHandler(
6059
List<InlineIndexKeyType> keyTypes,
6160
IndexKeyTypeSettings keyTypeSettings
6261
) {
63-
this.keyTypes = Collections.unmodifiableList(keyTypes);
64-
this.keyDefs = Collections.unmodifiableList(new ArrayList<>(keyDefs.values()));
62+
this.keyTypes = new ArrayList<>(keyTypes);
63+
this.keyDefs = new ArrayList<>(keyDefs.values());
6564

6665
props = new GridQueryProperty[keyDefs.size()];
6766
int propIdx = 0;

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexTree.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,7 @@ private boolean inlineObjectSupported(SortedIndexDefinition def, MetaPageInfo me
271271
int off = io.offset(idx);
272272

273273
List<IndexKeyDefinition> keyDefs = rowHnd.indexKeyDefinitions();
274-
275-
List<InlineIndexKeyType> keyTypes = rowHandler().inlineIndexKeyTypes();
274+
List<InlineIndexKeyType> keyTypes = rowHnd.inlineIndexKeyTypes();
276275

277276
for (keyIdx = 0; keyIdx < keyTypes.size(); keyIdx++) {
278277
try {
@@ -281,10 +280,6 @@ private boolean inlineObjectSupported(SortedIndexDefinition def, MetaPageInfo me
281280
if (row.key(keyIdx) == null)
282281
return 0;
283282

284-
// Other keys are not inlined. Should compare as rows.
285-
if (keyIdx >= keyTypes.size())
286-
break;
287-
288283
int maxSize = inlineSize - fieldOff;
289284

290285
InlineIndexKeyType keyType = keyTypes.get(keyIdx);

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/BooleanInlineIndexKeyType.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
2121
import org.apache.ignite.internal.cache.query.index.sorted.keys.BooleanIndexKey;
22+
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
2223
import org.apache.ignite.internal.cache.query.index.sorted.keys.NumericIndexKey;
2324
import org.apache.ignite.internal.pagemem.PageUtils;
2425

@@ -47,9 +48,9 @@ public BooleanInlineIndexKeyType() {
4748
}
4849

4950
/** {@inheritDoc} */
50-
@Override public int compareNumeric(NumericIndexKey key, long pageAddr, int off) {
51-
boolean bool1 = PageUtils.getByte(pageAddr, off + 1) != 0;
51+
@Override public int compare0(long pageAddr, int off, IndexKey key) {
52+
boolean val = PageUtils.getByte(pageAddr, off + 1) != 0;
5253

53-
return key.compareTo(bool1);
54+
return -Integer.signum(((NumericIndexKey)key).compareTo(val));
5455
}
5556
}

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/ByteInlineIndexKeyType.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
2121
import org.apache.ignite.internal.cache.query.index.sorted.keys.ByteIndexKey;
22+
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
2223
import org.apache.ignite.internal.cache.query.index.sorted.keys.NumericIndexKey;
2324
import org.apache.ignite.internal.pagemem.PageUtils;
2425

@@ -32,10 +33,10 @@ public ByteInlineIndexKeyType() {
3233
}
3334

3435
/** {@inheritDoc} */
35-
@Override public int compareNumeric(NumericIndexKey key, long pageAddr, int off) {
36-
byte byte1 = PageUtils.getByte(pageAddr, off + 1);
36+
@Override public int compare0(long pageAddr, int off, IndexKey key) {
37+
byte val = PageUtils.getByte(pageAddr, off + 1);
3738

38-
return key.compareTo(byte1);
39+
return -Integer.signum(((NumericIndexKey)key).compareTo(val));
3940
}
4041

4142
/** {@inheritDoc} */

modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/DoubleInlineIndexKeyType.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
2121
import org.apache.ignite.internal.cache.query.index.sorted.keys.DoubleIndexKey;
22+
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
2223
import org.apache.ignite.internal.cache.query.index.sorted.keys.NumericIndexKey;
2324
import org.apache.ignite.internal.pagemem.PageUtils;
2425

@@ -32,10 +33,10 @@ public DoubleInlineIndexKeyType() {
3233
}
3334

3435
/** {@inheritDoc} */
35-
@Override public int compareNumeric(NumericIndexKey key, long pageAddr, int off) {
36-
double val1 = Double.longBitsToDouble(PageUtils.getLong(pageAddr, off + 1));
36+
@Override public int compare0(long pageAddr, int off, IndexKey key) {
37+
double val = Double.longBitsToDouble(PageUtils.getLong(pageAddr, off + 1));
3738

38-
return key.compareTo(val1);
39+
return -Integer.signum(((NumericIndexKey)key).compareTo(val));
3940
}
4041

4142
/** {@inheritDoc} */

0 commit comments

Comments
 (0)