You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Peter Steijn <ps...@gmail.com> on 2006/04/01 14:57:26 UTC

[pool] Proposal: New Pool Impl -- GeneratorObjectPool

My name is Peter Steijn.  I am an undergraduate at the University of
Delaware, where I am working on an independent research project.  I have
been working on an optimization to object pooling upon which I have based a
new object pool implementation -- GeneratorObjectPool.  The following is
about the implemented changes to how object pooling works.

Before I go any further I would like to thank my faculty mentor, Professor
Phillip Conrad, for his insight and support.  I would also like to thank
apache contributor Sandy McArthur, who has spent the past few weeks
discussing my ideas, providing guidance and looking over my [sometimes
inane] attempts at coding.

You can access my objects at:  http://copland.udel.edu/~psteijn/apache/
  *GeneratorObjectPool.java -- the object pool implementation
  *GeneratorObjectPoolFactory.java -- basically GeneralObjectPoolFactory
  *TestGeneratorObjectPool.java -- JUnit tests modified to work with
GeneratorObjectPool, taken from TestGeneralObjectPool but modified because
of some changed assumptions in how some methods work (addObject in
particular).

GeneratorObjectPool changes the behavior of object pooling in the
borrowObject method.  Previous object pooling implementations, when
attempting to borrow an object from an empty pool, would create an object
for the requestor; blocking until the object was created and then returning
that object.  This is especially inefficient for object pooling, where the
objects you are creating are expected to be very expensive to create (read
http://www.theserverside.com/news/thread.tss?thread_id=37146 for an argument
on why you should only pool the heaviest of objects).  For example, creation
of a JDBC connection to a mysql database takes at least 25 milliseconds
(with virtually 0 latency) and seconds in even a minimally latent network.
In contrast to the creation of these objects, the act of borrowing, using
and returning the objects to the pool by a requestor usually takes less time
on the order of powers of ten.  Using a JDBC connection for an average query
can take as few as 5 milliseconds.  Using other objects that don't involve
sending data over a network would take even less time.

The observation that I am leading up to is this:
     Most likely the objects that are loaned out, making the pool empty for
your request (which is then going to sit there for up to seconds blocking
while your object is being made) are going to come back far before the
object you are creating would be ready.  The difference in time between the
first object that returns to the pool and when the requestor's object gets
created is avoidable blocking time.  In other words, we can save turn-around
time on requests which is what object pooling is all about!

GeneratorObjectPool does not block to create an object when a request hits
an empty pool.  Instead it schedules a TimerTask to run at the earliest time
possible.  This TimerTask creates an object and puts it into the pool.
While the TimerTask is running, the requestor is looking for any object to
return to the pool, not just the one that it asked to be created.  This is
done with a LinkedBlockingQueue (requires java 1.5.0_06 - there is a serious
bug in LinkedBlockingQueue in previous 1.5 releases [
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6215625], but I am also
planning to implement a version of GeneratorObjectPool that can run on java
1.3).

When hitting an empty object pool, the requestor should notice a very large
performance increase.  All other times the pool should not experience any
performance degradation.

Important issues to note:
-you can no longer assume that an additional object is in the pool directly
after calling the addObject method.  addObject only schedules an object to
be created and put into the pool.

I feel that I have justified why this optimization is necessary.  I look
forward to constructive criticism and any discussion of my ideas.

-Peter K. Steijn

PS - I have not done performance testing on my implementation yet, and have
only tested it on existing unit tests.  This is just to introduce my ideas
to the community.  The code is in no way guaranteed to be robust code.

Re: [pool] Proposal: New Pool Impl -- GeneratorObjectPool

Posted by Sandy McArthur <sa...@apache.org>.
On 4/1/06, Peter Steijn <ps...@gmail.com> wrote:
> > This should probably be changed. There is no reason addObject cannot
> > have the old blocking behavior and still have the generator behavior
> > for calls to borrowObject that need to create a new pooled object.
> >
> > This change will break code like:
> >
> > while (pool.getNumIdle() < 5) {
> >   pool.addObject();
> > }
> > by the time the getNumIdle returns 5 about 1000 objects will be
> > scheduled to be added to the pool.
>
>
> true, but I would still like to give the user the option to call a
> non-blocking addObject in case they want to use the functionality.
>
> Perhaps I should provide a function overloaded type where you can either
> call
> addObject()
> or
> addObject(boolean)  where boolean is a flag that sets whether the addObject
> should block or not?

That's fine, it's just the behavior of methods specified by ObjectPool
needs to be preserved.


--
Sandy McArthur

"He who dares not offend cannot be honest."
- Thomas Paine

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [pool] Proposal: New Pool Impl -- GeneratorObjectPool

Posted by Peter Steijn <ps...@gmail.com>.
> This should probably be changed. There is no reason addObject cannot
> have the old blocking behavior and still have the generator behavior
> for calls to borrowObject that need to create a new pooled object.
>
> This change will break code like:
>
> while (pool.getNumIdle() < 5) {
>   pool.addObject();
> }
> by the time the getNumIdle returns 5 about 1000 objects will be
> scheduled to be added to the pool.


true, but I would still like to give the user the option to call a
non-blocking addObject in case they want to use the functionality.

Perhaps I should provide a function overloaded type where you can either
call
addObject()
or
addObject(boolean)  where boolean is a flag that sets whether the addObject
should block or not?

Re: [pool] Proposal: New Pool Impl -- GeneratorObjectPool

Posted by Sandy McArthur <sa...@gmail.com>.
On 4/1/06, Peter Steijn <ps...@gmail.com> wrote:
> My name is Peter Steijn.  I am an undergraduate at the University of
> Delaware, where I am working on an independent research project.  I have
> been working on an optimization to object pooling upon which I have based a
> new object pool implementation -- GeneratorObjectPool.  The following is
> about the implemented changes to how object pooling works.
>
> Before I go any further I would like to thank my faculty mentor, Professor
> Phillip Conrad, for his insight and support.  I would also like to thank
> apache contributor Sandy McArthur, who has spent the past few weeks
> discussing my ideas, providing guidance and looking over my [sometimes
> inane] attempts at coding.
>
> You can access my objects at:  http://copland.udel.edu/~psteijn/apache/
>   *GeneratorObjectPool.java -- the object pool implementation
>   *GeneratorObjectPoolFactory.java -- basically GeneralObjectPoolFactory
>   *TestGeneratorObjectPool.java -- JUnit tests modified to work with
> GeneratorObjectPool, taken from TestGeneralObjectPool but modified because
> of some changed assumptions in how some methods work (addObject in
> particular).
>
> GeneratorObjectPool changes the behavior of object pooling in the
> borrowObject method.  Previous object pooling implementations, when
> attempting to borrow an object from an empty pool, would create an object
> for the requestor; blocking until the object was created and then returning
> that object.  This is especially inefficient for object pooling, where the
> objects you are creating are expected to be very expensive to create (read
> http://www.theserverside.com/news/thread.tss?thread_id=37146 for an argument
> on why you should only pool the heaviest of objects).  For example, creation
> of a JDBC connection to a mysql database takes at least 25 milliseconds
> (with virtually 0 latency) and seconds in even a minimally latent network.
> In contrast to the creation of these objects, the act of borrowing, using
> and returning the objects to the pool by a requestor usually takes less time
> on the order of powers of ten.  Using a JDBC connection for an average query
> can take as few as 5 milliseconds.  Using other objects that don't involve
> sending data over a network would take even less time.
>
> The observation that I am leading up to is this:
>      Most likely the objects that are loaned out, making the pool empty for
> your request (which is then going to sit there for up to seconds blocking
> while your object is being made) are going to come back far before the
> object you are creating would be ready.  The difference in time between the
> first object that returns to the pool and when the requestor's object gets
> created is avoidable blocking time.  In other words, we can save turn-around
> time on requests which is what object pooling is all about!
>
> GeneratorObjectPool does not block to create an object when a request hits
> an empty pool.  Instead it schedules a TimerTask to run at the earliest time
> possible.  This TimerTask creates an object and puts it into the pool.
> While the TimerTask is running, the requestor is looking for any object to
> return to the pool, not just the one that it asked to be created.  This is
> done with a LinkedBlockingQueue (requires java 1.5.0_06 - there is a serious
> bug in LinkedBlockingQueue in previous 1.5 releases [
> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6215625], but I am also
> planning to implement a version of GeneratorObjectPool that can run on java
> 1.3).
>
> When hitting an empty object pool, the requestor should notice a very large
> performance increase.  All other times the pool should not experience any
> performance degradation.
>
> Important issues to note:
> -you can no longer assume that an additional object is in the pool directly
> after calling the addObject method.  addObject only schedules an object to
> be created and put into the pool.

This should probably be changed. There is no reason addObject cannot
have the old blocking behavior and still have the generator behavior
for calls to borrowObject that need to create a new pooled object.

This change will break code like:

while (pool.getNumIdle() < 5) {
  pool.addObject();
}
by the time the getNumIdle returns 5 about 1000 objects will be
scheduled to be added to the pool.

> I feel that I have justified why this optimization is necessary.  I look
> forward to constructive criticism and any discussion of my ideas.
>
> -Peter K. Steijn
>
> PS - I have not done performance testing on my implementation yet, and have
> only tested it on existing unit tests.  This is just to introduce my ideas
> to the community.  The code is in no way guaranteed to be robust code.
>
>


--
Sandy McArthur

"He who dares not offend cannot be honest."
- Thomas Paine

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Re: [pool] Proposal: New Pool Impl -- GeneratorObjectPool

Posted by Peter Steijn <ps...@gmail.com>.
some promising performance tests:

using pool's 1.3.1 PerformanceTest.java

------------------------------------
on GeneralObjectPool
------------------------------------

Increase threads
-----------------------------------------
nrIterations: 1
nrThreads: 50
maxActive: 5
maxIdle: 5
nrSamples: 50
totalBorrowTime: 120364
totalReturnTime: 5519
avg BorrowTime: 2407
avg ReturnTime: 110
-----------------------------------------
nrIterations: 1
nrThreads: 100
maxActive: 5
maxIdle: 5
nrSamples: 100
totalBorrowTime: 494611
totalReturnTime: 11035
avg BorrowTime: 4946
avg ReturnTime: 110
-----------------------------------------
nrIterations: 1
nrThreads: 200
maxActive: 5
maxIdle: 5
nrSamples: 200
totalBorrowTime: 2048368
totalReturnTime: 22732
avg BorrowTime: 10241
avg ReturnTime: 113
-----------------------------------------
nrIterations: 1
nrThreads: 400
maxActive: 5
maxIdle: 5
nrSamples: 400
totalBorrowTime: 7144609
totalReturnTime: 46740
avg BorrowTime: 17861
avg ReturnTime: 116
Increase threads & poolsize
-----------------------------------------
nrIterations: 1
nrThreads: 50
maxActive: 5
maxIdle: 5
nrSamples: 50
totalBorrowTime: 120005
totalReturnTime: 5528
avg BorrowTime: 2400
avg ReturnTime: 110
-----------------------------------------
nrIterations: 1
nrThreads: 100
maxActive: 10
maxIdle: 10
nrSamples: 100
totalBorrowTime: 476851
totalReturnTime: 23280
avg BorrowTime: 4768
avg ReturnTime: 232
-----------------------------------------
nrIterations: 1
nrThreads: 200
maxActive: 20
maxIdle: 20
nrSamples: 200
totalBorrowTime: 1948777
totalReturnTime: 98885
avg BorrowTime: 9743
avg ReturnTime: 494
-----------------------------------------
nrIterations: 1
nrThreads: 400
maxActive: 40
maxIdle: 40
nrSamples: 400
totalBorrowTime: 7691536
totalReturnTime: 393762
avg BorrowTime: 19228
avg ReturnTime: 984
Increase maxIdle
-----------------------------------------
nrIterations: 1
nrThreads: 400
maxActive: 40
maxIdle: 5
nrSamples: 400
totalBorrowTime: 101715823
totalReturnTime: 5503246
avg BorrowTime: 254289
avg ReturnTime: 13758
-----------------------------------------
nrIterations: 1
nrThreads: 400
maxActive: 40
maxIdle: 40
nrSamples: 400
totalBorrowTime: 7631143
totalReturnTime: 393899
avg BorrowTime: 19077
avg ReturnTime: 984


------------------------------------
on GeneratorObjectPool
------------------------------------

 Increase threads  -----------------------------------------  nrIterations:
1  nrThreads: 50  maxActive: 5  maxIdle: 5  nrSamples: 50  totalBorrowTime:
25794  totalReturnTime: 500  avg BorrowTime: 515  avg ReturnTime: 10
-----------------------------------------  nrIterations: 1  nrThreads:
100  maxActive:
5  maxIdle: 5  nrSamples: 100  totalBorrowTime: 45228 
totalReturnTime: 2052  avg
BorrowTime: 452  avg ReturnTime: 20
-----------------------------------------  nrIterations: 1  nrThreads:
200  maxActive:
5  maxIdle: 5  nrSamples: 200  totalBorrowTime: 417837  totalReturnTime:
9663  avg BorrowTime: 2089  avg ReturnTime: 48
-----------------------------------------  nrIterations: 1  nrThreads:
400  maxActive:
5  maxIdle: 5  nrSamples: 400  totalBorrowTime: 823686  totalReturnTime:
14502  avg BorrowTime: 2059  avg ReturnTime: 36  Increase threads & poolsize
-----------------------------------------  nrIterations: 1  nrThreads:
50  maxActive:
5  maxIdle: 5  nrSamples: 50  totalBorrowTime: 31535  totalReturnTime: 512  avg
BorrowTime: 630  avg ReturnTime: 10
-----------------------------------------  nrIterations: 1  nrThreads:
100  maxActive:
10  maxIdle: 10  nrSamples: 100  totalBorrowTime: 56098  totalReturnTime:
1393  avg BorrowTime: 560  avg ReturnTime: 13
-----------------------------------------  nrIterations: 1  nrThreads:
200  maxActive:
20  maxIdle: 20  nrSamples: 200  totalBorrowTime: 176130  totalReturnTime:
7369  avg BorrowTime: 880  avg ReturnTime: 36
-----------------------------------------  nrIterations: 1  nrThreads:
400  maxActive:
40  maxIdle: 40  nrSamples: 400  totalBorrowTime: 611424  totalReturnTime:
33628  avg BorrowTime: 1528  avg ReturnTime: 84  Increase maxIdle
-----------------------------------------  nrIterations: 1  nrThreads:
400  maxActive:
40  maxIdle: 5  nrSamples: 400  totalBorrowTime: 717263  totalReturnTime:
57498  avg BorrowTime: 1793  avg ReturnTime: 143
-----------------------------------------  nrIterations: 1  nrThreads:
400  maxActive:
40  maxIdle: 40  nrSamples: 400  totalBorrowTime: 692964  totalReturnTime:
34029  avg BorrowTime: 1732  avg ReturnTime: 85

Re: [pool] Proposal: New Pool Impl -- GeneratorObjectPool

Posted by Peter Steijn <ps...@gmail.com>.
>
> Also, I don't think it saves any time if you create the pool and then
> immediately extract an object from the pool, so that would need to be
> documented.
>
> Stephen
>
> The act of creating an object when you hit an empty object pool is
important to grow the pool size even if you are not the requestor that
eventually utilizes that object.

You are correct, if there are no objects returned to the pool (like when you
have a fresh pool), then you get no performance improvement.  However, you
get no performance hit either.

Re: [pool] Proposal: New Pool Impl -- GeneratorObjectPool

Posted by Stephen Colebourne <sc...@btopenworld.com>.
Peter Steijn wrote:
> GeneratorObjectPool changes the behavior of object pooling in the
> borrowObject method.  Previous object pooling implementations, when
> attempting to borrow an object from an empty pool, would create an object
> for the requestor; blocking until the object was created and then returning
> that object.  This is especially inefficient for object pooling...
> <snip>
> GeneratorObjectPool does not block to create an object when a request hits
> an empty pool.  Instead it schedules a TimerTask to run at the earliest time
> possible.  This TimerTask creates an object and puts it into the pool.
> While the TimerTask is running, the requestor is looking for any object to
> return to the pool, not just the one that it asked to be created.

This sounds like a good enhancement to [pool]. As you indicated a JDK1.3 
version would be useful.

Also, I don't think it saves any time if you create the pool and then 
immediately extract an object from the pool, so that would need to be 
documented.

Stephen


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org