Followup to "Serious concurrency problems on fast systems"

R

Robert Klemme

Hi,

the recent thread "Serious concurrency problems on fast systems"
inspired me to put together a small demo that shows how different ways
of concurrency control affect execution. The example shares a dummy
configuration with a single long value. Multiple threads access the
shared resource read only and depending on test scenario a single thread
updates it from time to time. Concurrency control is done in these ways:

1. Plain synchronized on a single shared resource.

2. Synchronized but with redundant storage and update via observer pattern.

3. Copy on write with an immutable object and an AtomicReference.

You can download it here

http://docs.google.com/leaf?id=0B7Q7WZzdIMlIMDI4ZDk0ZGItYzk1My00ZTc1LWJlYmQtNDYzNWNlNzA3YTJm&hl=en

Have fun

robert
 
L

Lew

Robert said:
the recent thread "Serious concurrency problems on fast systems"
inspired me to put together a small demo that shows how different ways
of concurrency control affect execution.  The example shares a dummy
configuration with a single long value.  Multiple threads access the
shared resource read only and depending on test scenario a single thread
updates it from time to time.  Concurrency control is done in these ways:

1. Plain synchronized on a single shared resource.

2. Synchronized but with redundant storage and update via observer pattern.

3. Copy on write with an immutable object and an AtomicReference.

What about 'volatile'?

or
<http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/
AtomicLong.html>
?

Thank you for your contribution.

Aside from the semantics that you illustrate for updatable values,
nothing beats immutable (read-only, value fixed at initialization)
objects for safe and fast concurrency.

"But that's only for when you can get away with read-only values!" I
hear someone saying.

Yes, and one must always consider when one can get away with read-only
values. I've been on several projects where programmers used mutable
objects for concurrently-accessed objects with read-only usage. Often
they used lazy initialization, with broken double-checked locking yet!
for objects that never change value once initialized. Had they
considered the advantages of immutability, even constancy (e.g., for
read-only Strings), they would have prevented the concurrency and
performance issues their misguided implementations caused.
 
R

Robert Klemme


That would have been an alternative for the special scenario here (just
a single long). But I wanted to mimic a configuration which typically
consists of multiple values while at the same time save the effort of
providing more properties. From a pedagogical point of view it probably
would have been better to have at least one additional property.
Thank you for your contribution.

You're welcome!
Aside from the semantics that you illustrate for updatable values,
nothing beats immutable (read-only, value fixed at initialization)
objects for safe and fast concurrency.

"But that's only for when you can get away with read-only values!" I
hear someone saying.

Yes, and one must always consider when one can get away with read-only
values.

I completely agree: if you can get away with immutability that's the
best option you can have. Often though, changing the configuration at
runtime (even if rarely) is desirable for some kinds of applications in
order to avoid the shutdown restart loop.
I've been on several projects where programmers used mutable
objects for concurrently-accessed objects with read-only usage. Often
they used lazy initialization, with broken double-checked locking yet!
*shudder*

for objects that never change value once initialized. Had they
considered the advantages of immutability, even constancy (e.g., for
read-only Strings), they would have prevented the concurrency and
performance issues their misguided implementations caused.

Absolutely. A typical scenario where lazy initialization looks good at
first sight but is rather weak in hindsight is the typical singleton
pattern where the static reference is initialized in the accessor.
While you sometimes may get away with leaving this unsynchronized and
thus potentially initializing multiple times often you need to
synchronize. Voilá, there is the bottleneck - even on systems with few
cores (as my example showed).

Kind regards

robert
 
R

Robert Klemme

Am 02.07.2010 16:40, schrieb Robert Klemme:

You could also try the by Java provided (Reentrant)ReadWriteLock ...
it might (I am not sure there) be cheaper with lots of reads and few
writes than normal synchronization.

That's an excellent idea:

https://docs.google.com/leaf?id=0B7...MTExMmYwZjcwODAz&sort=name&layout=list&num=50

However, while it is faster than plain old synchronized on the global
resource, this confirms that centralized locking is an inferior approach
in highly concurrent systems. :)

Kind regards

robert
 
K

Kevin McMurtrie

Robert Klemme said:
That's an excellent idea:

https://docs.google.com/leaf?id=0B7Q7WZzdIMlIZjAxZDg5YzYtNzE3YS00ZjAyLTgzMmQtM
TExMmYwZjcwODAz&sort=name&layout=list&num=50

However, while it is faster than plain old synchronized on the global
resource, this confirms that centralized locking is an inferior approach
in highly concurrent systems. :)

Kind regards

robert

ALL thread interaction on a multi-CPU system is slow. Try 10 threads
writing to adjacent memory in a shared array compared to 10 threads
writing to memory in their own array. Not only is that a baseline for
all concurrency techniques, but it can hinder performance in non-obvious
areas.

In my benchmarks, a 'synchronized' block has less overhead than all of
the Java 1.5 lock mechanisms. If you must alter a set of data in a
consistent state, it seems to be the way to go. Its downside is that a
thread can use up its CPU quanta while holding the lock and everything
else piles up for a while. Java 1.5 locks give you options for shared
read locks and fail-fast lock acquisition that may reduce blocking.

For syncing a single primitive, the CAS operations in 'Atomic...'
classes are faster than a 'synchronized' block. Not super fast, because
of cache syncing, but much faster. CAS loops have a worst-case of
eating CPU time but they don't have a worst-case of waiting for a
suspended thread to release a lock.

If 'wait' and 'notify' are needed without the locking of 'synchronized',
it can be improved using LockSupport.park() and unpark(). I haven't
tested on Solaris or Linux yet, but these two hit a bottleneck in the
Mac OS X kernel.
 
R

Robert Klemme

ALL thread interaction on a multi-CPU system is slow. Try 10 threads
writing to adjacent memory in a shared array compared to 10 threads
writing to memory in their own array. Not only is that a baseline for
all concurrency techniques, but it can hinder performance in non-obvious
areas.

In my benchmarks, a 'synchronized' block has less overhead than all of
the Java 1.5 lock mechanisms. If you must alter a set of data in a
consistent state, it seems to be the way to go.

The demo code shows that although you can get pretty fast with
synchronized there are faster alternatives. Especially the variant with
an AtomicReference (or plain volatile) is dramatically faster. It
depends on the situation what to choose. synchronize is not the one
size fits all solution to concurrency issues.
Its downside is that a
thread can use up its CPU quanta while holding the lock and everything
else piles up for a while. Java 1.5 locks give you options for shared
read locks and fail-fast lock acquisition that may reduce blocking.

Exactly.

Cheers

robert
 
K

Knute Johnson

The demo code shows that although you can get pretty fast with
synchronized there are faster alternatives. Especially the variant with
an AtomicReference (or plain volatile) is dramatically faster. It
depends on the situation what to choose. synchronize is not the one size
fits all solution to concurrency issues.


Exactly.

Cheers

robert

Just as there is a diminishing return to more threads.
 
L

Lew

Knute said:
Just as there is a diminishing return to more threads.

Or more CPUs on a board, or more nodes in a cluster, or more programmers on a
team, ...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top