Measuring UI Responsiveness

Author: Tomas Pavek
$Revision: 1.1.1.1 $
$Date: 2009/10/29 16:50:16 $
Document history: available in CVS

Goal

Be able to measure exactly the time between invocation of a user action and finishing the action in the IDE. Typical user actions are

  • invoking menu (pull-down, pop-up, or submenu),
  • opening file,
  • invoking dialog or window,
  • expanding tree, etc.

It is desirable to have one general way to measure all kinds of actions. It should be simple to use and should not affect performance of the application.

Technique

User actions are triggered mostly by pressing a mouse button or a key. The action can be considered as finished after some painting is done and the program is responsive (idle).

So technically, we can watch for appropriate events to get the time when user action begins. Then we may watch the event queue (to see when it becomes idle) and painting events (whether some occurred during processing the event queue). Knowing the time between user event and event queue emptying, we have a good approximation of the response time. How can this be implemented?

This kind of measuring can be done easily by hacking the JDK classes directly (the sources are bundled with the JDK download). Full diffs are available here: for JDK 1.4.0, for JDK 1.4.1 and for JDK 1.4.2.

We start with java.awt.EventQueue, adding some fields:

    public static long firstEventTime;
    public static long userEventTime;
    public static boolean paintOccurred;
    private static int ncount;

Then we hack getNextEvent method. When the EventQueue starts dispatching events (after it was waiting on some), we remeber the current time:

    firstEventTime = System.currentTimeMillis();

To recognize user event, we may use the following code executed before getNextEvent returns:

    int id = eqi.event.getID();
    if (id == 501 || id == 502 || id == 401) {
        System.out.println("-> user event ["+id+"]");
        userEventTime = firstEventTime;
    }

(501 is whatever mouse down, 502 mouse up, 401 means pressed key.)

Note that we actually don't use the time of event creation, but the time of starting dispatching the events in EventQueue. More accurate would be to use e.g. InputEvent.getWhen() for getting the time, but it turned out it was not reliable on some systems. Using the event queue start dispatching time instead is a good approximation, differing just about in several milliseconds.

To watch whether some paint event occurred, we can simply hack JComponent.paint method by adding:

    java.awt.EventQueue.paintOccurred = true;

The rest is just to print results at moment the event queue is done and going to wait:

    if (paintOccurred && userEventTime > 0) {
        paintOccurred = false;
        long time = System.currentTimeMillis();
        long diff = time - userEventTime;
        if (diff > 40000)
            userEventTime = 0;
        else
            System.out.println(Integer.toString(++ncount)+". paint done "+diff);
    }

(Note we use a counter variable to number the painting events for better orientation in the output.)

To sum up, for any operation processed in event queue and painting something, we get the elapsed time from last user event.

There is actually one more thing to do: recognizing menu selection as user event. It is user event because it may invoke a submenu. But it may be triggered just by moving the mouse, not pressing anything -- so it is not covered by the code above. We may hack JMenu and JMenuItem by adding following code to their menuSelectionChanged method:

    if (java.awt.EventQueue.firstEventTime > java.awt.EventQueue.userEventTime) {
        java.awt.EventQueue.userEventTime = java.awt.EventQueue.firstEventTime;
        System.out.println("-> user event [menu]");
    }

(Note we must hack both, as JMenu does not call super.)

Usage

  • Compile the four changed classes (get diffs for JDK 1.4.0 or for JDK 1.4.1).
  • Pack the class files into a zip file, keeping the right package structure inside.
  • Modify netbeans.conf file (located in etc directory of NetBeans) -- add the following line to it (without the < > brackets):
    -J-Xbootclasspath/p:<path_to_that_zip_file>
  • Run the IDE.
  • Try some operations with mouse and keyboard, check the console to get used to the output.

Note that for operations triggered by "mouse down" event (like selecting nodes, expanding trees, invoking pull-down main menus), you should keep the mouse pressed until the operation is finished (so the "mouse up" event does not kick in in the middle).

Please send any comments to nbdev.

Project Features

About this Project

Performance was started in November 2009, is owned by tpavek, and has 27 members.
By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20160708.bf2ac18). © 2014, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
 
 
Close
loading
Please Confirm
Close