Related link: http://www.topologi.com
My company, Topologi
is releasing our new product line next week. This isn’t really a pitch for the products (repeat after me “I will try! I will buy!”) but more about the particular solutions I have come up with to making a large desktop application with Java that has just as good performance as a native Windows application.
Suckitude
Now, let me start off by saying I don’t mean good performance in the benchmarking sense: Java programs have, if well written, just as good performance as C++ programs when they are up and running. Indeed, as Java uses SSE instructions now, benchmark performances may exceed typical C++ binaries compiled without SSE.
But a real desktop application is competitive with other resources: in particular with other applications and with the Windows virtual memory system. Users have multiple applications running, they minimise them, or quickly switch between them. Lets call poor performance in these areas suckitude.
We faced two sets of problems. The first were issues with Windows and Java:
- Slow start-up times: our application uses a ton of libraries, and even though we did lazy initialization, start-up time was just too much. The initial prototypes of the product had used custom class loaders to reduce the problem, but we abandoned them because they didn’t fit.
Instead we had tried a little reflection, lazy intitialization, and invoking many methods through BeanShell scripts.
- Java didn’t know how much memory was optimally available. It just tries to reserve as much as it can up to some limit.
- Java GC does not interact particularly happily with paging. You don’t want too multiple JVMs running unless they have quite small heaps, but if they have small heaps they are useless for decent-sized work.
- Java scheduling and memory management does not have any idea of foreground/background. They don’t have any way to act differently when the Java application is not the foreground application or when the application is minimised. Consequently, it has no built-in way to be a good citizen.
The second came from issues relating to the size of our application. The Professional Editions contained a ton of functionality, but
- Download times were becoming too long because of
the large file size. We had found we had to include our
own JRE because we couldn’t be sure what JRE the user’s
had on their systems.
- Users often bought the system for specific funtionality
- Large WIMP applications are only
“
>intuitive”
for habituated power-users (sometimes called
“the Adobe syndrome” unfairly: Adobe’s interfaces
are exemplorary for reducing this problem that
any—and everyone’s—large
application faces,
in particular by moving beyond WIMP to
using floating pallettes and differentiated products,
backed up by their training programs).
Users wouldn’t look more than one menu deep to
find anything, and wouldn’t look things up in
help files even when the help was there.
Inductive, Task-Oriented GUI Design
It is that last issue that caused the lightbulb.
A few years back the practicality boffins at MicroSoft, in particular, started espousing
"http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/iuiguidelines.asp"
>inductive user interfaces.
href="http://mail.gnome.org/archives/hig/2002-March/msg00029.html"
>Jan Miksovsky says
“ I think
its most significant contribution is … that tight
constraints around the clarity of a page title can drive an interface’s
design.”
and
“The answer proposed by the inductive UI model is that a single task is something that can be stated
(ideally, directly on the screen) in a single concise question or
statement in natural language.“.
MicroSoft since have since moved on to
personas,
see this blog conversation:
>Who are you? Mort, Elvis or Einstein
>Inductive vs Productive UIs, and
>a response.
This is probably a logical step: when you need to
elaborate tasks, you have to ask “who” before “what.”
The cynic in me wonders whether inductive interfaces
are too thin-client friendly for MS, IYKWIM?
(An check out
>this great article
to see why personas won’t work for tomato sauce.)
Inductive interfaces, wizards, typical multi-stage web-based interfaces are all task-based.
Task-Orientedness as an Architecture
The issue I set myself was
Is there a way to re-factor our existing functionality
to reduce suckitude to the same limits as native Windows
applications?
The solution I came up with was to refactor our
application as a suite of independent utilities.
Rather than kick against the pricks
of Java’s applet/servlet focus, why not accept it
and love it:
use it as the impetus for a more task-oriented
user interface?
The utilities
each group together three to six related tasks.
The utilities are
independent in the sense that they each can
be downloaded independently, but
each utility can launch another in the
same JVM, using a pulldown menu on the toolbar.
They are virtual utilities: they
look like independent programs but they
run in the same Windows process.
The exemplar was WinZIP: a simple
interface with a small number of toolbar buttons.
Each button would be an inductive-style
verb+type statement. We threw out menus,
though the toolbars ended up a little
menu like.
Each utility launches with its own class-loader.
Each utility has a “Lauch Utility” button that
invokes whichever other Topologi utilities have
been installed: if it is the first launch of that
utility in the current JVM, the utility is
launched by its own class loader.
Closing the window of a utility does not cause
the destruction of the utility’s Java object: instead it
acts like a smart window-minimization. The utility
frees up as many objects as possible (trees,
files, etc) and hides the utility window.
Next time the utility is launched from another
utility, it comes up almost instantly.
Results
So here is how the problems fared:
- Slow start-up times:
The initial start-up time is less, because
only the libraries needed for the utility
need to be loaded.
The time to launch a new utility from an existing one
is smaller, because there is no need to launch a new
JVM.
Indeed, if the utility was previously launched then
closed but there was always some utility keeping the JVM
alive, it may open instantly.
- Java didn’t know how much memory was optimally available.
I blogged last year about this: the utilities are invoked
through a executable which calculates the optimal
heap.
- Java GC does not interact particularly happily with paging.
Using the same JVM rather than launching
others reduces the pressure that active pages will be
paged out.
- Java scheduling and memory management does not have any idea of foreground/background.
Again, by keeping everything in one JVM, we
reduce the liklihood of
- Download times:
Each utility is substantially smaller:
often less than 20% of the size of the Professional
Edition. Just as helpfully,
we figured that by now most people have upgraded
to JRE 1.4.2 or JRE 1.5, so we didn’t need to
rebundle the JRE.
- Users often bought the system for specific funtionality:
Obviously the utility idea caters for this well.
- Large WIMP applications are not “intuitive”
Removing the menus, and giving a clear, small set
of tasks on the buttons makes the interface easier
to figure out.
We also simplified the help files and made them
available in the first tab.
In our performance goals from three years before,
we had a launch limits of 4 seconds to get a splash
screen, 4 second to get the application frame and
main menu, and 4 seconds to get the main GUI
items functional (4:4:4), on our target PC.
We had gone pretty well, but
load times were hindering us, and one prototypes
of a recent product we had degraded closer to 8:8:16,
which represented high suckitude. The utilities are
each about 1:3:2 for the first one launched,
0:2:2 for the initial fast lauch of one utility from
another,
and 0:0:0 for subsequent fast launch times. Yahoo.
It is a good architecture: it matches Java’s and Window’s
natures quite well. To actually spit up the Topologi
Professional Edition completely along these lines would
take at least 20 utilities, so we started off with 5
and enabled fast launch to the simpler Mark Editor
product,
and with several more utilities in the pipeline.
In one sense, what we are doing is moving the top-level menus to the Windows Start menu. This in effect moves up all the underlying interace objects up one level. Users select the utility (which groups related tasks) and then have the tasks staring them in the face, rather then hidden under one or two layers of menus. Similarly when switching to help files the user sees the help belonging to the tasks, rather than amalgamated help for all functionality, which requires searching and head-scratching.
The limitation currently is that fast-launching only
works from inside a utility. If you have one utility open
and launch another from the Start menu, say, you get two JVMs. I may look at having our custom launchers
find an existing JVM before launching a utility in a new one, and there may be some way to use the JDIC Windows Quicklaunch Toolbar to keep the JVM alive, but that is down the road.
I don’t know whether moving to a task-oriented, utility/mini-program approach will work on your application, but I certain commend it. It makes Java applications feel like different beasts.
Java experts often recommend custom class loaders as
the way to reduce initial loadtime. What no-one (who I have
read, at least) suggested was what I found: that to use
class-loaders successfully you have to design your program so that the user
interacts in chunks of related tasks, rather than the WIMP approach of having most functions theoretically available at the same time (which can happen as a result of removing modality from a WIMP interface, for example, in HCI jargon).
Grouped Tasks -> Virtual Utilities -> Fast Loading & smaller size -> good suckitude