-
Notifications
You must be signed in to change notification settings - Fork 28.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'apache:master' into SPARK-32915-followup
- Loading branch information
Showing
59 changed files
with
2,249 additions
and
870 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,7 @@ github: | |
- jdbc | ||
- sql | ||
- spark | ||
enabled_merge_buttons: | ||
merge: false | ||
squash: true | ||
rebase: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
...n/network-common/src/main/java/org/apache/spark/network/util/TimerWithCustomTimeUnit.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You 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 org.apache.spark.network.util; | ||
|
||
import java.io.OutputStream; | ||
import java.io.PrintWriter; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import com.codahale.metrics.Clock; | ||
import com.codahale.metrics.ExponentiallyDecayingReservoir; | ||
import com.codahale.metrics.Snapshot; | ||
import com.codahale.metrics.Timer; | ||
|
||
/** | ||
* A custom version of a {@link Timer} which allows for specifying a specific {@link TimeUnit} to | ||
* be used when accessing timing values via {@link #getSnapshot()}. Normally, though the | ||
* {@link #update(long, TimeUnit)} method requires a unit, the extraction methods on the snapshot | ||
* do not specify a unit, and always return nanoseconds. It can be useful to specify that a timer | ||
* should use a different unit for its snapshot. Note that internally, all values are still stored | ||
* with nanosecond-precision; it is only before being returned to the caller that the nanosecond | ||
* value is converted to the custom time unit. | ||
*/ | ||
public class TimerWithCustomTimeUnit extends Timer { | ||
|
||
private final TimeUnit timeUnit; | ||
private final double nanosPerUnit; | ||
|
||
public TimerWithCustomTimeUnit(TimeUnit timeUnit) { | ||
this(timeUnit, Clock.defaultClock()); | ||
} | ||
|
||
TimerWithCustomTimeUnit(TimeUnit timeUnit, Clock clock) { | ||
super(new ExponentiallyDecayingReservoir(), clock); | ||
this.timeUnit = timeUnit; | ||
this.nanosPerUnit = timeUnit.toNanos(1); | ||
} | ||
|
||
@Override | ||
public Snapshot getSnapshot() { | ||
return new SnapshotWithCustomTimeUnit(super.getSnapshot()); | ||
} | ||
|
||
private double toUnit(double nanos) { | ||
// TimeUnit.convert() truncates (loses precision), so floating-point division is used instead | ||
return nanos / nanosPerUnit; | ||
} | ||
|
||
private long toUnit(long nanos) { | ||
return timeUnit.convert(nanos, TimeUnit.NANOSECONDS); | ||
} | ||
|
||
private class SnapshotWithCustomTimeUnit extends Snapshot { | ||
|
||
private final Snapshot wrappedSnapshot; | ||
|
||
SnapshotWithCustomTimeUnit(Snapshot wrappedSnapshot) { | ||
this.wrappedSnapshot = wrappedSnapshot; | ||
} | ||
|
||
@Override | ||
public double getValue(double v) { | ||
return toUnit(wrappedSnapshot.getValue(v)); | ||
} | ||
|
||
@Override | ||
public long[] getValues() { | ||
long[] nanoValues = wrappedSnapshot.getValues(); | ||
long[] customUnitValues = new long[nanoValues.length]; | ||
for (int i = 0; i < nanoValues.length; i++) { | ||
customUnitValues[i] = toUnit(nanoValues[i]); | ||
} | ||
return customUnitValues; | ||
} | ||
|
||
@Override | ||
public int size() { | ||
return wrappedSnapshot.size(); | ||
} | ||
|
||
@Override | ||
public long getMax() { | ||
return toUnit(wrappedSnapshot.getMax()); | ||
} | ||
|
||
@Override | ||
public double getMean() { | ||
return toUnit(wrappedSnapshot.getMean()); | ||
} | ||
|
||
@Override | ||
public long getMin() { | ||
return toUnit(wrappedSnapshot.getMin()); | ||
} | ||
|
||
@Override | ||
public double getStdDev() { | ||
return toUnit(wrappedSnapshot.getStdDev()); | ||
} | ||
|
||
@Override | ||
public void dump(OutputStream outputStream) { | ||
try (PrintWriter writer = new PrintWriter(outputStream)) { | ||
for (long value : getValues()) { | ||
writer.println(value); | ||
} | ||
} | ||
} | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
.../network-common/src/test/java/org/apache/spark/network/util/TimerWithCustomUnitSuite.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You 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 org.apache.spark.network.util; | ||
|
||
import java.time.Duration; | ||
import java.util.Arrays; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import com.codahale.metrics.Clock; | ||
import com.codahale.metrics.Snapshot; | ||
import com.codahale.metrics.Timer; | ||
import org.junit.Test; | ||
|
||
import static org.junit.Assert.assertArrayEquals; | ||
import static org.junit.Assert.assertEquals; | ||
|
||
/** Tests for {@link TimerWithCustomTimeUnit} */ | ||
public class TimerWithCustomUnitSuite { | ||
|
||
private static final double EPSILON = 1.0 / 1_000_000_000; | ||
|
||
@Test | ||
public void testTimerWithMillisecondTimeUnit() { | ||
testTimerWithCustomTimeUnit(TimeUnit.MILLISECONDS); | ||
} | ||
|
||
@Test | ||
public void testTimerWithNanosecondTimeUnit() { | ||
testTimerWithCustomTimeUnit(TimeUnit.NANOSECONDS); | ||
} | ||
|
||
private void testTimerWithCustomTimeUnit(TimeUnit timeUnit) { | ||
Timer timer = new TimerWithCustomTimeUnit(timeUnit); | ||
Duration[] durations = { | ||
Duration.ofNanos(1), | ||
Duration.ofMillis(1), | ||
Duration.ofMillis(5), | ||
Duration.ofMillis(100), | ||
Duration.ofSeconds(10) | ||
}; | ||
Arrays.stream(durations).forEach(timer::update); | ||
|
||
Snapshot snapshot = timer.getSnapshot(); | ||
assertEquals(toTimeUnit(durations[0], timeUnit), snapshot.getMin()); | ||
assertEquals(toTimeUnitFloating(durations[0], timeUnit), snapshot.getValue(0), EPSILON); | ||
assertEquals(toTimeUnitFloating(durations[2], timeUnit), snapshot.getMedian(), EPSILON); | ||
assertEquals(toTimeUnitFloating(durations[3], timeUnit), snapshot.get75thPercentile(), EPSILON); | ||
assertEquals(toTimeUnit(durations[4], timeUnit), snapshot.getMax()); | ||
|
||
assertArrayEquals(Arrays.stream(durations).mapToLong(d -> toTimeUnit(d, timeUnit)).toArray(), | ||
snapshot.getValues()); | ||
double total = Arrays.stream(durations).mapToDouble(d -> toTimeUnitFloating(d, timeUnit)).sum(); | ||
assertEquals(total / durations.length, snapshot.getMean(), EPSILON); | ||
} | ||
|
||
@Test | ||
public void testTimingViaContext() { | ||
ManualClock clock = new ManualClock(); | ||
Timer timer = new TimerWithCustomTimeUnit(TimeUnit.MILLISECONDS, clock); | ||
Duration[] durations = { Duration.ofNanos(1), Duration.ofMillis(100), Duration.ofMillis(1000) }; | ||
for (Duration d : durations) { | ||
Timer.Context context = timer.time(); | ||
clock.advance(toTimeUnit(d, TimeUnit.NANOSECONDS)); | ||
context.stop(); | ||
} | ||
|
||
Snapshot snapshot = timer.getSnapshot(); | ||
assertEquals(0, snapshot.getMin()); | ||
assertEquals(100, snapshot.getMedian(), EPSILON); | ||
assertEquals(1000, snapshot.getMax(), EPSILON); | ||
} | ||
|
||
private static long toTimeUnit(Duration duration, TimeUnit timeUnit) { | ||
return timeUnit.convert(duration.toNanos(), TimeUnit.NANOSECONDS); | ||
} | ||
|
||
private static double toTimeUnitFloating(Duration duration, TimeUnit timeUnit) { | ||
return ((double) duration.toNanos()) / timeUnit.toNanos(1); | ||
} | ||
|
||
private static class ManualClock extends Clock { | ||
|
||
private long currTick = 1; | ||
|
||
void advance(long nanos) { | ||
currTick += nanos; | ||
} | ||
|
||
@Override | ||
public long getTick() { | ||
return currTick; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.