diff options
author | Luck <git@lucko.me> | 2019-02-21 13:06:10 +0000 |
---|---|---|
committer | Luck <git@lucko.me> | 2019-02-21 13:06:10 +0000 |
commit | 161d3bdfbfe331a1ea3c6da1d096e86fd7a5c525 (patch) | |
tree | 96be6a24e9bd45198e4d5620eff57bba1e0a94cb | |
parent | 77c93aeeea763c68a4fd7f5b59afcfc2f6336379 (diff) | |
download | spark-161d3bdfbfe331a1ea3c6da1d096e86fd7a5c525.tar.gz spark-161d3bdfbfe331a1ea3c6da1d096e86fd7a5c525.tar.bz2 spark-161d3bdfbfe331a1ea3c6da1d096e86fd7a5c525.zip |
Allow sampling at fractions of a millisecond intervals
7 files changed, 44 insertions, 18 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java index d240554..2b202af 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java @@ -89,6 +89,18 @@ public class Arguments { return -1; // undefined } + public double doubleFlag(String key) { + Iterator<String> it = this.parsedArgs.get(key).iterator(); + if (it.hasNext()) { + try { + return Math.abs(Double.parseDouble(it.next())); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid input for '" + key + "' argument. Please specify a number!"); + } + } + return -1; // undefined + } + public Set<String> stringFlag(String key) { return this.parsedArgs.get(key); } diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java index 6ebbee1..9d00a96 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java @@ -73,7 +73,7 @@ public class SamplerModule<S> implements CommandModule<S> { platform.sendPrefixedMessage(sender, "&7The accuracy of the output will significantly improve when sampling is able to run for longer periods. Consider setting a timeout value over 30 seconds."); } - int intervalMillis = arguments.intFlag("interval"); + double intervalMillis = arguments.doubleFlag("interval"); if (intervalMillis <= 0) { intervalMillis = 4; } diff --git a/spark-common/src/main/java/me/lucko/spark/sampler/Sampler.java b/spark-common/src/main/java/me/lucko/spark/sampler/Sampler.java index 3a9a271..2e8ac65 100644 --- a/spark-common/src/main/java/me/lucko/spark/sampler/Sampler.java +++ b/spark-common/src/main/java/me/lucko/spark/sampler/Sampler.java @@ -23,6 +23,7 @@ package me.lucko.spark.sampler; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gson.stream.JsonWriter; + import me.lucko.spark.sampler.aggregator.DataAggregator; import me.lucko.spark.sampler.aggregator.SimpleDataAggregator; import me.lucko.spark.sampler.aggregator.TickedDataAggregator; @@ -71,7 +72,7 @@ public class Sampler implements Runnable { /** A future to encapsulation the completion of this sampler instance */ private final CompletableFuture<Sampler> future = new CompletableFuture<>(); - /** The interval to wait between sampling, in milliseconds */ + /** The interval to wait between sampling, in microseconds */ private final int interval; /** The time when sampling first began */ private long startTime = -1; @@ -98,7 +99,7 @@ public class Sampler implements Runnable { public void start() { this.startTime = System.currentTimeMillis(); this.dataAggregator.start(); - this.task = this.workerPool.scheduleAtFixedRate(this, 0, this.interval, TimeUnit.MILLISECONDS); + this.task = this.workerPool.scheduleAtFixedRate(this, 0, this.interval, TimeUnit.MICROSECONDS); } public long getStartTime() { diff --git a/spark-common/src/main/java/me/lucko/spark/sampler/SamplerBuilder.java b/spark-common/src/main/java/me/lucko/spark/sampler/SamplerBuilder.java index 07449ec..bf9dc04 100644 --- a/spark-common/src/main/java/me/lucko/spark/sampler/SamplerBuilder.java +++ b/spark-common/src/main/java/me/lucko/spark/sampler/SamplerBuilder.java @@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit; */ public class SamplerBuilder { - private int samplingInterval = 4; + private double samplingInterval = 4; // milliseconds private boolean includeLineNumbers = false; private long timeout = -1; private ThreadDumper threadDumper = ThreadDumper.ALL; @@ -39,7 +39,7 @@ public class SamplerBuilder { public SamplerBuilder() { } - public SamplerBuilder samplingInterval(int samplingInterval) { + public SamplerBuilder samplingInterval(double samplingInterval) { this.samplingInterval = samplingInterval; return this; } @@ -75,10 +75,12 @@ public class SamplerBuilder { public Sampler start() { Sampler sampler; + + int intervalMicros = (int) (this.samplingInterval * 1000d); if (this.ticksOver != -1 && this.tickCounter != null) { - sampler = new Sampler(this.samplingInterval, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers, this.tickCounter, this.ticksOver); + sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers, this.tickCounter, this.ticksOver); } else { - sampler = new Sampler(this.samplingInterval, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers); + sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.includeLineNumbers); } sampler.start(); diff --git a/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/SimpleDataAggregator.java b/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/SimpleDataAggregator.java index 25e2071..a72b47f 100644 --- a/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/SimpleDataAggregator.java +++ b/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/SimpleDataAggregator.java @@ -43,7 +43,7 @@ public class SimpleDataAggregator implements DataAggregator { /** The instance used to group threads together */ private final ThreadGrouper threadGrouper; - /** The interval to wait between sampling, in milliseconds */ + /** The interval to wait between sampling, in microseconds */ private final int interval; /** If line numbers should be included in the output */ diff --git a/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/TickedDataAggregator.java b/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/TickedDataAggregator.java index 2c68b02..ef568c8 100644 --- a/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/TickedDataAggregator.java +++ b/spark-common/src/main/java/me/lucko/spark/sampler/aggregator/TickedDataAggregator.java @@ -50,14 +50,14 @@ public class TickedDataAggregator implements DataAggregator { /** The instance used to group threads together */ private final ThreadGrouper threadGrouper; - /** The interval to wait between sampling, in milliseconds */ + /** The interval to wait between sampling, in microseconds */ private final int interval; /** If line numbers should be included in the output */ private final boolean includeLineNumbers; - /** Tick durations under this threshold will not be inserted */ - private final int tickLengthThreshold; + /** Tick durations under this threshold will not be inserted, measured in microseconds */ + private final long tickLengthThreshold; /** The expected number of samples in each tick */ private final int expectedSize; @@ -74,9 +74,10 @@ public class TickedDataAggregator implements DataAggregator { this.threadGrouper = threadGrouper; this.interval = interval; this.includeLineNumbers = includeLineNumbers; - this.tickLengthThreshold = tickLengthThreshold; + this.tickLengthThreshold = TimeUnit.MILLISECONDS.toMicros(tickLengthThreshold); // 50 millis in a tick, plus 10 so we have a bit of room to go over - this.expectedSize = (50 / interval) + 10; + double intervalMilliseconds = interval / 1000d; + this.expectedSize = (int) ((50 / intervalMilliseconds) + 10); } @Override @@ -101,10 +102,10 @@ public class TickedDataAggregator implements DataAggregator { TickList currentData = this.currentData; // approximate how long the tick lasted - int tickLengthMillis = currentData.getList().size() * this.interval; + int tickLengthMicros = currentData.getList().size() * this.interval; // don't push data below the threshold - if (tickLengthMillis < this.tickLengthThreshold) { + if (tickLengthMicros < this.tickLengthThreshold) { return; } diff --git a/spark-common/src/main/java/me/lucko/spark/sampler/node/AbstractNode.java b/spark-common/src/main/java/me/lucko/spark/sampler/node/AbstractNode.java index 75632c4..859014f 100644 --- a/spark-common/src/main/java/me/lucko/spark/sampler/node/AbstractNode.java +++ b/spark-common/src/main/java/me/lucko/spark/sampler/node/AbstractNode.java @@ -30,6 +30,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; /** @@ -45,12 +46,21 @@ public abstract class AbstractNode { private final Map<String, StackTraceNode> children = new ConcurrentHashMap<>(); /** - * The accumulated sample time for this node + * The accumulated sample time for this node, measured in microseconds */ private final LongAdder totalTime = new LongAdder(); - + + /** + * Returns the total sample time for this node in milliseconds. + * + * @return the total time + */ public long getTotalTime() { - return this.totalTime.longValue(); + long millis = TimeUnit.MICROSECONDS.toMillis(this.totalTime.longValue()); + if (millis == 0) { + return 1; + } + return millis; } private AbstractNode resolveChild(String className, String methodName, int lineNumber) { |