package org.sef4j.callstack.stats;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import org.junit.Assert;
import org.junit.Test;
import org.sef4j.callstack.ThreadCpuTstUtils;
public class ThreadTimeUtilsTest {
private static final long prec = 40 * 1000*1000; // precision ~40ms, in nanos
@Test
public void testCheckThreadCpuEnabled() {
// Prepare
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
threadMXBean.setThreadCpuTimeEnabled(false);
// Perform
ThreadTimeUtils.checkThreadCpuEnabled();
// Post-check
Assert.assertTrue(!threadMXBean.isThreadCpuTimeSupported() || threadMXBean.isThreadCpuTimeEnabled());
}
@Test
public void testGetTime() throws InterruptedException {
// Prepare
long sleepTime = 50;
// Perform
long time1 = ThreadTimeUtils.getTime();
Thread.sleep(sleepTime);
long time2 = ThreadTimeUtils.getTime();
// Post-check
Assert.assertTrue(time1 <= time2);
assertApproxEquals(time1 + sleepTime, time2, 2*prec);
}
/**
* compute diff between successive call to getTime()
* => interpret as precision + speed of loop
* display as statistical histogram :
* <PRE>
diff count
15 0.003701
16 0.056233
17 0.201642
18 0.445661
19 0.185245
20 0.036909
21 0.034543
22 0.01548
23 0.005571
24 0.003046
25 0.002344
* </PRE>
*
* Got asymmetric "gaussian" curve, centered at ~18 nanos
* in range from ~15 to ~30 nanos + seldom values above (gc, thread swap, ..)
* consecutive GetTime() are at least 15 nanos diff
* <PRE>
* count
* ^ .
* |
* | . .
* | . .
* | . .
* +------------------------> diff
* 15 30
* 18
* </PRE>
*
*/
@Test
public void testGetTime_precision() throws Exception {
int maxDiff = 100000;
int repeat = 1000000;
doReapeatAccumulateTimeDiff(maxDiff, repeat); // first call fro hostspot
int[] countPerDiff = doReapeatAccumulateTimeDiff(maxDiff, repeat);
StringBuilder sb = new StringBuilder();
StringBuilder csvSb = new StringBuilder();
csvSb.append("diff,count\n");
for(int i = 0; i < maxDiff; i++) {
if (countPerDiff[i] != 0) {
sb.append(i + ":" + countPerDiff[i] + ", ");
if (countPerDiff[i] > 2) { // apply small threshold...
double proba = ((double)countPerDiff[i])/repeat;
csvSb.append(i + "," + proba + "\n");
}
}
}
// FileUtils.write(new File("target/precision-GetTime.csv"), csvSb.toString());
System.out.println("histogram for precision diff of consecutive getTime() in nanos:" + sb.toString());
}
private static int[] doReapeatAccumulateTimeDiff(int maxDiff, int repeat) {
int[] countPerDiff = new int[maxDiff];
long prevTime = ThreadTimeUtils.getTime();
for (int i = 0; i < repeat; i++) {
long time = ThreadTimeUtils.getTime();
long diff = time - prevTime;
if (0 < diff && diff < maxDiff-1) {
countPerDiff[(int)diff]++;
} else if (diff >= maxDiff){
countPerDiff[maxDiff-1]++;
} else if (diff < 0) {
// should not occur!
} else {
// out of range.. ignore...
}
prevTime = time;
}
return countPerDiff;
}
@Test
public void testGetCurrentThreadCpuTime_sleep() throws InterruptedException {
// Prepare
long sleepTime = 30;
// Perform
long time1 = ThreadTimeUtils.getCurrentThreadCpuTime();
Thread.sleep(sleepTime);
long time2 = ThreadTimeUtils.getCurrentThreadCpuTime();
// Post-check
Assert.assertTrue(time1 <= time2);
assertApproxEquals(time1, time2, prec);
}
@Test
public void testGetCurrentThreadCpuTime_cpu() throws InterruptedException {
// Prepare
long expectedCpuTime = 80;
long cpuLoop = ThreadCpuTstUtils.cpuLoopCountForMillis(expectedCpuTime);
// Perform
long time1 = ThreadTimeUtils.getCurrentThreadCpuTime();
ThreadCpuTstUtils.cpuLoop(cpuLoop);
long time2 = ThreadTimeUtils.getCurrentThreadCpuTime();
// Post-check
Assert.assertTrue(time1 <= time2);
long expectedTime2 = time1 + ThreadTimeUtils.millisToNanos(expectedCpuTime);
assertApproxEquals(expectedTime2, time2, prec);
}
private static final int CST_M = 1000*1000;
private static final int CST_APPROX_M = 1024*1024;
private static final long MILLIS_TO_NANOS = 1000000;
private static final long CST_ANY = 40000;
@Test
public void testBitShiftApproxMillisToNanos() {
Assert.assertEquals(1 << 20, CST_APPROX_M);
}
@Test
public void testNanosToApproxMillisToMillis() {
Assert.assertEquals(CST_ANY-2, //round...
ThreadTimeUtils.approxMillisToMillis(ThreadTimeUtils.nanosToApproxMillis(CST_ANY * MILLIS_TO_NANOS)));
Assert.assertEquals(2*CST_ANY-1, //round...
ThreadTimeUtils.approxMillisToMillis(ThreadTimeUtils.nanosToApproxMillis(2*CST_ANY * MILLIS_TO_NANOS)));
Assert.assertEquals(3*CST_ANY-1, //round...
ThreadTimeUtils.approxMillisToMillis(ThreadTimeUtils.nanosToApproxMillis(3*CST_ANY * MILLIS_TO_NANOS)));
}
@Test
public void testNanosToApproxMillis() {
Assert.assertEquals(CST_M, ThreadTimeUtils.nanosToApproxMillis(CST_APPROX_M * MILLIS_TO_NANOS));
Assert.assertEquals(1, ThreadTimeUtils.nanosToApproxMillis(CST_APPROX_M));
}
@Test
public void testNanosToMillis() {
Assert.assertEquals(1, ThreadTimeUtils.nanosToMillis(MILLIS_TO_NANOS));
}
@Test
public void testMillisToNanos() {
Assert.assertEquals(MILLIS_TO_NANOS, ThreadTimeUtils.millisToNanos(1));
}
@Test
public void testApproxMillisToMillis() {
long approxMillis = ThreadTimeUtils.nanosToApproxMillis(CST_ANY * MILLIS_TO_NANOS);
Assert.assertEquals(CST_ANY-2, //approx
ThreadTimeUtils.approxMillisToMillis(approxMillis));
//?? Assert.assertEquals(CST_ANY*CST_M, ThreadTimeUtils.approxMillisToMillis(CST_ANY*CST_APPROX_M));
}
@Test
public void testApproxMillisToNanos() {
// Assert.assertEquals(CST_M * MILLIS_TO_NANOS, ThreadTimeUtils.approxMillisToNanos(CST_APPROX_M));
Assert.assertEquals(CST_APPROX_M, ThreadTimeUtils.approxMillisToNanos(1));
}
public static void assertApproxEquals(long expectedValue, long actualValue, long prec) {
if (! (expectedValue - prec <= actualValue)) {
Assert.fail("expected " + expectedValue + " ~ " + actualValue + " +/-" + prec + " .. got diff:" + (actualValue-expectedValue));
}
if (! (actualValue <= expectedValue + prec)) {
Assert.fail("expected " + expectedValue + " ~ " + actualValue + " +/-" + prec + " .. got diff:" + (actualValue-expectedValue));
}
}
}