Velocity and Acceleration for MBean Attributes

Paul Brown @ 2006-09-05T05:13:00Z

Testing is great, but instrumentation is both critically important and often ignored. After all, failures always come from untested scenarios.

Basic instrumentation (like heap data) is good, but basic instrumentation is also a bit like taking a picture of a race car. Other than from context, it's impossible to know whether it's going 200MPH or just parked in the middle of the racetrack. For example, the average velocity of a response time parameter indicates whether response times are getting longer or shorter or staying about the same, and the average rate of change of the average rate of change indicates whether a linear extrapolation is likely to miss high or low.

Here's a simple approach to get thumbnail rates of change for a stream of values; I'll explain how to integrate it with JMX a little later. The first ingredient is a useful little utility class that computes the moving average of a stream of long values. (Clearly the values can't be that large, ideally no larger than MAX_LONG/2*size.)

public class CircularWindow {
  private long sum;
  private long last;
  private long[] x;
  private int ptr;
  private int size;
  private boolean full;

  public CircularWindow(int size) {
    x = new long[size];
    this.size = size;
  }

  public void add(long y) {
    sum += (last = y) - x[ptr];
    x[ptr] = y;
    if (++ptr == size) {
      ptr = 0;
      full = true;
    }
  }

  public long average() {
    return full?(sum/size):((ptr!=0)?(sum/ptr):sum);
  }

  public long last() {
    return last;
  }
}

It is built with the assumption that the average will be requested frequently but not necessarily every time a value is added. (If the average was requested infrequently versus the size of the buffer, it would make more sense to compute the sum only when the average was requested.)

To get at rates of change, chain up two of the windows and add values like so:

D_s.add(value - s.last());
s.add(value);

Now, what does this do? For a hint, try this:

CircularWindow x = new CircularWindow(100);
CircularWindow y = new CircularWindow(100);
CircularWindow z = new CircularWindow(100);
for (long i=0; i < 50000; ++i) {
  long value = i*i;
  z.add(value - y.last() - x.last());
  y.add(value- x.last());
  x.add(value);
  if (i > 49990) {
    System.out.println("x av = " + x.average());
    System.out.println("y av = " + y.average());
    System.out.println("z av = " + z.average());
  }
}

The series y ticks up two every time, and the z series is constant 2. It looks like the cleverly-named D_s series from above approximates the first derivative of the s series, and presuming that the values in the series come from a suitable well-behaved differentiable function, that's obvious if you write it out:

D_s(n+1) ~ (s(n+1) - s(n))/((n+1) - n) = s(n+1) - s(n)

The main window and the second window for tracking the rate of change can be encapsulated conveniently:

public class VelocityWindow {
  private CircularWindow s;
  private CircularWindow D_s;

  public VelocityWindow(int size) {
    s = new CircularWindow(size);
    D_s = new CircularWindow(size);
  }

  public void add(long x) {
    D_s.add(x - s.last());
    s.add(x);
  }

  public long average() {
    return s.average();
  }

  public long velocity() {
    return D_s.average();
  }

  public long ticksToClear() {
    if (D_s.average >= 0) return -1;
    return s.last() / D_s.average();
  }
}

The fully reusable path for integration into an application's JMX instrumentation is via a MonitorMBean implementation. A MonitorMBean typically uses a timer (e.g., a TimerTask) to sample an attribute and then either exposes attributes or fires notifications. (The GaugeMonitor is similar in flavor but not really the same thing.)

I haven't ever gotten around to taking the MonitorMBean approach, since the quick and dirty path is simply to embed the VelocityWindow directly in the MBean and expose average and velocity as attributes. Another derived metric that is useful when tracking backlogs is ticks to clear, which would either be infinite if D_s.average() is non-negative or -1*s.last()/D_s.average() otherwise.

(comment bubbles) 1 comment

Unclear on the Concept

Paul Brown @ 2006-09-05T03:56:00Z

I got a SPAM this weekend for "Blogger and Podcaster Magazine". In all likelihood, this is just a case of someone identifying a demographic where the advertising revenues from a magazine could exceed the cost of printing and mailing a bunch of free copies, but this strikes me as a case of unclear on the concept. A hard copy, snail-mailed magazine targeted at people who are deeply involved in blogging, podcasting, and other purely on-line activities? That's just the kind of content that you can't find on-line... [sic.]

I don't get it, but maybe that's just me.

(comment bubbles) 0 comments

Devil's Definition of Kickboxing

Paul Brown @ 2006-08-30T04:15:44Z

Another entry for my personal Devil's Dictionary:

kickboxing, n. A programming language idiom whereby a kick is converted to a Kick.
(comment bubbles) 0 comments

No Pyrokinetic Powers

Paul Brown @ 2006-08-29T04:47:00Z

The kid is a teething machine, by which I mean that she has quite a few teeth for a sixteen-month-old, and she's getting more with her top and bottom canines on their way in and then only her four back molars left. Combine this with a normal amount of toddler testiness, and you've got a recipe for some pretty good outbursts. This past weekend, we were having lunch, and her mood came to a head. She picked up her plate with clear intent to throw it, but I caught her in mid wind-up. Her response to a stern "No." was a seething look of anger that turned her whole face bright red.

After she was cleaned up and running around, my wife commented, "It's good that she doesn't have pyrokinetic powers, or she'd have burned you into a little potato chip." Nonetheless, I'll be on the lookout when she hits puberty...

(comment bubbles) 0 comments

Open Source Java. Yawn.

Paul Brown @ 2006-08-20T20:55:00Z

It looks like SUN's serious about open sourcing Java, where Java means the JDK. Among the various languages that I use, I could count the number of times that I've looked at the source code for Ruby, Python, Perl, GCC, GCL, and GHC on the fingers of an absent-minded stonemason, and almost all of those times have been when I wanted to compile something bleeding edge on an obscure operating system. Diving into a highly complex codebase and making positive changes simply isn't done. The SUN JVMs are already open enough for my taste. The JVMTI is available as a plug-in point, and the source code for the Java standard libraries is available.

Will "open source Java" prevent cruft from getting into the standard libraries for Java or evict some of the current dross and detritus? Will it save us from the next EJB or Crimson? Strip the Java standard libraries down to a minimal subset of what they currently contain, don't put it back, provide a great package manager, and that's open enough for me.

(comment bubbles) 0 comments

If Yogi Berra Managed Software Projects

Paul Brown @ 2006-08-19T06:23:08Z

If Yogi Berra managed software projects, he would say things like what I heard on a conference call today:

Are there any risks you're not aware of that could impact the "go-to-staging" date?
(comment bubbles) 0 comments

Rule of Thumb for Throws

Paul Brown @ 2006-08-18T06:13:00Z

The subject of when to use the throws declaration on a method definition in Java came up in a discussion recently, and my personal preference can be expressed in one simple rule:

An exception belongs in the throws declaration if and only if it is actually thrown in the body of the method.

In fact, it's so simple that it can be expressed in PMD 's XPath syntax:

//MethodDeclaration/NameList/Name[\
@Image != ../../Block//ThrowStatement//\
AllocationExpression/ClassOrInterfaceType/@Image]

(The backslashes are linebreaks and not part of the actual expression.) The expression might look a little odd, but it works because a string (a member of the throws declaration) and a nodeset (all of the throw new Foo in the method body) are equal according to XPath 1.0 if the nodeset contains a node with the same string value as the string.

(comment bubbles) 2 comments

All Posts contains 399 items in 57 pages of 7 items each:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57