/*
* Copyright (C) 2014 - present by Yann Le Tallec.
* Please see distribution for license.
*/
package com.assylias.performance;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
/**
*
*/
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO25573616 {
private Set<LocalDate> retainDates;
private Set<LocalDate> removeDates;
private TimeSeries ts;
private ImmutableTimeSeries its;
@Setup
public void prepare() {
ts = new TimeSeries();
retainDates = new HashSet<>();
removeDates = new HashSet<>();
LocalDate now = LocalDate.now();
for (int i = 0; i < 1500; i++) {
LocalDate d = now.minusDays(i);
if (i % 500 != 0) retainDates.add(d);
else removeDates.add(d);
ts.add(d, i);
}
LocalDate[] dates = new LocalDate[ts.dates().size()];
double[] prices = new double[ts.dates().size()];
int i = 0;
for (Entry<LocalDate, Double> entry : ts.prices.entrySet()) {
dates[i] = entry.getKey();
prices[i] = entry.getValue();
++i;
}
its = new ImmutableTimeSeries(dates, prices);
}
// @Benchmark
public double baseline() {
return ts.lastPriceAt(LocalDate.now());
}
@Benchmark
public TimeSeries onDates_current() {
TimeSeries filtered = new TimeSeries(new TreeMap<>(ts.prices));
filtered.dates().retainAll(retainDates);
return filtered;
}
// @Benchmark
public TimeSeries onDates_biziclop() {
TreeMap<LocalDate, Double> filtered = new TreeMap<>();
for (Entry<LocalDate, Double> entry : ts.prices.entrySet()) {
if (retainDates.contains(entry.getKey())) {
filtered.put(entry.getKey(), entry.getValue());
}
}
return new TimeSeries(filtered);
}
// @Benchmark
public TimeSeries onDates_maaartinus() {
ImmutableSortedMap.Builder<LocalDate, Double> filtered = new ImmutableSortedMap.Builder<> (Ordering.natural());
for (Entry<LocalDate, Double> entry : ts.prices.entrySet()) {
if (retainDates.contains(entry.getKey())) {
filtered.put(entry.getKey(), entry.getValue());
}
}
return new TimeSeries(filtered.build());
}
// @Benchmark
public ImmutableTimeSeries onDates_immutable_timeseries() {
LocalDate[] dates = new LocalDate[retainDates.size()];
double[] prices = new double[retainDates.size()];
int i = 0;
for (Entry<LocalDate, Double> entry : ts.prices.entrySet()) {
if (retainDates.contains(entry.getKey())) {
dates[i] = entry.getKey();
prices[i] = entry.getValue();
++i;
}
}
return new ImmutableTimeSeries(dates, prices);
}
// @Benchmark
public ImmutableTimeSeries onDates_immutable_timeseries_from_immutable() {
LocalDate[] dates = new LocalDate[retainDates.size()];
double[] prices = new double[retainDates.size()];
int j = 0;
for (int i = 0; i < its.dates.length; i++) {
if (retainDates.contains(its.dates[i])) {
dates[j] = its.dates[i];
prices[j] = its.prices[i];
++j;
}
}
return new ImmutableTimeSeries(dates, prices);
}
@Benchmark
public ImmutableTimeSeries onDates_immutable_timeseries_from_immutable_remove() {
int originalSize = its.dates.length;
int newSize = originalSize - removeDates.size();
LocalDate[] dates = new LocalDate[newSize];
double[] prices = new double[newSize];
int j = 0;
for (int i = 0; i < its.dates.length; i++) {
if (removeDates.contains(its.dates[i])) continue;
dates[j] = its.dates[i];
prices[j] = its.prices[i];
++j;
}
return new ImmutableTimeSeries(dates, prices);
}
@Benchmark
public double lastPrice_current() {
LocalDate date = LocalDate.now().minusYears(1);
return ts.lastPriceAt(date);
}
@Benchmark
public double lastPrice_immutable() {
LocalDate date = LocalDate.now().minusYears(1);
return its.lastPriceAt(date);
}
public static class TimeSeries {
private final NavigableMap<LocalDate, Double> prices;
public TimeSeries() {
prices = new TreeMap<>();
}
private TimeSeries(NavigableMap<LocalDate, Double> prices) {
this.prices = prices;
}
public void add(LocalDate date, double price) {
prices.put(date, price);
}
public Set<LocalDate> dates() {
return prices.keySet();
}
//the 2 methods below are examples of why I need a TreeMap
public double lastPriceAt(LocalDate date) {
Map.Entry<LocalDate, Double> price = prices.floorEntry(date);
return price.getValue(); //after some null checks
}
public TimeSeries between(LocalDate from, LocalDate to) {
return new TimeSeries(this.prices.subMap(from, true, to, true));
}
}
public static class ImmutableTimeSeries {
LocalDate[] dates;
double[] prices;
public ImmutableTimeSeries(LocalDate[] dates, double[] prices) {
this.dates = dates;
this.prices = prices;
}
public Set<LocalDate> dates() {
Set<LocalDate> set = new TreeSet<> ();
Collections.addAll(set, dates);
return set;
}
//the 2 methods below are examples of why I need a TreeMap
public double lastPriceAt(LocalDate date) {
int index = Arrays.binarySearch(dates, date);
if (index >= 0) return prices[index];
index = - index - 1;
if (index == prices.length) index = prices.length - 1;
return prices[index];
}
}
}