java performanceA Cost Model for Java Performance![]() <[email protected]> Glen McCluskey is a consultant with 15 years of experience and has focused on programming lan-guages since 1988. He specializes in Java and C++ perfor-mance, testing, and technical documenta-tion areas. In his book Programming Pearls, Jon Bentley discusses the use of cost models in connection with programming languages. A cost model identifies the relative execution times of specific language constructs. For example, you can determine roughly how much time is required to perform a divide operation as compared to an add operation, or find out how expensive function calls are. In this column we'll look at a simple cost model for the Java language. Determining the cost of language features does have some pitfalls. One is that a compiler optimizer may optimize away simple usage, for example, summing two numbers millions of times in a loop. Related to this problem is the issue of Java just-in-time compilers that perform dynamic compilation and optimization. Such a compiler might, for example, dynamically inline a function. This analysis uses Sun's Java Development Kit Java interpreter, without optimization and with a switch specified that disables just-in-time compilation: java -Djava.compiler=NONE file where file.class exists and was created from file.java. This discussion includes a series of benchmark programs and concludes with a summary of results. Download the benchmarks from <ftp://ftp.glenmccl.com/pub/free/cost.zip>. Empty Loops and ArithmeticThe initial set of benchmarks focuses on the cost of looping and arithmetic. The first program measures the cost of an empty loop:
public class cost_loop { In these programs, the value of N is adjusted so that each program takes approximately the same amount of time to run. Elapsed time is computed in milliseconds and then scaled up to nanoseconds. The next three programs measure the cost of integer addition, integer division, and floating-point division:
public class cost_add {
public class cost_divide {
public class cost_fp { Method CallsThe next set of programs measures the costs of method invocation. The Java language features several kinds of method calls. The first one is a static call, a call of a class method without reference to a particular object:
public class cost_static { The second is an instance method call. It requires more work because the actual method to be called must be determined dynamically, based on the type of the object. (This type of call often goes by the name "virtual function.")
public class cost_virtual { The last type of method call is a call through an interface reference, which requires even more work:
interface A { Array IndexingThe Java language specifies that all array subscripts are range-checked dynamically, with an exception thrown for out-of-bounds indices. So it's interesting to examine the cost of array lookup:
public class cost_array { String OperationsThe Java language has a built-in operator, +, for concatenating strings. Java strings are instances of the java.lang.String class and are more sophisticated (and expensive) than simpler C-style strings. Here is a program that measures string operation time:
public class cost_string { Exception Handling and Synchronized StatementsJava exception handling is a mechanism for raising, propagating, and trapping exceptions that represent error conditions. Exception handling tends to be relatively costly, given the work in unwinding the stack, trying various exception handlers, and so on. A program that evaluates this feature is:
public class cost_eh { A synchronized statement a statement executed after obtaining a lock on an object is used in thread programming. This program times such synchronization:
public class cost_sync { CastingA Java class reference refers to objects of a given class type and also can refer to objects of subclasses of the type. If B is a subclass of A, then an A reference can refer to A objects or to B objects. If I have an A reference, I can cast it to a B reference, but the cast must be dynamically checked, because the A reference might really refer to an A object, which cannot be cast to a B.Here's a program that measures the cost of casting:
class A {} Creating New ObjectsThe final area we'll examine is the cost of creating new objects and calling the constructor for each created instance. This particular benchmark also includes the cost of garbage collection, given that you don't explicitly deallocate no-longer-used objects:
class A { Timing ResultsWe can divide the timing results into sections, according to the above organization. The times include the "overhead" cost of loop iteration, which is around 138 nanoseconds per iteration. The numbers in parentheses are adjusted nanosecond time values, with the 138 ns loop cost subtracted from the raw value in each case.
Empty Loops and Arithmetic
Method Calls
Array Indexing
String Operations
Exception Handling and Synchronized Statements,
Casting
Creating New Objects Using the adjusted numbers, we can make some observations about the costs of various operations. For example, calling a method through an interface reference is about twice as costly as through a normal object reference. Integer division costs about four times as much as integer addition, and floating-point division costs about nine times as much. Creating a new object is more than 40 times as expensive as a simple operation like addition. The most expensive operation of all is string concatenation, which involves multiple method calls, creation of a new string, copying, and so on. SummaryCost models are a useful way to gain performance insights about the features of a programming language so that you can better gauge the relative costs of different language features. |
![]() Last changed: 20 nov. 2000 ah |
|