[Contents] [Prev] [Next] [Index]

CHAPTER 2 - The Performance Process

Producing fast software is nontrivial. Performance isn't a single step in the software development process; it needs to be a part of your overall software development plan. Achieving maximum performance from your software requires continued effort throughout all phases of development, not just coding.

This chapter introduces a performance process that is based on standard object-oriented software development methodologies. This process includes the standard phases of a software development cycle and adds one additional phase, performance profiling.

If you're interested in learning more about object-oriented design (OOD) and the overall software development process, the resources listed in Section 2.2 are a good place to start.

2.1 Developing a Performance Process

The processes that have been developed for object-oriented software development (OOSD) are applicable to the problem of producing high-performance software. It turns out that solid analysis and design is a crucial part of the performance process. If you don't have a good analysis of the problem you're modeling, it's nearly impossible to produce a solid software design. Without a solid software design, it's nearly impossible to achieve good performance through optimizations alone.

The basic OOSD process has four main phases:

Figure 2-1 shows the steps in the performance process. These mirror the phases of the traditional OOSD model, with one addition. This new phase is performance profiling-determining the performance characteristics of a software system.
The performance process

Popular OOSD methodologies define particular tasks to be completed during each development phase. As we discuss each phase, we'll introduce some additional performance-related tasks that are critical to producing high-performance software. These complement, rather than replace, those required by the OOSD methodologies.

2.1.1 Analysis

Analysis is the first stage of the development process. It is also the first step in producing high-performance software. Analysis is central to the performance tuning process. During this phase of the development process, you shouldn't worry about low-level issues like language, syntax, classes, methods, or data structures.

Different methodologies specify different deliverables for the analysis phase. One of these is usually a software requirements specification (SRS). The SRS specifies exactly what your software is supposed to do. It generally includes a high-level analysis of the problem domain and a set of scenarios that illustrate key functionality. This information is required input to the design phase of the project.

From a performance perspective, there are some key pieces of information that need to be included in the SRS. One key item that should be included in your SRS is a set of use cases. Use cases are descriptions of the sequence of events and actions that occur when a user interacts with the system. They are used to describe the outwardly visible requirements of a system.1 These descriptions can be textual, or might include diagrams. Use cases are an important step in defining the conceptual model of your system, and they are also crucial for creating testing plans and documentation. The major reason to be interested in them from a performance perspective, however, is benchmarking. In general, use cases can make an excellent way to determine what types of benchmarks you should create to measure the performance of your system. By basing your benchmarks on use cases, you can better ensure that your benchmarks are representative of the real-world use of your system. See Chapter 3, Measurement Is Everything, for more information about benchmarking.

In addition to the information about what the software is going to do, the SRS should specify planned limitations-what your product definitely will not do. What parts of the problem domain will the product not address? This information is very important-a product cannot be all things to all people. Systems that try to do everything generally fail. Well-defined boundaries on your software's scope can open up big optimization opportunities during the design and coding phases.

The SRS must also go beyond just software requirements; it needs to include system and performance requirements. System requirements include information such as:

You'll also want to know whether other software is likely to be running at the same time and anything else that is peculiar to your target environment. The more information you have about your target environment, the better.

Once you have your basic system requirements you can define specific performance requirements. Performance requirements are the most useful when they are quantifiable. For example:

It's not always possible to quantify all of an application's performance requirements, especially for user interface code. If a goal can't be effectively quantified, qualitative requirements are much better than no requirement at all. For example:

Too often, projects move from analysis, to design, to coding, and on to beta testing before the engineering team knows what the performance requirements are. The team might have the system working well in the lab on a 500MHz machine, only to find that the target customer will be using a 200MHz machine. Before you start, engineering, management, marketing, and ideally your customers should reach a consensus about the performance requirements for your system. If you don't, how will you know when your product is fast enough?

2.1.2 Object-Oriented Design

Object-oriented design plays a major role in the performance process. While there are many factors that contribute to a good design, and all of them are important, there is one concept that is especially critical to creating high-performance systems. This concept is called encapsulation. In his book Applying UML Patterns, Craig Larman defines encapsulation as "a mechanism used to hide the data, internal structure and implementation details of an object. All interaction with an object is through a public interface of operations."2

Encapsulation encourages you to isolate the internal structure of your objects from the rest of your program. Generally, encapsulation is discussed in the context of improved software maintenance-not improved performance. In fact, there is a general perception among some programmers that encapsulation hurts performance by requiring greater data indirection and more method calls. While it is true that the increased levels of indirection lead to some slowdowns when compared to theoretical maximum performance levels, encapsulation is essential in large, scalable, high-performance systems.

Making encapsulation part of your design from the start enables you to:

To understand how encapsulation helps in the evaluation of algorithms and data structures, let's look at an example from the Java 2 Collections Framework. The Collections Framework provides an abstraction for the concept of a List. The framework also provides several implementations of this abstraction including LinkedList and ArrayList. These are all discussed in greater detail in Chapter 8, Algorithms and Data Structures, but for now all you need to know is that these two classes have radically different performance characteristics even though they implement the same interface. Neither class is the fastest for all tasks-either can be several times faster than the other, depending on the types of operations that are performed on the List.

The increased indirection caused by the encapsulation of the list data-structures does mean that more method invocations are used to carry out operations than if the internal structures were exposed. However, this is a very small performance decrease-in typical cases, only a few percent. Contrast this with the possibility that switching from an array-based list to a linked list might result in a tenfold speedup of a critical operation in your system. If you expose the raw data structures everywhere in your program, you might have to modify hundreds of lines of code just to find out which solution is fastest. On the other hand, if you use encapsulation, you might only need to change one line of code to try the alternative solution.

A similar case is found in the Swing3 model classes. Chapter 10, Swing Models and Renderers, shows how the use of encapsulation makes it possible for components such as JTable to scale to accommodate huge data sets.

These cases both show how the use of encapsulation inside the Java libraries is important. However, it is equally important to apply these concepts when designing your own classes.

Using encapsulation makes it easier to adapt to changing requirements. This is obviously important for system maintenance, but it is also critical for system performance. For example, if your initial SRS states that your system has to be able to handle up to 100 users. you might decide to store user data in flat files. If the system is so well-received during beta testing that management decides to roll it out companywide to 10,000 users, your flat-file system is probably going to become a performance bottleneck.

To eliminate the bottleneck, you need to rework the system to use an industrial-strength database to store user data. If you encapsulated your data-storage mechanism inside a set of well-designed abstract classes, moving from the flat-file structure to the database should be fairly easy. However, if the storage mechanism isn't well encapsulated and many parts of your system assume that the user data is stored in files, the rearchitecture could take a significant amount of development time.

2.1.3 Coding

Clearly, the way you write your code has an impact on how your system performs. Part II of this book shows numerous examples of how similar pieces of code can have radically different performance characteristics. However, it is important to remember that it is easy to spend a lot of time tweaking parts of your system to improve performance, even though they might not be performance-critical. Always remember that analysis, design, and profiling are at least as important as the actual coding phase of the process.

2.1.4 Testing

Quality assurance is an important part of the software development process. In this context, however, we want to focus on performance testing rather than quality testing. Performance testing is really about benchmarking. Once you have a version of your system running (even a partial system), you can begin to construct benchmarks to measure its performance. These benchmarks should be based on your performance requirements. Having a solid set of benchmarks allows you to track your progress over time and see where you are with regard to your requirements. Meeting or exceeding your performance requirements should be part of the shipping criteria for your product. Chapter 3 describes benchmarking in more detail.

2.1.5 Profiling

Many systems don't meet all of their performance requirements on the first try. Once you've determined that a performance problem exists, you need to begin profiling. Profiling determines what areas of the system are consuming the most resources. Many tools are available to help you with this process. Profilers are most useful for identifying computational performance and RAM footprint issues.

By analyzing data from a profiler, you can isolate the parts of the system that are causing your performance problems. This information can then be used to determine what changes will reap the greatest benefit. Sometimes the solution is as simple as modifying a single method, algorithm, or data structure. However, studying your system with a profiler can also reveal flaws in your OOD, and even in your original problem analysis. You need to be willing to revisit these early stages of the process. Grady Booch, a prominent object-oriented design expert, notes:

Rigid approaches to design only lead to largely useless design products that resemble a progression of lies, behind which developers shield themselves because no one is willing to admit that poor design decisions should be changed early, rather than late.4

There are several good commercial profiling tools on market, and a simple free tool is included with Sun's version of the Java 2 SDK. Without a profiling tool you'll be left guessing about what parts of your system need optimization, and you risk wasting a great deal of effort optimizing code that has nothing to do with the true bottlenecks. Profiling is discussed in depth in Chapter 3.

2.2 References on Object-Oriented Design

Booch, Grady. Object-Oriented Analysis and Design with Applications, Second Edition, Addison-Wesley, Reading, MA, 1994.

Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995.

Goldstein, Neal, and Jeff Alger. Developing Object-Oriented Software for the Macintosh: Analysis, Design, and Programming, Addison-Wesley, Reading, MA, 1992.

Grand, Mark. Patterns in Java, Volume 1, John Wiley & Sons, New York, 1998.

Larman, Craig. Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design, Prentice Hall, Upper Saddle River, NJ, 1998.

Schneider, Geri, and Jason Winters. Applying Use Cases: A Practical Guide, Addison-Wesley, Reading, MA, 1998.

Key Points

  • Writing high-performance software requires action during all phases of the software development lifecycle.
  • Creating clear system and performance requirements is the key to evaluating the success of your project.
  • Scalability is more dependent on good design decisions than optimal coding techniques.
  • Performance tuning is an iterative process. Data gathered during profiling needs to be fed back into the development process.



[Contents] [Prev] [Next] [Index]

1

For more information about use cases, see Geri Schneider and Jason Winters, Applying Use Cases: A Practical Guide, p. 1. Addison-Wesley, 1998.

2

Craig Larman, Applying UML Patterns: An Introduction to Object-Oriented Analysis and Design, p. 499. Prentice Hall, 1998.

3

Swing refers to the Java Foundation Classes (JFC) Swing packages, which provide a comprehensive set of classes for building graphical user interfaces.

4

Grady Booch, Object-Oriented Analysis and Design with Applications. Addison-Wesley, 1994.

Copyright © 2001, Sun Microsystems,Inc.. All rights reserved.