问题
this is my first post on stackoverflow... I hope someone can help me
I have a big performance regression with Java 6 LinkedBlockingQueue
.
In the first thread i generate some objects which i push in to the queue
In the second thread i pull these objects out. The performance regression occurs when the take()
method of the LinkedBlockingQueue
is called frequently.
I monitored the whole program and the take()
method claimed the most time overall.
And the throughput goes from ~58Mb/s to 0.9Mb/s...
the queue pop and take methods ar called with a static method from this class
public class C_myMessageQueue {
private static final LinkedBlockingQueue<C_myMessageObject> x_queue = new LinkedBlockingQueue<C_myMessageObject>( 50000 );
/**
* @param message
* @throws InterruptedException
* @throws NullPointerException
*/
public static void addMyMessage( C_myMessageObject message )
throws InterruptedException, NullPointerException {
x_queue.put( message );
}
/**
* @return Die erste message der MesseageQueue
* @throws InterruptedException
*/
public static C_myMessageObject getMyMessage() throws InterruptedException {
return x_queue.take();
}
}
how can I tune the take()
method to accomplish at least 25Mb/s, or is there a other class I can use which will block when the "queue" is full or empty.
kind regards
Bart
P.S.: sorry for my bad english, I'm from Germany ;)
回答1:
Your producer thread simply puts more elements than the consumer consumes, so the queue eventually hits its capacity limit, thus the producer waits.
Consolidating my original answer since now we have basically the full picture:
- You hit the inherent throughput limit of the
LinkedBlockingQueue
(every queue has one) by doing extremely fastput()
s, where even continualtake()s
, with zero further processing, cannot keep up. (By the way this shows that in this structure, on your JVM and machine anyway, put()s are at least slightly more costly than the reads). - Since there is a particular lock that consumers lock, putting more consumer threads could not possibly help (if your consumer was actually doing some processing and that was bounding the throughput, then adding more consumers would help. There are better queue implementations for a scenario with more than one consumers (or producers), you could try
SynchronousQueue
,ConcurrentLinkedQueue
, and the upcomingTransferQueue
of jsr166y).
Some suggestions:
- Try to make more coarse-grained objects, so that the overhead of queueing each is balanced with the actual work that is offloaded from the producing thread (in your case, it seems you create much communication overhead for objects that represent negligible amounts of work)
- You could also have the producer help the consumer by offloading some consuming work (not much point in waiting idly when there is work to be done).
/updated after John W. rightly pointed out my original answer was misleading
回答2:
I would generally recommend not using a LinkedBlockingQueue in a performance sensitive area of code, use an ArrayBlockingQueue. It will giving a much nicer garbage collection profile and is more cache friendly than the LinkedBlockingQueue.
Try the ArrayBlockingQueue and measure the performance.
The only advantage of the LinkedBlockingQueue is that it can be unbounded, however this is rarely what you actually want. If you have a case where a consumer fails and queues start backing up, having bounded queues allows the system to degrade gracefully rather risk OutOfMemoryErrors that may occur if queues are unbounded.
回答3:
Here are a couple of things to try:
Replace the LinkedBlockingQueue
with an ArrayBlockingQueue
. It has no dangling references and so is better behaved when the queue fills up. Specifically, given the 1.6 implementation of LinkedBlockingQueue, full GC of the elements will not happen until the queue actually becomes empty.
If the producer side is consistently out performing the consumer side, consider using drain
or drainTo
to perform a "bulk" take operation.
Alternatively, have the queue take arrays or Lists of message objects. The the producer fills a List or array with message objects and each put or take moves multiple messages with the same locking overhead. Think of it as a secretary handing you a stack of "While you were out" messages vs. handing them to you one at a time.
回答4:
It's hard to say what happens without knowing something about the filling process.
If addMyMessage
is called less frequently - perhaps because of a performance problem in a whole different part of your application - the take
method has to wait.
That way it looks like take
is the culprit, but actually it's the filling part of your application.
回答5:
Found this interesting post about performance problems due to queue size and garbage collection.
回答6:
It could be possible that your application is affected by the locking related changes in Java 6, especially the "biased locking" feature.
Try disabling it by using the -XX:-UseBiasedLocking
switch and see if that makes a difference.
See this for further information: http://java.sun.com/performance/reference/whitepapers/6_performance.html
回答7:
Can't tell anything for sure. But you can try to change BlockingQueue
implementation (just as an experiment).
You set initial capacity 50k and use LinkedBlockingQueue
. Try ArrayBlockingQueue
with the same capacity, you can also play with fair
parameter.
回答8:
If the raw performance overhead for putting and taking object from your blockingqueues is your bottleneck (and not the slow-producer/consumer problem), you can get huge performance improvements with batching of objects: For instance, instead of putting or taking fine-grained objects, you put or take coarse-grained lists of objects. Here is a code snippet:
ArrayBlockingQueue<List<Object>> Q = new ArrayBlockingQueue<List<Object>>();
// producer side
List<Object> l = new ArrayList<Object>();
for (int i=0; i<100; i++) {
l.add(i); // your initialization here
}
Q.put(l);
// consumer side
List<Object> l2 = Q.take();
// do something
Batching can boost your performance by an order of magnitude.
来源:https://stackoverflow.com/questions/2644688/java-performance-problem-with-linkedblockingqueue