Skip to content

Commit

Permalink
Remove unnecessary intermediate data gathering when sorting (#479)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jtnelson committed Jul 4, 2022
1 parent b3f1e40 commit f6b753a
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Changelog

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

#### Version 0.11.3 (June 4, 2022)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
*/
package com.cinchapi.concourse.data.sort;

import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.concurrent.NotThreadSafe;

Expand Down Expand Up @@ -131,10 +133,10 @@ protected Map<Long, V> delegate() {
*
* @return a sortable view of the {@link #delegate}
*/
private Map<Long, Map<String, V>> sortable() {
private Stream<Entry<Long, Map<String, V>>> sortable() {
return delegate.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
entry -> ImmutableMap.of(this.key, entry.getValue())));
.map(entry -> new SimpleImmutableEntry<>(entry.getKey(),
ImmutableMap.of(this.key, entry.getValue())));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
*/
package com.cinchapi.concourse.data.sort;

import java.util.AbstractMap;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

Set<Entry<Long, Map<String, V>>> entrySet = null;

@Override
public Set<Entry<Long, Map<String, V>>> entrySet() {
if(entrySet == null) {
entrySet = LazyTransformSet.of(delegate, record -> {
return new SimpleImmutableEntry<>(record,
ImmutableMap.of());
});
}
return entrySet;
}

};
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ public interface Sorter<V> {
* @return a {@link Stream} that contains each of the entries in
* {@code data} in sorted order
*/
public Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data);
public default Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data) {
return sort(data.entrySet().stream());
}

/**
* Sort {@code data} using the {@code at} timestamp as temporal binding for
Expand All @@ -52,8 +54,33 @@ public Stream<Entry<Long, Map<String, V>>> sort(
* @return a {@link Stream} that contains each of the entries in
* {@code data} in sorted order
*/
public default Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data, @Nullable Long at) {
return sort(data.entrySet().stream(), at);
}

/**
* Sort the {@code stream}.
*
* @param stream
* @return a {@link Stream} that contains each of the entries in
* {@code data} in sorted order
*/
public Stream<Entry<Long, Map<String, V>>> sort(
Stream<Entry<Long, Map<String, V>>> stream);

/**
* Sort the {@code stream} using the {@code at} timestamp as temporal
* binding for missing value lookups when an order component does not
* explicitly specify a timestamp.
*
* @param stream
* @param at
* @return a {@link Stream} that contains each of the entries in
* {@code data} in sorted order
*/
public Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data, @Nullable Long at);
Stream<Entry<Long, Map<String, V>>> stream, @Nullable Long at);

/**
* Sort and collect {@code data}.
Expand All @@ -63,8 +90,10 @@ public Stream<Entry<Long, Map<String, V>>> sort(
* order
*/
default Map<Long, Map<String, V>> organize(Map<Long, Map<String, V>> data) {
return sort(data).collect(Collectors.toMap(Entry::getKey,
Entry::getValue, (a, b) -> b, LinkedHashMap::new));
return sort(data)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(a, b) -> b, () -> new LinkedHashMap<>(data.size())));

}

/**
Expand All @@ -79,7 +108,37 @@ default Map<Long, Map<String, V>> organize(Map<Long, Map<String, V>> data) {
*/
default Map<Long, Map<String, V>> organize(Map<Long, Map<String, V>> data,
@Nullable Long at) {
return sort(data, at).collect(Collectors.toMap(Entry::getKey,
return sort(data, at)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(a, b) -> b, () -> new LinkedHashMap<>(data.size())));
}

/**
* Sort and collect the {@code stream}.
*
* @param data
* @return a collected {@link Map} that contains the {@code data} in sorted
* order
*/
default Map<Long, Map<String, V>> organize(
Stream<Entry<Long, Map<String, V>>> stream) {
return sort(stream).collect(Collectors.toMap(Entry::getKey,
Entry::getValue, (a, b) -> b, LinkedHashMap::new));
}

/**
* Sort and collected {@code data} using the {@code at} timestamp as
* temporal binding for missing value lookups when an order component does
* not explicitly specify a timestamp.
*
* @param data
* @param at
* @return a collected {@link Map} that contains the {@code data} in sorted
* order
*/
default Map<Long, Map<String, V>> organize(
Stream<Entry<Long, Map<String, V>>> stream, @Nullable Long at) {
return sort(stream, at).collect(Collectors.toMap(Entry::getKey,
Entry::getValue, (a, b) -> b, LinkedHashMap::new));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2013-2022 Cinchapi Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cinchapi.concourse.ete.performance;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.cinchapi.common.profile.Benchmark;
import com.cinchapi.concourse.lang.sort.Order;
import com.cinchapi.concourse.util.Random;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;

/**
* Benchmark for sorting various kinds of result sets
*
* @author Jeff Nelson
*/
public class CrossVersionSortPerformanceBenchmarkTest
extends CrossVersionBenchmarkTest {

static List<Multimap<String, Object>> data;
static {
data = new ArrayList<>();
for (int i = 0; i < 2000; ++i) {
Multimap<String, Object> row = ImmutableMultimap.of("name",
Random.getString(), "age", Random.getNumber(), "foo",
Random.getBoolean(), "bar", Random.getString(), "include",
true);
data.add(row);
}
}

@Override
protected void beforeEachBenchmarkRuns() {
client.insert(data);
}

@Test
public void testSortColumn() {
Benchmark benchmark = new Benchmark(TimeUnit.MILLISECONDS) {

@Override
public void action() {
client.select("name", "include = true",
Order.by("name").then("age"));
}

};
long elapsed = benchmark.run();
record("column", elapsed);
}

@Test
public void testSortSet() {
Benchmark benchmark = new Benchmark(TimeUnit.MILLISECONDS) {

@Override
public void action() {
client.find("include = true", Order.by("name").then("age"));
}

};
long elapsed = benchmark.run();
record("set", elapsed);
}

@Test
public void testSortTable() {
Benchmark benchmark = new Benchmark(TimeUnit.MILLISECONDS) {

@Override
public void action() {
client.select("include = true", Order.by("name").then("age"));
}

};
long elapsed = benchmark.run();
record("table", elapsed);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ private static class NoOrderSorter<V> implements Sorter<V> {

@Override
public Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data) {
Stream<Entry<Long, Map<String, V>>> data) {
throw EmptyOperationException.INSTNACE;
}

@Override
public Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data, Long at) {
Stream<Entry<Long, Map<String, V>>> data, Long at) {
throw EmptyOperationException.INSTNACE;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ protected StoreSorter(Order order, Store store) {

@Override
public final Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data) {
return sort(data, null);
Stream<Entry<Long, Map<String, V>>> stream) {
return sort(stream, null);
}

@Override
public final Stream<Entry<Long, Map<String, V>>> sort(
Map<Long, Map<String, V>> data, @Nullable Long at) {
Stream<Entry<Long, Map<String, V>>> stream, @Nullable Long at) {
ArrayBuilder<Comparator<Entry<Long, Map<String, V>>>> comparators = ArrayBuilder
.builder();
for (OrderComponent component : order.spec()) {
Expand Down Expand Up @@ -121,7 +121,7 @@ else if(!Empty.ness().describes(v2)) {
comparators.add((e1, e2) -> e1.getKey().compareTo(e2.getKey()));
Comparator<Entry<Long, Map<String, V>>> comparator = CompoundComparator
.of(comparators.build());
return data.entrySet().stream().sorted(comparator);
return stream.sorted(comparator);
}

/**
Expand Down

0 comments on commit f6b753a

Please sign in to comment.