Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2 3
  ignore  |  Print  
  Garbage collector tuning  (Read 6653 times)
0 Members and 1 Guest are viewing this topic.
Offline Gudradain
« Posted 2012-07-31 04:18:48 »

Currently reading a nice article on Java performance :

http://www.ibm.com/developerworks/java/library/j-jtp01274/index.html

I thought I would share because it talk about a subject I heard a lot around here : Object pooling. I guess it's nice to understand why it is a bad idea to use Object pooling Smiley

Quote
Object pooling
Object pooling is a straightforward concept -- maintain a pool of frequently used objects and grab one from the pool instead of creating a new one whenever needed. The theory is that pooling spreads out the allocation costs over many more uses. When the object creation cost is high, such as with database connections or threads, or the pooled object represents a limited and costly resource, such as with database connections, this makes sense. However, the number of situations where these conditions apply is fairly small.
In addition, object pooling has some serious downsides. Because the object pool is generally shared across all threads, allocation from the object pool can be a synchronization bottleneck. Pooling also forces you to manage deallocation explicitly, which reintroduces the risks of dangling pointers. Also, the pool size must be properly tuned to get the desired performance result. If it is too small, it will not prevent allocation; and if it is too large, resources that could get reclaimed will instead sit idle in the pool. By tying up memory that could be reclaimed, the use of object pools places additional pressure on the garbage collector. Writing an effective pool implementation is not simple.
In his "Performance Myths Exposed" talk at JavaOne 2003, Dr. Cliff Click offered concrete benchmarking data showing that object pooling is a performance loss for all but the most heavyweight objects on modern JVMs. Add in the serialization of allocation and the dangling-pointer risks, and it's clear that pooling should be avoided in all but the most extreme cases.

Here are 2 other links that give the necessary information to understand this article Smiley

http://www.ibm.com/developerworks/java/library/j-jtp10283/
http://www.ibm.com/developerworks/java/library/j-jtp11253/#1.0
Offline Roquen
« Reply #1 - Posted 2012-07-31 07:03:00 »

Note pool can be interesting for dataflow optimizations.
Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 844
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #2 - Posted 2012-07-31 08:27:48 »

That is an article from 2003. Things have changed, and not only for the better.

If you want semi-realtime behavior (a stable framerate), then object pooling can still be a good idea, as the GC won't have to clean up those tens of thousands of objects created every frame.

Let alone Android, were the GC is poor and object allocation is slow.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Grunnt

JGO Kernel


Medals: 95
Projects: 8
Exp: 5 years


Complex != complicated


« Reply #3 - Posted 2012-07-31 08:51:20 »

I tended to be really careful not to generate any garbage in the main loop of a game, but I started being more relaxed about it after reading on JGO that garbage collection is not such a big deal anymore. And usually, in PC's, it isn't.

Some ideas on this:
- Games are a special case, in that usually the "smoothness" and real-time character of a game is a critical aspect, much more so than with most other application types. Stutter caused by garbage collection ruins a game much faster than, for example, a word processor.
- Synchronization issues are irrelevant if you do not create a multithreaded game (which most indie developers don't I guess).
- "Tuning the pool size" is not that hard if you have an idea on how many game elements you want to support. And, ofcourse, you can increase the pool size by a step whenever needed.

The main reason for me for not using object pools is mainly that I usually don't run into performance issues with garbage collection (hence, unnecessary optimization), and it makes the code a bit more complicated. But in rare cases it may be useful, e.g. when the garbage collector is very inefficient (e.g. Android? dunno) or when massive numbers of objects are created/disposed each update (e.g. a gazillion bullets on-screen).

Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #4 - Posted 2012-07-31 09:05:45 »

Pooling is great, especially for games, especially for single threaded games, even more especially if you've got the G1 collector. The things I pool are sprites and particles which are used and reused at a pretty frightening rate. I also pool DirectByteBuffers which I use for loading images (so basically I only end up actually allocating one rather large one and reusing it over and over, as it should be), and certain hacks in my sprite engine to render arbitrary geometry are pooled - particularly things like dynamically sized arrays which are created every frame - expensive.

"Always be pooling"

The degenerate form of pooling is of course a static final scratch object. I use those a lot too...

Cas Smiley

Offline nsigma
« Reply #5 - Posted 2012-07-31 10:42:00 »

- Synchronization issues are irrelevant if you do not create a multithreaded game (which most indie developers don't I guess).

Just to be pedantic, if you've got audio in your game, then it's multi-threaded!   Wink  Not that you're likely to be sharing pooled objects between audio and video renderers.

Pooling is great, especially for games, especially for single threaded games, even more especially if you've got the G1 collector.

Why especially with G1?  Out of interest, has anyone found it to be any better?  I'm still using -Xincgc and that still works better for me.  It handles audio and GC well without much pooling - I mention audio because it's not usual you're aiming for ~700fps in video and missing an audio frame is far more noticeable, not because the application is audio only.

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 844
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #6 - Posted 2012-07-31 10:59:54 »

if you've got audio in your game, then it's multi-threaded!
OpenAL handles async streaming just fine persecutioncomplex

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline 65K
« Reply #7 - Posted 2012-07-31 11:12:58 »

Why especially with G1?  Out of interest, has anyone found it to be any better? 
With a bit of highly unscientific testing I found the G1 causing regular hickups when I run my game on only one CPU core. Which does not happen with the default GC.

Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #8 - Posted 2012-07-31 11:19:14 »

Almost everyone has a dualcore system now (almost - here's your up-to-date typical gamer hardware survey - this is truly the gospel with regards to games and what's out there and who's using what). Its chief advantage, allegedly, is that it is concerned with throwing away easy garbage before it goes looking at the rest of the heap, or so I am led to believe. This means that things like object pools, which basically make your heap full of old, live objects (the worst sort of object for ordinary GC performance purposes), are much cheaper to the G1 GC, as they aren't generally looked at.

Experiments with the G1 GC with Project Zomboid are interesting. The heap barely grows - in eden - and is collected very frequently and pauselessly (they use a lot of threads too mind). The incremental GC suffers a noticeably annoying pause every minute as eden fills up considerably more. Unfortunately, for reasons we are still trying to determine, the G1 GC causes a permanent graphical corruption after its first collection (incgc also causes it but it seems less obtrusive, oddly).

Cas Smiley

Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #9 - Posted 2012-07-31 11:22:22 »

btw OpenAL is an interesting case, as behind the scenes OpenAL has its own mixing thread running in high priority. Then in Java you generally need a separate thread from your game thread to stream data to OpenAL. But seeing as you're just using the same set of buffers over and over and not creating all that many objects on a frame-by-frame basis you're never going to drop a frame of audio. Well, you might once in a while due to circumstances beyond your control but we're not talking realtime OSes here.

Cas Smiley

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline nsigma
« Reply #10 - Posted 2012-07-31 11:26:52 »

if you've got audio in your game, then it's multi-threaded!
OpenAL handles async streaming just fine persecutioncomplex

Which is not the same as saying the whole thing is single threaded!

... Then in Java you generally need a separate thread from your game thread to stream data to OpenAL. But seeing as you're just using the same set of buffers over and over and not creating all that many objects on a frame-by-frame basis you're never going to drop a frame of audio.

hmm, that's assuming that objects created in your graphics thread and GC'd don't affect the audio thread - GC can be 'stop the world'!

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #11 - Posted 2012-07-31 11:31:37 »

Indeed they absolutely will. However the cunning part is that you queue up several milliseconds of data in advance to OpenAL, and as OpenAL threads are unaffected by a Java GC pause, they carry on mixing and playing your sounds. Only if you're particularly unlucky do you get sound stuttering issues when Java has been unable to queue up enough sound to OpenAL and it runs out.

FWIW I queue up about 2 seconds of audio on each music stream playing (usually 1 or 2), probably overkill as a GC pause is rarely ever more than 20ms.

Cas Smiley

Offline nsigma
« Reply #12 - Posted 2012-07-31 11:43:04 »

@Cas - with 2s of audio you shouldn't ever hear issues - because I'm doing the DSP in Java I'm usually working with about 6ms and GC isn't an issue!

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #13 - Posted 2012-07-31 11:56:04 »

Now you mention it, it is a bit daft Smiley I've cut it down to 4 buffers of 4kb each (about 0.1sec).

Cas Smiley

Offline Gudradain
« Reply #14 - Posted 2012-07-31 12:27:14 »

If you want semi-realtime behavior (a stable framerate), then object pooling can still be a good idea, as the GC won't have to clean up those tens of thousands of objects created every frame.

That is wrong... There are absolutely no cost for the GC to clear tens of thousands of objects created every frame. For all those that didn't bother to read the 3 articles here is the idea:

By default, in JDK 1.4 (ok it's old), the GC is a generational garbage collector. What that means? Simply that the GC makes a differences between yound object and old object. Young object are object that doesn't last a long time. There are a lot of those object in your programs; in fact the average is 98% of young object. For example of young object, consider variable that you create only for the scope of a method. Old object are usually static class field or instance variable in another object.

Now the GC use 2 differents algorithm for young and for old objects. For old objects, it use a mark-sweep-compact GC. For the the young objects, it use a copying collector. The way he acheives that is by splitting the memory the JVM use in two. In the first half, the GC stores the old objects and in the second half it stores the young object. Further more, the second half is again split in two; one part where are the young object and the other part is empty and will be used to copy the live young objects when the first part is full.

For the young objects, the GC use the copying collector. With the copying collector you split the memory in two and when one part is full you copy everything that is still active in the other part. There is indeed a cost to copying everything to the other side. The good point is that only object that are still active need to be copied. In fact, object that are not active anymore will not even be visited as the copying collector only visit active object.

So there are absolutely no cost in clearing the thousands of object created every frame.

In fact, you can think of the copying collector for young object as automatic object pooling. You don't even have to think about it and if you try to do it by hand you will fight against the GC.
Offline Roquen
« Reply #15 - Posted 2012-07-31 12:37:56 »

No matter how you slice it...GC must walk memory (not free) and usually random memory (even less free) and when compacting must move memory (not free).
Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #16 - Posted 2012-07-31 12:54:40 »

@Gudradain - I'm afraid you're the wrong one in this case. The GC does actually cost fairly significant time to clean tens of thousands of objects - often longer than an entire frame - which is no good if you need a rock steady 60Hz update. So far the G1 GC is actually better than most when it comes to sweeping eden out but is otherwise somewhat slower. Long story short, pooling creates no work at all for the GC, and so you therefore have no problems with unexpected pauses.

Cas Smiley

Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 844
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #17 - Posted 2012-07-31 12:56:14 »

@Gudradain you explained the workings of the GC quite nicely, but sadly drew the wrong conclusions. There is a (relatively) high cost in GC, which gets exponentially worse when dealing with high concurrency. But let's not go there yet, even in single-threaded applications both object creation and cleanup is far, far from free.

No matter how you slice it...GC must walk memory (not free) and usually random memory (even less free) and when compacting must move memory (not free).
and rewriting all references to the moved objects (even more extremely less free)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Gudradain
« Reply #18 - Posted 2012-07-31 13:00:04 »

I'm afraid you have misconception and are using wrong optimization. Here is a quick example that I did and see right away that object pooling degrade the performance.

It took 620 ms in average without object pulling.
It took 860 ms in average with object pulling.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
public class NoPool {
   
   long count = 0;
   
   public void run(){
      for(int i=0; i<100000; i++){
         SomeObject o = new SomeObject(i, "Creation : " + i);
         count += o.getI();
      }
   }
   
   public static void main(String [] args){
      NoPool np = new NoPool();
      long begin = System.nanoTime();
      for(int i=0; i<100; i++){
         np.run();
      }
      long end = System.nanoTime();
      long delta = end-begin;
      System.out.println("Time elapsed in nano : " + delta);
      System.out.println("Time elapse in milli : " + delta/(1000*1000));
   }

}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
public class ObjectPool {
   
   private SomeObject[] pool = new SomeObject[100000];
   long count = 0;
   
   public ObjectPool(){
      for(int i=0; i<100000; i++){
         pool[i] = new SomeObject(0, "");
      }
   }
   
   public void run(){
      for(int i=0; i<100000; i++){
         SomeObject o = pool[i];
         o.setI(i);
         o.setS("Creation : " + i);
         count += o.getI();
      }
   }
   
   public static void main(String [] args){
      ObjectPool op = new ObjectPool();
      long begin = System.nanoTime();
      for(int i=0; i<100; i++){
         op.run();
      }
      long end = System.nanoTime();
      long delta = end-begin;
      System.out.println("Time elapsed in nano : " + delta);
      System.out.println("Time elapse in milli : " + delta/(1000*1000));
   }

}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
public class SomeObject {
   
   private int i;
   private String s;
   
   public SomeObject(int i, String s){
      this.i = i;
      this.s = s;
   }
   
   public int getI(){
      return i;
   }
   
   public String getS(){
      return s;
   }
   
   public void setI(int i){
      this.i = i;
   }
   
   public void setS(String s){
      this.s = s;
   }

}
Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 844
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #19 - Posted 2012-07-31 13:02:05 »

You confuse throughput with latency.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline Gudradain
« Reply #20 - Posted 2012-07-31 13:06:54 »

You confuse throughput with latency.

Right...

Still the performance is worse with object pooling.

I would need a way to calculate the pause of the GC for each. Since there is a big difference in the performance of the 2, you might still be surprise.

EDIT: I found it -> The command line argument -verbose:gc prints information at every collection.
Offline Roquen
« Reply #21 - Posted 2012-07-31 13:09:49 »

and rewriting all references to the moved objects (even more extremely less free)
Very good point.

The only way object creation/deletion is 'free' is if it's escaped, the type is statically known to be a specific concrete type (don't know if hotspot attempts this) and scalar replacement/evolution can determine all fields are written before reads...and probably some other stuff I'm forgetting and are very unlikely to be determined to be true.
Offline Roquen
« Reply #22 - Posted 2012-07-31 13:17:48 »

Actually, if this wasn't an issue we'd need to ask ourselves why there are always new presentations being given on GC tuning (including from Sun and now Oracle).
Offline Gudradain
« Reply #23 - Posted 2012-07-31 13:22:15 »

Quote
In general, a particular generation sizing chooses a trade-off between these considerations. For example, a very large young generation may maximize throughput, but does so at the expense of footprint, promptness, and pause times. young generation pauses can be minimized by using a small young generation at the expense of throughput. To a first approximation, the sizing of one generation does not affect the collection frequency and pause times for another generation.

From : http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html

I'm still reading Smiley
Offline Gudradain
« Reply #24 - Posted 2012-07-31 13:30:33 »

And yet we have more surprises! Smiley

I run my previous test with the vm argument : -verbose:gc

Result with object pooling :

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
[GC 49216K->10758K(188352K), 0.0076902 secs]
[GC 59974K->12750K(237568K), 0.0070728 secs]
[GC 111182K->14302K(237568K), 0.0070655 secs]
[GC 112734K->15478K(336000K), 0.0067169 secs]
[GC 212342K->16430K(336000K), 0.0067652 secs]
[GC 213294K->17246K(535872K), 0.0063414 secs]
[GC 410974K->17214K(535872K), 0.0072485 secs]
[GC 410942K->17246K(929728K), 0.0069668 secs]
Time elapsed in nano : 918742224
Time elapse in milli : 918


Result without object pooling

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
[GC 49216K->256K(188352K), 0.0007276 secs]
[GC 49472K->192K(188352K), 0.0005208 secs]
[GC 49408K->256K(188352K), 0.0004196 secs]
[GC 49472K->160K(237568K), 0.0003678 secs]
[GC 98592K->208K(237568K), 0.0004676 secs]
[GC 98640K->224K(328192K), 0.0005211 secs]
[GC 197088K->176K(328192K), 0.0008081 secs]
[GC 197040K->176K(320064K), 0.0004403 secs]
[GC 189168K->176K(312896K), 0.0002397 secs]
[GC 181680K->176K(305472K), 0.0003220 secs]
[GC 174576K->176K(299008K), 0.0002982 secs]
Time elapsed in nano : 654859105
Time elapse in milli : 654


Conclusion : The pause with object pooling are usually 10 to 20 times longer and the throughput is a lot worse!
Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 844
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #25 - Posted 2012-07-31 13:32:15 »

How about fixing the obvious flaws in your benchmark first?

"text"+n means 4 allocations right there (StringBuilder & char[], String & char[])



Besides, synthetic benchmarks rarely showcase the realworld problems of garbage collector induced latency. In your case eden is simply cleared, and as there is no graph of references, no references have to be rewritten, and no memory has to be moved.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #26 - Posted 2012-07-31 13:38:13 »

That benchmark is entirely incorrect.

Cas Smiley

Offline Gudradain
« Reply #27 - Posted 2012-07-31 13:39:24 »

Sad How should I write a good benchmark?
Online Riven
« League of Dukes »

« JGO Overlord »


Medals: 844
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #28 - Posted 2012-07-31 13:39:55 »

Sad How should I write a good benchmark?
What's the point? You're not going to disproof any of our realworld experiences anyway. Pointing

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social
Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #29 - Posted 2012-07-31 13:40:17 »

Well, only test what you're supposed to be testing for a start - Riven beat me to the punch though; your benchmark creates tons of garbage entirely outside what it is you are trying to test for a start.

Cas Smiley

Pages: [1] 2 3
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (37 views)
2014-12-15 09:26:44

Mr.CodeIt (31 views)
2014-12-14 19:50:38

BurntPizza (62 views)
2014-12-09 22:41:13

BurntPizza (99 views)
2014-12-08 04:46:31

JscottyBieshaar (60 views)
2014-12-05 12:39:02

SHC (74 views)
2014-12-03 16:27:13

CopyableCougar4 (77 views)
2014-11-29 21:32:03

toopeicgaming1999 (138 views)
2014-11-26 15:22:04

toopeicgaming1999 (127 views)
2014-11-26 15:20:36

toopeicgaming1999 (38 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!