Extending StopWatch's mathiness

June 24, 2009

On occasion when running a series of tests, it is handy to have data about the average, standard deviation, minimum, and maximum run times of the series. I find this particularly useful when performing extensively parallel thread testing, and need to collect metrics for analysis.

These features would fit well in the org.springframework.util.StopWatch class, and could be implemented as follows:

public double getAverageTaskTimeMillis() throws IllegalStateException {
    if (this.taskCount <= 0) {
        throw new IllegalStateException(
                "Can't calculate average task time: StopWatch hasn't run");
    }
    long sum = 0;
    for (TaskInfo taskInfo : (List<TaskInfo>) this.taskList) {
        sum += taskInfo.getTimeMillis();
    }
    return (double) sum / this.taskCount;
}

public double getTaskTimeMillisStandardDeviation()
        throws IllegalStateException {
    if (this.taskCount <= 0) {
        throw new IllegalStateException(
                "Can't calculate task time standard deviation: StopWatch hasn't run");
    }
    long stdDev = 0;
    double average = this.getAverageTaskTimeMillis();
    for (TaskInfo taskInfo : (List<TaskInfo>) this.taskList) {
        stdDev += Math.pow(taskInfo.getTimeMillis() - average, 2);
    }
    return Math.sqrt(stdDev / this.taskCount);
}

public long getMinTaskTimeMillis() throws IllegalStateException {
    if (this.taskCount <= 0) {
        throw new IllegalStateException(
                "Can't calculate average task time: StopWatch hasn't run");
    }
    long min = ((List<TaskInfo>) this.taskList).get(0).getTimeMillis();
    for (TaskInfo taskInfo : (List<TaskInfo>) this.taskList) {
        min = Math.min(min, taskInfo.getTimeMillis());
    }
    return min;
}

public long getMaxTaskTimeMillis() throws IllegalStateException {
    if (this.taskCount <= 0) {
        throw new IllegalStateException(
                "Can't calculate average task time: StopWatch hasn't run");
    }
    long max = ((List<TaskInfo>) this.taskList).get(0).getTimeMillis();
    for (TaskInfo taskInfo : (List<TaskInfo>) this.taskList) {
        max = Math.max(max, taskInfo.getTimeMillis());
    }
    return max;
}

I have tested the above code with the following added to org.springframework.util.StopWatchTests.testValidUsage():

    assertTrue("average", sw.getAverageTaskTimeMillis() >= 105.5
            && sw.getAverageTaskTimeMillis() <= 111);
    assertTrue("standard deviation", sw
            .getTaskTimeMillisStandardDeviation() >= 55.5
            && sw.getTaskTimeMillisStandardDeviation() <= 65.5);
    assertTrue("min", sw.getMinTaskTimeMillis() >= 45L
            && sw.getMinTaskTimeMillis() < 166L);
    assertTrue("max", sw.getMaxTaskTimeMillis() >= 166L);

I have submitted this as SPR-5864.