Skip to content

Commit

Permalink
Restore date aggregation performance in UTC case
Browse files Browse the repository at this point in the history
The benchmarks showed a sharp decrease in aggregation performance for
the UTC case.

This commit uses the same calculation as joda time, which requires no
conversion into any java time object, also, the check for an fixedoffset
has been put into the ctor to reduce the need for runtime calculations.
The same goes for the amount of the used unit in milliseconds.

Closes elastic#37826
  • Loading branch information
spinscale committed Feb 1, 2019
1 parent 35ed137 commit 242df6f
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
import org.openjdk.jmh.annotations.Warmup;

import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.concurrent.TimeUnit;

import static org.elasticsearch.common.Rounding.DateTimeUnit.DAY_OF_MONTH;

@Fork(3)
@Warmup(iterations = 10)
@Measurement(iterations = 10)
Expand All @@ -48,23 +51,13 @@ public class RoundingBenchmark {
private final ZoneId zoneId = ZoneId.of("Europe/Amsterdam");
private final DateTimeZone timeZone = DateUtils.zoneIdToDateTimeZone(zoneId);

private final long timestamp = 1548879021354L;

private final org.elasticsearch.common.rounding.Rounding jodaRounding =
org.elasticsearch.common.rounding.Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(timeZone).build();
org.elasticsearch.common.rounding.Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(timeZone).build();
private final Rounding javaRounding = Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY)
.timeZone(zoneId).build();

private final org.elasticsearch.common.rounding.Rounding jodaDayOfMonthRounding =
org.elasticsearch.common.rounding.Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(timeZone).build();
private final Rounding javaDayOfMonthRounding = Rounding.builder(TimeValue.timeValueMinutes(60))
.timeZone(zoneId).build();

private final org.elasticsearch.common.rounding.Rounding timeIntervalRoundingJoda =
org.elasticsearch.common.rounding.Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(timeZone).build();
private final Rounding timeIntervalRoundingJava = Rounding.builder(TimeValue.timeValueMinutes(60))
.timeZone(zoneId).build();

private final long timestamp = 1548879021354L;

@Benchmark
public long timeRoundingDateTimeUnitJoda() {
return jodaRounding.round(timestamp);
Expand All @@ -75,6 +68,11 @@ public long timeRoundingDateTimeUnitJava() {
return javaRounding.round(timestamp);
}

private final org.elasticsearch.common.rounding.Rounding jodaDayOfMonthRounding =
org.elasticsearch.common.rounding.Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(timeZone).build();
private final Rounding javaDayOfMonthRounding = Rounding.builder(DAY_OF_MONTH)
.timeZone(zoneId).build();

@Benchmark
public long timeRoundingDateTimeUnitDayOfMonthJoda() {
return jodaDayOfMonthRounding.round(timestamp);
Expand All @@ -85,6 +83,11 @@ public long timeRoundingDateTimeUnitDayOfMonthJava() {
return javaDayOfMonthRounding.round(timestamp);
}

private final org.elasticsearch.common.rounding.Rounding timeIntervalRoundingJoda =
org.elasticsearch.common.rounding.Rounding.builder(TimeValue.timeValueMinutes(60)).timeZone(timeZone).build();
private final Rounding timeIntervalRoundingJava = Rounding.builder(TimeValue.timeValueMinutes(60))
.timeZone(zoneId).build();

@Benchmark
public long timeIntervalRoundingJava() {
return timeIntervalRoundingJava.round(timestamp);
Expand All @@ -94,4 +97,19 @@ public long timeIntervalRoundingJava() {
public long timeIntervalRoundingJoda() {
return timeIntervalRoundingJoda.round(timestamp);
}

private final org.elasticsearch.common.rounding.Rounding timeUnitRoundingUtcMonthOfYearJoda =
org.elasticsearch.common.rounding.Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(DateTimeZone.UTC).build();
private final Rounding timeUnitRoundingUtcMonthOfYearJava = Rounding.builder(DAY_OF_MONTH)
.timeZone(ZoneOffset.UTC).build();

@Benchmark
public long timeUnitRoundingUtcMonthOfYearJava() {
return timeUnitRoundingUtcMonthOfYearJava.round(timestamp);
}

@Benchmark
public long timeUnitRoundingUtcMonthOfYearJoda() {
return timeUnitRoundingUtcMonthOfYearJoda.round(timestamp);
}
}
23 changes: 22 additions & 1 deletion server/src/main/java/org/elasticsearch/common/Rounding.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,16 @@ public enum DateTimeUnit {

private final byte id;
private final TemporalField field;
private final long unitMillis;

DateTimeUnit(byte id, TemporalField field) {
this.id = id;
this.field = field;
this.unitMillis = field.getBaseUnit().getDuration().toMillis();
}

public long getUnitMillis() {
return unitMillis;
}

public byte getId() {
Expand Down Expand Up @@ -182,12 +188,14 @@ static class TimeUnitRounding extends Rounding {
private final DateTimeUnit unit;
private final ZoneId timeZone;
private final boolean unitRoundsToMidnight;
private final boolean isFixedOffset;


TimeUnitRounding(DateTimeUnit unit, ZoneId timeZone) {
this.unit = unit;
this.timeZone = timeZone;
this.unitRoundsToMidnight = this.unit.field.getBaseUnit().getDuration().toMillis() > 3600000L;
this.isFixedOffset = timeZone.getRules().isFixedOffset();
}

TimeUnitRounding(StreamInput in) throws IOException {
Expand Down Expand Up @@ -236,7 +244,20 @@ private LocalDateTime truncateLocalDateTime(LocalDateTime localDateTime) {
}

@Override
public long round(final long utcMillis) {
public long round(long utcMillis) {
// this works as long as the offset doesn't change. It is worth getting this case out of the way first, as
// the calculations for fixing things near to offset changes are a little expensive and are unnecessary in the common case
// of working in UTC.
if (isFixedOffset) {
long unitMillis = unit.getUnitMillis();
if (utcMillis >= 0) {
return utcMillis - utcMillis % unitMillis;
} else {
utcMillis += 1;
return utcMillis - utcMillis % unitMillis - unitMillis;
}
}

Instant instant = Instant.ofEpochMilli(utcMillis);
if (unitRoundsToMidnight) {
final LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, timeZone);
Expand Down

0 comments on commit 242df6f

Please sign in to comment.