Skip to content
Snippets Groups Projects
Commit 201b1c29 authored by Sean Busbey's avatar Sean Busbey
Browse files

Merge pull request #719 from manolama/jvmstats

[core] Add the "measurement.trackjvm" property to measure JVM statistics
parents 5b53485a eea1b485
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,12 @@ class StatusThread extends Thread ...@@ -47,6 +47,12 @@ class StatusThread extends Thread
/** Counts down each of the clients completing. */ /** Counts down each of the clients completing. */
private final CountDownLatch _completeLatch; private final CountDownLatch _completeLatch;
/** Stores the measurements for the run. */
private final Measurements _measurements;
/** Whether or not to track the JVM stats per run */
private final boolean _trackJVMStats;
/** The clients that are running. */ /** The clients that are running. */
private final List<ClientThread> _clients; private final List<ClientThread> _clients;
...@@ -56,8 +62,17 @@ class StatusThread extends Thread ...@@ -56,8 +62,17 @@ class StatusThread extends Thread
/** The interval for reporting status. */ /** The interval for reporting status. */
private long _sleeptimeNs; private long _sleeptimeNs;
/** JVM max/mins */
private int _maxThreads;
private int _minThreads = Integer.MAX_VALUE;
private long _maxUsedMem;
private long _minUsedMem = Long.MAX_VALUE;
private double _maxLoadAvg;
private double _minLoadAvg = Double.MAX_VALUE;
private long lastGCCount = 0;
/** /**
* Creates a new StatusThread. * Creates a new StatusThread without JVM stat tracking.
* *
* @param completeLatch The latch that each client thread will {@link CountDownLatch#countDown()} as they complete. * @param completeLatch The latch that each client thread will {@link CountDownLatch#countDown()} as they complete.
* @param clients The clients to collect metrics from. * @param clients The clients to collect metrics from.
...@@ -67,14 +82,33 @@ class StatusThread extends Thread ...@@ -67,14 +82,33 @@ class StatusThread extends Thread
*/ */
public StatusThread(CountDownLatch completeLatch, List<ClientThread> clients, public StatusThread(CountDownLatch completeLatch, List<ClientThread> clients,
String label, boolean standardstatus, int statusIntervalSeconds) String label, boolean standardstatus, int statusIntervalSeconds)
{
this(completeLatch, clients, label, standardstatus, statusIntervalSeconds, false);
}
/**
* Creates a new StatusThread.
*
* @param completeLatch The latch that each client thread will {@link CountDownLatch#countDown()} as they complete.
* @param clients The clients to collect metrics from.
* @param label The label for the status.
* @param standardstatus If true the status is printed to stdout in addition to stderr.
* @param statusIntervalSeconds The number of seconds between status updates.
* @param trackJVMStats Whether or not to track JVM stats.
*/
public StatusThread(CountDownLatch completeLatch, List<ClientThread> clients,
String label, boolean standardstatus, int statusIntervalSeconds,
boolean trackJVMStats)
{ {
_completeLatch=completeLatch; _completeLatch=completeLatch;
_clients=clients; _clients=clients;
_label=label; _label=label;
_standardstatus=standardstatus; _standardstatus=standardstatus;
_sleeptimeNs=TimeUnit.SECONDS.toNanos(statusIntervalSeconds); _sleeptimeNs=TimeUnit.SECONDS.toNanos(statusIntervalSeconds);
_measurements = Measurements.getMeasurements();
_trackJVMStats = trackJVMStats;
} }
/** /**
* Run and periodically report status. * Run and periodically report status.
*/ */
...@@ -94,6 +128,10 @@ class StatusThread extends Thread ...@@ -94,6 +128,10 @@ class StatusThread extends Thread
long nowMs=System.currentTimeMillis(); long nowMs=System.currentTimeMillis();
lastTotalOps = computeStats(startTimeMs, startIntervalMs, nowMs, lastTotalOps); lastTotalOps = computeStats(startTimeMs, startIntervalMs, nowMs, lastTotalOps);
if (_trackJVMStats) {
measureJVM();
}
alldone = waitForClientsUntil(deadline); alldone = waitForClientsUntil(deadline);
...@@ -102,6 +140,9 @@ class StatusThread extends Thread ...@@ -102,6 +140,9 @@ class StatusThread extends Thread
} }
while (!alldone); while (!alldone);
if (_trackJVMStats) {
measureJVM();
}
// Print the final stats. // Print the final stats.
computeStats(startTimeMs, startIntervalMs, System.currentTimeMillis(), lastTotalOps); computeStats(startTimeMs, startIntervalMs, System.currentTimeMillis(), lastTotalOps);
} }
...@@ -187,6 +228,83 @@ class StatusThread extends Thread ...@@ -187,6 +228,83 @@ class StatusThread extends Thread
return alldone; return alldone;
} }
/** Executes the JVM measurements. */
private void measureJVM() {
final int threads = Utils.getActiveThreadCount();
if (threads < _minThreads) {
_minThreads = threads;
}
if (threads > _maxThreads) {
_maxThreads = threads;
}
_measurements.measure("THREAD_COUNT", threads);
// TODO - once measurements allow for other number types, switch to using
// the raw bytes. Otherwise we can track in MB to avoid negative values
// when faced with huge heaps.
final int usedMem = Utils.getUsedMemoryMegaBytes();
if (usedMem < _minUsedMem) {
_minUsedMem = usedMem;
}
if (usedMem > _maxUsedMem) {
_maxUsedMem = usedMem;
}
_measurements.measure("USED_MEM_MB", usedMem);
// Some JVMs may not implement this feature so if the value is less than
// zero, just ommit it.
final double systemLoad = Utils.getSystemLoadAverage();
if (systemLoad >= 0) {
// TODO - store the double if measurements allows for them
_measurements.measure("SYS_LOAD_AVG", (int)systemLoad);
if (systemLoad > _maxLoadAvg) {
_maxLoadAvg = systemLoad;
}
if (systemLoad < _minLoadAvg) {
_minLoadAvg = systemLoad;
}
}
final long gcs = Utils.getGCTotalCollectionCount();
_measurements.measure("GCS", (int)(gcs - lastGCCount));
lastGCCount = gcs;
}
/** @return The maximum threads running during the test. */
public int getMaxThreads() {
return _maxThreads;
}
/** @return The minimum threads running during the test. */
public int getMinThreads() {
return _minThreads;
}
/** @return The maximum memory used during the test. */
public long getMaxUsedMem() {
return _maxUsedMem;
}
/** @return The minimum memory used during the test. */
public long getMinUsedMem() {
return _minUsedMem;
}
/** @return The maximum load average during the test. */
public double getMaxLoadAvg() {
return _maxLoadAvg;
}
/** @return The minimum load average during the test. */
public double getMinLoadAvg() {
return _minLoadAvg;
}
/** @return Whether or not the thread is tracking JVM stats. */
public boolean trackJVMStats() {
return _trackJVMStats;
}
} }
/** /**
...@@ -474,6 +592,8 @@ public class Client ...@@ -474,6 +592,8 @@ public class Client
*/ */
public static final String DO_TRANSACTIONS_PROPERTY = "dotransactions"; public static final String DO_TRANSACTIONS_PROPERTY = "dotransactions";
/** An optional thread used to track progress and measure JVM stats. */
private static StatusThread statusthread = null;
public static void usageMessage() public static void usageMessage()
{ {
...@@ -553,6 +673,16 @@ public class Client ...@@ -553,6 +673,16 @@ public class Client
exporter.write("OVERALL", "RunTime(ms)", runtime); exporter.write("OVERALL", "RunTime(ms)", runtime);
double throughput = 1000.0 * (opcount) / (runtime); double throughput = 1000.0 * (opcount) / (runtime);
exporter.write("OVERALL", "Throughput(ops/sec)", throughput); exporter.write("OVERALL", "Throughput(ops/sec)", throughput);
exporter.write("TOTAL_GCs", "Count", Utils.getGCTotalCollectionCount());
if (statusthread != null && statusthread.trackJVMStats()) {
exporter.write("MAX_MEM_USED", "MBs", statusthread.getMaxUsedMem());
exporter.write("MIN_MEM_USED", "MBs", statusthread.getMinUsedMem());
exporter.write("MAX_THREADS", "Count", statusthread.getMaxThreads());
exporter.write("MIN_THREADS", "Count", statusthread.getMinThreads());
exporter.write("MAX_SYS_LOAD_AVG", "Load", statusthread.getMaxLoadAvg());
exporter.write("MIN_SYS_LOAD_AVG", "Load", statusthread.getMinLoadAvg());
}
Measurements.getMeasurements().exportMeasurements(exporter); Measurements.getMeasurements().exportMeasurements(exporter);
} finally } finally
...@@ -894,8 +1024,6 @@ public class Client ...@@ -894,8 +1024,6 @@ public class Client
clients.add(t); clients.add(t);
} }
StatusThread statusthread=null;
if (status) if (status)
{ {
boolean standardstatus=false; boolean standardstatus=false;
...@@ -904,7 +1032,9 @@ public class Client ...@@ -904,7 +1032,9 @@ public class Client
standardstatus=true; standardstatus=true;
} }
int statusIntervalSeconds = Integer.parseInt(props.getProperty("status.interval","10")); int statusIntervalSeconds = Integer.parseInt(props.getProperty("status.interval","10"));
statusthread=new StatusThread(completeLatch,clients,label,standardstatus,statusIntervalSeconds); boolean trackJVMStats = props.getProperty(Measurements.MEASUREMENT_TRACK_JVM_PROPERTY,
Measurements.MEASUREMENT_TRACK_JVM_PROPERTY_DEFAULT).equals("true");
statusthread=new StatusThread(completeLatch,clients,label,standardstatus,statusIntervalSeconds,trackJVMStats);
statusthread.start(); statusthread.start();
} }
......
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
package com.yahoo.ycsb; package com.yahoo.ycsb;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.List;
import java.util.Random; import java.util.Random;
/** /**
...@@ -174,4 +178,49 @@ public class Utils ...@@ -174,4 +178,49 @@ public class Utils
public static byte[] doubleToBytes(final double val) { public static byte[] doubleToBytes(final double val) {
return longToBytes(Double.doubleToRawLongBits(val)); return longToBytes(Double.doubleToRawLongBits(val));
} }
/**
* Measure the estimated active thread count in the current thread group.
* Since this calls {@link Thread.activeCount} it should be called from the
* main thread or one started by the main thread. Threads included in the
* count can be in any state.
* For a more accurate count we could use {@link Thread.getAllStackTraces().size()}
* but that freezes the JVM and incurs a high overhead.
* @return An estimated thread count, good for showing the thread count
* over time.
*/
public static int getActiveThreadCount() {
return Thread.activeCount();
}
/** @return The currently used memory in bytes */
public static long getUsedMemoryBytes() {
final Runtime runtime = Runtime.getRuntime();
return runtime.totalMemory() - runtime.freeMemory();
}
/** @return The currently used memory in megabytes. */
public static int getUsedMemoryMegaBytes() {
return (int)(getUsedMemoryBytes() / 1024 / 1024);
}
/** @return The current system load average if supported by the JDK.
* If it's not supported, the value will be negative. */
public static double getSystemLoadAverage() {
final OperatingSystemMXBean osBean =
ManagementFactory.getOperatingSystemMXBean();
return osBean.getSystemLoadAverage();
}
/** @return The total number of garbage collections executed for all
* memory pools. */
public static long getGCTotalCollectionCount() {
final List<GarbageCollectorMXBean> gcBeans =
ManagementFactory.getGarbageCollectorMXBeans();
long count = 0;
for (final GarbageCollectorMXBean bean : gcBeans) {
count += bean.getCollectionCount();
}
return count;
}
} }
...@@ -49,6 +49,9 @@ public class Measurements { ...@@ -49,6 +49,9 @@ public class Measurements {
public static final String MEASUREMENT_INTERVAL = "measurement.interval"; public static final String MEASUREMENT_INTERVAL = "measurement.interval";
private static final String MEASUREMENT_INTERVAL_DEFAULT = "op"; private static final String MEASUREMENT_INTERVAL_DEFAULT = "op";
public static final String MEASUREMENT_TRACK_JVM_PROPERTY = "measurement.trackjvm";
public static final String MEASUREMENT_TRACK_JVM_PROPERTY_DEFAULT = "false";
static Measurements singleton=null; static Measurements singleton=null;
static Properties measurementproperties=null; static Properties measurementproperties=null;
......
...@@ -99,6 +99,19 @@ public class TestUtils { ...@@ -99,6 +99,19 @@ public class TestUtils {
Utils.bytesToDouble(new byte[] { 0, 0, 0, 0, 0, 0, 0 }); Utils.bytesToDouble(new byte[] { 0, 0, 0, 0, 0, 0, 0 });
} }
@Test
public void jvmUtils() throws Exception {
// This should ALWAYS return at least one thread.
assertTrue(Utils.getActiveThreadCount() > 0);
// This should always be greater than 0 or something is goofed up in the JVM.
assertTrue(Utils.getUsedMemoryBytes() > 0);
// Some operating systems may not implement this so we don't have a good
// test. Just make sure it doesn't throw an exception.
Utils.getSystemLoadAverage();
// This will probably be zero but should never be negative.
assertTrue(Utils.getGCTotalCollectionCount() >= 0);
}
/** /**
* Since this version of TestNG doesn't appear to have an assertArrayEquals, * Since this version of TestNG doesn't appear to have an assertArrayEquals,
* this will compare the two to make sure they're the same. * this will compare the two to make sure they're the same.
......
# Copyright (c) 2012 YCSB contributors. All rights reserved. # Copyright (c) 2012-2016 YCSB contributors. All rights reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you # Licensed under the Apache License, Version 2.0 (the "License"); you
# may not use this file except in compliance with the License. You # may not use this file except in compliance with the License. You
...@@ -133,6 +133,16 @@ measurementtype=histogram ...@@ -133,6 +133,16 @@ measurementtype=histogram
# a new output file will be created. # a new output file will be created.
#measurement.raw.output_file = /tmp/your_output_file_for_this_run #measurement.raw.output_file = /tmp/your_output_file_for_this_run
# JVM Reporting.
#
# Measure JVM information over time including GC counts, max and min memory
# used, max and min thread counts, max and min system load and others. This
# setting must be enabled in conjunction with the "-s" flag to run the status
# thread. Every "status.interval", the status thread will capture JVM
# statistics and record the results. At the end of the run, max and mins will
# be recorded.
# measurement.trackjvm = false
# The range of latencies to track in the histogram (milliseconds) # The range of latencies to track in the histogram (milliseconds)
histogram.buckets=1000 histogram.buckets=1000
...@@ -168,4 +178,4 @@ timeseries.granularity=1000 ...@@ -168,4 +178,4 @@ timeseries.granularity=1000
# core_workload_insertion_retry_limit = 0 # core_workload_insertion_retry_limit = 0
# #
# the following number controls the interval between retries (in seconds): # the following number controls the interval between retries (in seconds):
# core_workload_insertion_retry_interval = 3 # core_workload_insertion_retry_interval = 3
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment