Skip to content

Commit f6b753a

Browse files
committed
Remove unnecessary intermediate data gathering when sorting (#479)
* Remove unnecessary intermediate data gathering when sorting * add performance test * Revert "Remove unnecessary intermediate data gathering when sorting" This reverts commit cb794a6. * Revert "Revert "Remove unnecessary intermediate data gathering when sorting"" This reverts commit 28f6417. * add more tests * formatting * Update changelog
1 parent b3f1e40 commit f6b753a

File tree

7 files changed

+181
-33
lines changed

7 files changed

+181
-33
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## Changelog
22

33
#### Version 0.11.4 (TBD)
4+
* Slightly improved the performance of result sorting by removing unnecessary intermediate data gathering.
45
* Improved random access performance for all result sets.
56

67
#### Version 0.11.3 (June 4, 2022)

concourse-driver-java/src/main/java/com/cinchapi/concourse/data/sort/SortableColumn.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
*/
1616
package com.cinchapi.concourse.data.sort;
1717

18+
import java.util.AbstractMap.SimpleImmutableEntry;
1819
import java.util.LinkedHashMap;
1920
import java.util.Map;
2021
import java.util.Set;
2122
import java.util.stream.Collectors;
23+
import java.util.stream.Stream;
2224

2325
import javax.annotation.concurrent.NotThreadSafe;
2426

@@ -131,10 +133,10 @@ protected Map<Long, V> delegate() {
131133
*
132134
* @return a sortable view of the {@link #delegate}
133135
*/
134-
private Map<Long, Map<String, V>> sortable() {
136+
private Stream<Entry<Long, Map<String, V>>> sortable() {
135137
return delegate.entrySet().stream()
136-
.collect(Collectors.toMap(Entry::getKey,
137-
entry -> ImmutableMap.of(this.key, entry.getValue())));
138+
.map(entry -> new SimpleImmutableEntry<>(entry.getKey(),
139+
ImmutableMap.of(this.key, entry.getValue())));
138140
}
139141

140142
}

concourse-driver-java/src/main/java/com/cinchapi/concourse/data/sort/SortableSet.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
*/
1616
package com.cinchapi.concourse.data.sort;
1717

18-
import java.util.AbstractMap;
18+
import java.util.AbstractMap.SimpleImmutableEntry;
1919
import java.util.LinkedHashSet;
2020
import java.util.Map;
2121
import java.util.Map.Entry;
2222
import java.util.Set;
2323
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
2425

25-
import com.cinchapi.common.collect.lazy.LazyTransformSet;
2626
import com.cinchapi.concourse.EmptyOperationException;
2727
import com.google.common.collect.ForwardingSet;
2828
import com.google.common.collect.ImmutableMap;
@@ -83,23 +83,11 @@ public void sort(Sorter<V> sorter, long at) {
8383
*
8484
* @return a sortable view of the {@link #delegate}
8585
*/
86-
private Map<Long, Map<String, V>> sortable() {
87-
return new AbstractMap<Long, Map<String, V>>() {
86+
private Stream<Entry<Long, Map<String, V>>> sortable() {
87+
Map<String, V> value = ImmutableMap.of();
88+
return delegate.stream()
89+
.map(record -> new SimpleImmutableEntry<>(record, value));
8890

89-
Set<Entry<Long, Map<String, V>>> entrySet = null;
90-
91-
@Override
92-
public Set<Entry<Long, Map<String, V>>> entrySet() {
93-
if(entrySet == null) {
94-
entrySet = LazyTransformSet.of(delegate, record -> {
95-
return new SimpleImmutableEntry<>(record,
96-
ImmutableMap.of());
97-
});
98-
}
99-
return entrySet;
100-
}
101-
102-
};
10391
}
10492

10593
@Override

concourse-driver-java/src/main/java/com/cinchapi/concourse/data/sort/Sorter.java

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ public interface Sorter<V> {
3939
* @return a {@link Stream} that contains each of the entries in
4040
* {@code data} in sorted order
4141
*/
42-
public Stream<Entry<Long, Map<String, V>>> sort(
43-
Map<Long, Map<String, V>> data);
42+
public default Stream<Entry<Long, Map<String, V>>> sort(
43+
Map<Long, Map<String, V>> data) {
44+
return sort(data.entrySet().stream());
45+
}
4446

4547
/**
4648
* Sort {@code data} using the {@code at} timestamp as temporal binding for
@@ -52,8 +54,33 @@ public Stream<Entry<Long, Map<String, V>>> sort(
5254
* @return a {@link Stream} that contains each of the entries in
5355
* {@code data} in sorted order
5456
*/
57+
public default Stream<Entry<Long, Map<String, V>>> sort(
58+
Map<Long, Map<String, V>> data, @Nullable Long at) {
59+
return sort(data.entrySet().stream(), at);
60+
}
61+
62+
/**
63+
* Sort the {@code stream}.
64+
*
65+
* @param stream
66+
* @return a {@link Stream} that contains each of the entries in
67+
* {@code data} in sorted order
68+
*/
69+
public Stream<Entry<Long, Map<String, V>>> sort(
70+
Stream<Entry<Long, Map<String, V>>> stream);
71+
72+
/**
73+
* Sort the {@code stream} using the {@code at} timestamp as temporal
74+
* binding for missing value lookups when an order component does not
75+
* explicitly specify a timestamp.
76+
*
77+
* @param stream
78+
* @param at
79+
* @return a {@link Stream} that contains each of the entries in
80+
* {@code data} in sorted order
81+
*/
5582
public Stream<Entry<Long, Map<String, V>>> sort(
56-
Map<Long, Map<String, V>> data, @Nullable Long at);
83+
Stream<Entry<Long, Map<String, V>>> stream, @Nullable Long at);
5784

5885
/**
5986
* Sort and collect {@code data}.
@@ -63,8 +90,10 @@ public Stream<Entry<Long, Map<String, V>>> sort(
6390
* order
6491
*/
6592
default Map<Long, Map<String, V>> organize(Map<Long, Map<String, V>> data) {
66-
return sort(data).collect(Collectors.toMap(Entry::getKey,
67-
Entry::getValue, (a, b) -> b, LinkedHashMap::new));
93+
return sort(data)
94+
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
95+
(a, b) -> b, () -> new LinkedHashMap<>(data.size())));
96+
6897
}
6998

7099
/**
@@ -79,7 +108,37 @@ default Map<Long, Map<String, V>> organize(Map<Long, Map<String, V>> data) {
79108
*/
80109
default Map<Long, Map<String, V>> organize(Map<Long, Map<String, V>> data,
81110
@Nullable Long at) {
82-
return sort(data, at).collect(Collectors.toMap(Entry::getKey,
111+
return sort(data, at)
112+
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
113+
(a, b) -> b, () -> new LinkedHashMap<>(data.size())));
114+
}
115+
116+
/**
117+
* Sort and collect the {@code stream}.
118+
*
119+
* @param data
120+
* @return a collected {@link Map} that contains the {@code data} in sorted
121+
* order
122+
*/
123+
default Map<Long, Map<String, V>> organize(
124+
Stream<Entry<Long, Map<String, V>>> stream) {
125+
return sort(stream).collect(Collectors.toMap(Entry::getKey,
126+
Entry::getValue, (a, b) -> b, LinkedHashMap::new));
127+
}
128+
129+
/**
130+
* Sort and collected {@code data} using the {@code at} timestamp as
131+
* temporal binding for missing value lookups when an order component does
132+
* not explicitly specify a timestamp.
133+
*
134+
* @param data
135+
* @param at
136+
* @return a collected {@link Map} that contains the {@code data} in sorted
137+
* order
138+
*/
139+
default Map<Long, Map<String, V>> organize(
140+
Stream<Entry<Long, Map<String, V>>> stream, @Nullable Long at) {
141+
return sort(stream, at).collect(Collectors.toMap(Entry::getKey,
83142
Entry::getValue, (a, b) -> b, LinkedHashMap::new));
84143
}
85144

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (c) 2013-2022 Cinchapi Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.cinchapi.concourse.ete.performance;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.concurrent.TimeUnit;
21+
22+
import org.junit.Test;
23+
24+
import com.cinchapi.common.profile.Benchmark;
25+
import com.cinchapi.concourse.lang.sort.Order;
26+
import com.cinchapi.concourse.util.Random;
27+
import com.google.common.collect.ImmutableMultimap;
28+
import com.google.common.collect.Multimap;
29+
30+
/**
31+
* Benchmark for sorting various kinds of result sets
32+
*
33+
* @author Jeff Nelson
34+
*/
35+
public class CrossVersionSortPerformanceBenchmarkTest
36+
extends CrossVersionBenchmarkTest {
37+
38+
static List<Multimap<String, Object>> data;
39+
static {
40+
data = new ArrayList<>();
41+
for (int i = 0; i < 2000; ++i) {
42+
Multimap<String, Object> row = ImmutableMultimap.of("name",
43+
Random.getString(), "age", Random.getNumber(), "foo",
44+
Random.getBoolean(), "bar", Random.getString(), "include",
45+
true);
46+
data.add(row);
47+
}
48+
}
49+
50+
@Override
51+
protected void beforeEachBenchmarkRuns() {
52+
client.insert(data);
53+
}
54+
55+
@Test
56+
public void testSortColumn() {
57+
Benchmark benchmark = new Benchmark(TimeUnit.MILLISECONDS) {
58+
59+
@Override
60+
public void action() {
61+
client.select("name", "include = true",
62+
Order.by("name").then("age"));
63+
}
64+
65+
};
66+
long elapsed = benchmark.run();
67+
record("column", elapsed);
68+
}
69+
70+
@Test
71+
public void testSortSet() {
72+
Benchmark benchmark = new Benchmark(TimeUnit.MILLISECONDS) {
73+
74+
@Override
75+
public void action() {
76+
client.find("include = true", Order.by("name").then("age"));
77+
}
78+
79+
};
80+
long elapsed = benchmark.run();
81+
record("set", elapsed);
82+
}
83+
84+
@Test
85+
public void testSortTable() {
86+
Benchmark benchmark = new Benchmark(TimeUnit.MILLISECONDS) {
87+
88+
@Override
89+
public void action() {
90+
client.select("include = true", Order.by("name").then("age"));
91+
}
92+
93+
};
94+
long elapsed = benchmark.run();
95+
record("table", elapsed);
96+
}
97+
98+
}

concourse-server/src/main/java/com/cinchapi/concourse/server/query/sort/Sorting.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ private static class NoOrderSorter<V> implements Sorter<V> {
7373

7474
@Override
7575
public Stream<Entry<Long, Map<String, V>>> sort(
76-
Map<Long, Map<String, V>> data) {
76+
Stream<Entry<Long, Map<String, V>>> data) {
7777
throw EmptyOperationException.INSTNACE;
7878
}
7979

8080
@Override
8181
public Stream<Entry<Long, Map<String, V>>> sort(
82-
Map<Long, Map<String, V>> data, Long at) {
82+
Stream<Entry<Long, Map<String, V>>> data, Long at) {
8383
throw EmptyOperationException.INSTNACE;
8484
}
8585

concourse-server/src/main/java/com/cinchapi/concourse/server/query/sort/StoreSorter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ protected StoreSorter(Order order, Store store) {
6565

6666
@Override
6767
public final Stream<Entry<Long, Map<String, V>>> sort(
68-
Map<Long, Map<String, V>> data) {
69-
return sort(data, null);
68+
Stream<Entry<Long, Map<String, V>>> stream) {
69+
return sort(stream, null);
7070
}
7171

7272
@Override
7373
public final Stream<Entry<Long, Map<String, V>>> sort(
74-
Map<Long, Map<String, V>> data, @Nullable Long at) {
74+
Stream<Entry<Long, Map<String, V>>> stream, @Nullable Long at) {
7575
ArrayBuilder<Comparator<Entry<Long, Map<String, V>>>> comparators = ArrayBuilder
7676
.builder();
7777
for (OrderComponent component : order.spec()) {
@@ -121,7 +121,7 @@ else if(!Empty.ness().describes(v2)) {
121121
comparators.add((e1, e2) -> e1.getKey().compareTo(e2.getKey()));
122122
Comparator<Entry<Long, Map<String, V>>> comparator = CompoundComparator
123123
.of(comparators.build());
124-
return data.entrySet().stream().sorted(comparator);
124+
return stream.sorted(comparator);
125125
}
126126

127127
/**

0 commit comments

Comments
 (0)