ObJectBridge
BRIDGING JAVA OBJECTS AND RELATIONAL DATABASES


Important note: This document covers very early releases of OJB (0.1.30 - 0.1.48). Some technical details described here do not apply to the current releases of OJB. The test results presented here should be carefully checked against current releases.


EJB performance analysis

© Thomas Mahler, December 2000

abstract

As a part of a J2EE architecture project my team performed a performance analysis for IBM WebSphere.

Our aim was to study different persistence mechanisms under a variety of application scenarios in order to find a solution that provides the best compromise of performance, scalability and architectural “beauty”.

This article presents a rough overview of our approach and our test results.

 

Test-scenarios

We decided to implement real-world test-scenarios against an existing database. We used a database with the german postalcodes (270.000 rows) as a base for the following scenarios:

 

1. Scenario "find all postalcodes of the city 'Baden-Baden' and fully read each entry"

2. Scenario "find all postalcodes of the city ‘Berlin’ with streets named Hauptstrasse and fully read each entry"

3. Scenario "update all postalcode entries for city 'Baden-Baden'"

4. Scenario "Iterate through 1000 entries and update 32 entries"

5. Scenario "Iterate through 500 entries and update randomly selected entries"

 

These scenarios represent activities (and their distributions) that are typical for applications that we build for our customers. We did not test INSERT operations. Insert operations modify the index structure and may lead to performance degradations for later tests. Thus we ommited inserts to keeps our tests reproducable.

The study of relationships between entities was beyond the scope of the present analysis.

It is important to note that the strucure of the scenarios has a deep impact on the test results. Our scenarios may be not suited to represent the call structure of other applications.

We used the JUnit framework to implement our scenarios as TestCases. We used the JUnitEE Servlet based html Testclient. We chose the servlet based client to eliminate network overhead between client and EJB server.

 

Observed persistence operations

We identified the following operations as the “atoms” of our scenarios:

  1. Looking up an object from the db. I.e. perform a “SELECT FROM …” and materialize the object from the returned ResultSet.

  2. Reading object attributes i.e. calling getters

  3. Setting object attributes and Updating the underlying database row.

 

We used a simple “Profiler” that allows to count method calls and to accumulate the time spend in these method calls.

 

The tested persistence mechanisms

  1. Container managed Entity Beans (CMP) with VAP (IBM VisualAge Persistence) generated JDBC code. This is the pure IBM way of implementing CMP with VisualAge for Java and WebSphere.

  2. BeanManaged Entity beans (BMP) that use the ObJectBridge PersistenceBroker for performing DB read/write operations and object caching.

  3. A pure JDBC reference implementation that does not run within an EJB container. It represent a “normal” JDBC application using prepared statements.

 

Our test environment

The Application server:

IBM Netfinity M10

-         4x Pentium XEON 400 MHz

-         2 GB RAM

-         Windows NT 4.0 Server, SP 6

-         WebSphere 3.5 Advanced Edition

-         CMP Code generated by IBM VisualAge 3.5 with IBM VAP

-         BMP Code uses the ObJectBridge 0.1.30 release

-         DB UDB Client 6.1 and respective JDBC App-Driver

 

Database server

IBM Intellistation Mpro

-         2x Pentium III 500 MHz

-         512 MB Ram

-         NT Workstation 4.0 SP 6

-         IBM DB2 UDB 6.1

 

Testclient

IBM PC

-         1x Pentium III 500 MHz

-         256 MB RAM

-         NT Workstation 4.0 SP 6

-         MS InternetExplorer 5.0 SP1

 

The test results

 

The following figure presents the results of the performance test. For each scenario we tested all tree persistence mechanisms and recorded values for the three observed persistence operations.

 

Measurements for 5 scenarios against 3 different persistence mechanisms

 

 

 

 

 

 

 

 

 

 

1. Scenario "find all postalcodes of city named 'Baden-Baden'"

 

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

Lookup incl. Finder

690

1265

1,83

690

1452

2,10

690

292

0,42

call all getters

690

41.313

59,87

690

1.720

2,49

690

0

0,00

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2. Scenario "find all postalcodes of city Berlin and streetname Hauptstrasse"

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

Lookup incl. Finder

25

376

15,04

25

391

15,64

25

47

1,88

call all getters

25

1.546

61,84

25

78

3,12

25

0

0,00

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3. Scenario "update all postalcode entries for city 'Baden-Baden'"

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

call all getters

690

42.729

61,93

690

1.934

2,80

690

0

0,00

update one field

690

10.473

15,18

690

329

0,48

690

2.517

3,65

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4. Scenario "Iterate through 1000 entries and update 32 entries"

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

Lookup incl. Finder

1.000

10.855

10,86

1000

2.264

2,26

1.000

3.984

3,98

call all getters

1.000

92.767

92,77

1000

3.314

3,31

1.000

0

0,00

update one field

32

471

14,72

32

16

0,50

32

125

3,91

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5. Scenario "Iterate through 500 entries and update randomly selected entries"

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

Lookup incl. Finder

500

6.430

12,86

500

16.274

32,55

500

3.292

6,58

call all getters

500

41.381

82,76

500

2.008

4,02

500

0

0,00

update one field

100

1.435

14,35

100

57,41

0,57

100

488

4,88

 

 

The following figure presents results for each persistence operation. It also shows totals and average values.

 

Lookup incl. Finder

 

 

 

 

 

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

scenario

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

1

690

1265

1,83

690

1452

2,10

690

292

0,42

2

25

376

15,04

25

391

15,64

25

47

1,88

4

1.000

10.855

10,86

1000

2.264

2,26

1.000

3.984

3,98

5

500

6.430

12,86

500

16.274

32,55

500

3.292

6,58

 

 

 

 

 

 

 

 

 

 

total/avg

2215

18926

8,54

2215

20381

9,20

2215

7615,30313

3,44

remarks:

OJB in it's current form does not improve performance for finders! Two reasons: 1.) no use of prepared statements yet, 2.) reflection is slow.

 

OJB will use prepared statements in future versions. I expect that this will have a positive impact on finder performance.

 

 

 

 

 

 

 

 

 

 

call all getters

 

 

 

 

 

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

scenario

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

1

690

41.313

59,87

690

1.720

2,49

690

0

0,00

2

25

1.546

61,84

25

78

3,12

25

0

0,00

3

690

42.729

61,93

690

1.934

2,80

690

0

0,00

4

1.000

92.767

92,77

1000

3.314

3,31

1.000

0

0,00

5

500

41.381

82,76

500

2.008

4,02

500

0

0,00

 

 

 

 

 

 

 

 

 

 

total/avg

2905

219736

75,64

2905

9054

3,12

2905

0

0,00

remarks:

In the pure JDBC Version there is no overhead for Container activities. Thus accessing attributes takes virtually no time.

 

CMP/VAP needs exorbitantly more time for calling getters, as Entities are stored, passivated, loaded and activated

 

between each method-call by the EJB Container . The VAP implementation implies a DB read and write for each ejbStore() and ejbLoad().

 

In BMP/OJB the EJB Container still does all the activation/passivation overhead, but through OJB's ObjectCache DB-lookups are minimized

 

 

 

 

 

 

 

 

 

 

update one field

 

 

 

 

 

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

calls

total [ms]

avg [ms]

3

690

10.473

15,18

690

329

0,48

690

2.517

3,65

4

32

471

14,72

32

16

0,50

32

125

3,91

5

100

1.435

14,35

100

57,41

0,57

100

488

4,88

 

 

 

 

 

 

 

 

 

 

total/avg

822

12378,8624

15,06

822

402,407407

0,49

822

3129,26324

3,81

remarks:

How can OJB be 7 times faster than a pure JDBC solution for updates? As for updates we have no performance boost through

 

an ObjectCache, but have to perform "real" DB UPDATES, there must be another reson for this.

 

 

 

1. There could be an error in our TestCase (I will check this soon)

 

 

 

 

 

2. The JDBC prepared statement updates ALL columns. OJB uses smart Updates to detect modified attributes and updates only the modified column.

 

The following figure is a condensed view that compares performance per scenario and per operation. The times for the JDBC variant are taken as reference and the factors of CMP/VAP resp. BMP/OJB are given behind the totals.

 

per method

 

 

 

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

 total [ms]

factor to JDBC

 total [ms]

factor to JDBC

 total [ms]

factor to JDBC

Lookup incl. Finder

      18.926,00   

2,49

      20.381,00   

2,68

        7.615,30   

1,00

call all getters

    219.736,00   

--

        9.054,00   

--

                 -     

1,00

update one field

      12.378,86   

3,96

          402,41   

0,13

        3.129,26   

1,00

 

 

 

 

 

 

 

All methods

    251.040,86   

23,36

      29.837,41   

2,78

      10.744,57   

1,00

 

 

 

 

 

 

 

 

 

 

 

 

 

 

per scenario

 

 

 

 

 

 

 

CMP / IBM VAP

BMP / ObJectBridge

JDBC prepared Statements

 

 total [ms]

factor to JDBC

 total [ms]

factor to JDBC

 total [ms]

factor to JDBC

1

      42.578,00   

145,79

        3.172,00   

10,86

          292,05   

1,00

2

        1.922,00   

40,89

          469,00   

9,98

            47,00   

1,00

3

      53.202,00   

21,14

        2.263,00   

0,90

        2.516,71   

1,00

4

    104.093,00   

25,33

        5.594,00   

1,36

        4.109,00   

1,00

5

      49.245,86   

13,03

      18.339,41   

4,85

        3.779,80   

1,00

 

 

 

 

 

 

 

All scenarios

    251.040,86   

23,36

      29.837,41   

2,78

      10.744,57   

1,00

 

 

 

 

 

 

 

 

 

 

 

 

 

 

factor to BMP/ObJectBridge

8,41

 

1,00

 

0,36

 

Remarks

General observations

From an OJB point of view the results look quite promising. The overall performance of typical EJB applications can be improved by a factor of 8,41 (i.e. 841%).

The performance for the getters is improved by a factor of 24 and the performance for updates is even improved by a factor of 30. Only lookup of objects could not be improved. In a future release of OJB prepared statements will be used for primary key lookups. I assume this will enhance the lookup performance by a remarkable factor.

 

ObJectBridge and EJB containers

We anticipated the performance gain for getters, as we observed the behaviour of the WebSphere EJB container. After calling any method on an Entity it is stored and passivated immediately. For any subsequent method-call it has first to be found, loaded and activated (which implies three ejbLoad calls !) . This Activation/Passivation strategy results in an enormous performance degradation if an ejbLoad() call has to access the RDBMS.

If the ejbStore() uses and UPDATE for each call this results in completely unneccesary write operations if the entity has not been modified during the method call.

The CMP/VAP code works in this way,so we were not surprised about the bad results for the getters.

OJB uses an ObjectCache. Thus the ejbLoad() just reads an object once from the rdbms all subsequent calls retrieve the objects from the cache.

In addition, OJB uses smart updates, i.e. if the broker detects that an object has not been modified it will NOT perform an update against the RDBMS. If attributes have been modified it will only update modified columns. Thus the ejbStore() calls are not as expensive as for the CMP/VAP scheme.

Using a caching O/R layer in the context of EJB containers seems to be a good strategy to balance the containers benefits against its performance-costs.

If the Lookup speed can be further improved, OJB will help EJB developers to increase the performance of their applications by more than an order of magnitude.

 

I think the most important result is: The overhead for using an EJB container instead of writing a pure JDBC application where you have to build all the needed infrastructure and services on your own is only 2,78.

This is really not much compared to the benefits you gain from using an EJB container: O/R mapping, transaction handling, scalability, remote object infrastructure, security, etc.

Open questions

My biggest surprise are the very good results for the Updates. It’s hard to believe that OJB (with its dynamic SQL generation using the slow Java-Reflection) can outperform a native JDBC application using a fixed set of prepared statements (“SELECT * FROM … WHERE ID=?”, “UPDATE … WHERE ID=?”).

I have two possible explanation for our results:

  1. There is a bug in the BMP/OJB Code and updates are not executed at all.

  2. OJB is really faster because it uses smart updates (only modified columns are updated). The JDBC prepared statement updates all columns.
    To use a fixed set of prepared statements for smart updates is not an option due to a combinatorial explosion: for a table with n columns you would need a set of 2n-1 statements.

I will soon check wich explanation holds true.

 

Further evaluations

We plan to port our test-cases to other EJB containers (JBOSS and Bea Weblogic). We want to compare the overall performance of IBM WebSphere with that of different containers. Other containers might implement different activation/passivation startegies that reduce the container overhead. They might also use different persistence layers for CMP that do a better job than VAP.
I’m especially interested how ObJectBridge compares to ‘industrial strength’ persistence layers.

 

 


release: 0.7.235, date: 2001-11-24