GC statistics (was Re: big project ported)

Boehm, Hans hboehm@exch.hpl.hp.com
Wed Oct 27 13:37:00 GMT 1999


It's worth trying to understand the performance numbers a bit more, since
they're not all consistent.  What was the code used to test the "2,000,000
Bytes" allocation?

Based on a different GC test I concluded that gcj performance was something
like a factor of 2 slower than HotSpot or Sun's Exact VM, and I could get
most of that difference back by replacing the Java client by a C++ client
and using my latest GC in incremental mode.  IBMs GC seemed to be about
halfway between the Sun ones and gcj.  Everything else I tried was
appreciably slower.  (This included most of the Linux VMs, but didn't
include the non-Sun win32 VMs.)

I would expect that 5.0 alpha 4 (hopefully about to be released) will be
measurably faster in incremental mode than its predecessors.  Other than
that I don't expect huge performance improvements.  By default, the 5.0
versions tend to touch less memory, and thus spend a bit more processsor
cycles.  A notable change is that even in nonincremental mode, a GC is
usually triggered before the heap fills up.  This avoids breaking up large
blocks during each GC cycle, and thus seems to be appreciably less prone to
fragmentation issues.  It does mean that increasing the heap size doesn't
decrease GC frequency nearly as much as it used to.

I would expect there to be an issue with the allocation of very large
(multi-MB) Java arrays.  By default, and I believe the way they are
allocated now, a misidentified pointer to anywhere in the array retains the
array.  The allocator will try to place such arrays between known bogus
references to minimize the problem.  But this tends to be hard for
sufficiently big arrays.  I know of no way to avoid this issue without
teaching the code generator to keep a reference to the beginning of the
array around as long as the array is needed.  Once you do that, it becomes
trivial.

The huge heap growth in this test suggests that something is seriously
wrong.  It seems likely that the collector's view of live data differs
significantly from reality, and it's doing way too much work as a result.
If it's a small test, I would really like to try it here.

As suggested by C/C++ client experiments, and as Tom mentioned, I also
expect that there is something significant to be gained by tuning the
gcj/collector interface.  The allocation path could be shortened
appreciably.  My current guess is that the same applies to the per object
mark costs.  (But the factor of 30 difference below is almost certainly
caused by something else.)

Hans

From: Bryce McKinlay <bryce@albatross.co.nz>

Jeff Sturm wrote:

> It may not be working correctly though... on the first collection it
> printed
>
>  Initiating full world-stop collection 2 after 16946220 allocd bytes
>  0 bytes in heap blacklisted for interior pointers
>  --> Marking for collection 2 after 16946220 allocd bytes + 14280 wasted
> bytes
>  Collection 1 reclaimed 0 bytes ---> heapsize = 67174400 bytes
>  World-stopped marking took 20 msecs
>  Bytes recovered before sweep - f.l. count = -103180
>  Immediately reclaimed 16848164 bytes in heap of size 67174400 bytes
>  12296 (atomic) + 32208 (composite) collectable bytes in use
>  Finalize + initiate sweep took 0 + 10 msecs
>
> and then immediately dumped core.  I guess we really need to upgrade
> boehm-gc...

Hmm, it works okay for me - did you rebuild all of libgcj against the
modified gc? I
had this problem until I did a full "make clean && make". I am using GC
5.0_alpha1
however.

Unfortunately the times reported by the gc are cpu times, which are only
accurate to
10ms on Linux. It should be fairly easy to change it to use gettimeofday()
which
should give more accurate results. It would also be nice to be able to get a
summary
of the time spent marking, sweeping, etc from both IBM and boehm and compare
them.

After a few very un-scientific tests I am inclined to agree with your
conclusion
that gc is the major source of performance problems in libgcj. Boehm is
fairly
competitive (ie just slightly slower) in tests which allocate small numbers
of large
objects (eg 20,000 byte arrays of 100 bytes each), but poor in tests which
allocate
large numbers of small objects (eg 2,000,000 "Bytes"). IBM takes around 2
secs on my
P2-233 for each iteration of the 2,000,000 byte tests where as Boehm is
taking 10
secs +. Boehm is spending up to 900ms marking for each gc iteration, when
IBM is
spending maybe 30ms.

Also, with IBM the heap stabilizes at around 28MB for this test while in
libgcj the
heap keeps growing to 140MB+ if I dont call Runtime.gc()

After seeing this I decided to experiment with boehm's incremental mode
(which
requires MPROTECT_VDB, which breaks multithreaded read() calls in libgcj,
but for
testing purposes seems to work well enough). Incremental seems to have a
detrimental
effect on the small-number-of-large-object cases (~20% slowdown) but does
improve
the large-number-of-objects case somewhat: down to around 6-7secs. Still
nowhere
near IBM's times though.

regards

  [ bryce ]



------- end -------


More information about the Java mailing list