Closed GoogleCodeExporter closed 9 years ago
Do you have data on what version of Android you're building for, and what part
of sun.misc.Unsafe is different or unsupported on Android?
I think the "right solution" is to make this work out of the box on Android,
not to expose any additional API.
Original comment by lowas...@google.com
on 13 Dec 2012 at 10:20
I'm developing on Android 4.2, this all started out because ProGuard complains
about the Striped64 usage of Unsafe.compareAndSwapLong() and
Unsafe.compareAndSwapInt(). I'll be honest and say I don't know if Unsafe is
available at runtime as it is stripped out of my classes.dex due to the
aforementioned ProGuard complaints.
I'm guessing it's using Unsafe rather than AtomicInt/Long for some extra
performance gain. I was contemplating rewriting Striped64 using AtomicX in the
short term or using a more vanilla version using primitive longs.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 10:28
Actually rewriting Striped64 with Atomics looks pretty tough... Maybe a simple
stats counter using Atomics would suffice for both desktop and Android use
cases? I'll leave it in your capable hands.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 10:33
After a quick Google search,
http://www.java2s.com/Open-Source/Android/android-core/platform-libcore/sun/misc
/Unsafe.java.htm seems to suggest at least some version of Android has
Unsafe.cAS{Long,Int}.
Have you tried building your app without ProGuard?
Original comment by lowas...@google.com
on 13 Dec 2012 at 10:35
(FYI, we use Caches in production here at Google, so I suspect we would want an
implementation that performs as well as the current one.)
Original comment by lowas...@google.com
on 13 Dec 2012 at 10:37
At the moment I need to use ProGuard because I'm actually developing in Scala
which is unusable without it. It may be as simple as creating a stub version of
the Unsafe class for use a build time so PG can at least resolve it and then it
should *just work* when deployed onto the device. I'll give that a try and see
how I go.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 10:39
Actually part of the problem is here in Striped64:
java.lang.reflect.Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
On android the field is called "THE_ONE" so it will never resolve.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 10:50
Hmmmkay. Will take a look.
I suspect that in the worst-case scenario, we might set it up to try getting
the Unsafe instance, and upon failure fall back to a simple Atomic
implementation. (This wouldn't be the first place where Guava had a "use
Unsafe if possible, otherwise fall back to this traditional implementation"
algorithm.(
Original comment by lowas...@google.com
on 13 Dec 2012 at 11:05
For Joe Average (ie me) a version of the stats counter based on Atomics would
more than suffice :) I'm actually interested in knowing how big the performance
different is, I'll add it to my never ending stack of cool things to do in my
*spare* time.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 11:07
Some good news. I can confirm that if I hack Striped64 to use "THE_ONE" it now
works. A fix could be as simple as just detecting Android in the environment as
using the alternate string.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 11:13
By works I mean the original exception is not thrown.. I'm now debugging
actually runtime behaviour. Will take a bit longer.
Original comment by alex.rad...@gmail.com
on 13 Dec 2012 at 11:13
It's essential to use "-dontwarn sun.misc.Unsafe" whenever building with
ProGuard. See
https://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava. If you
do that, does any problem remain?
Original comment by kevinb@google.com
on 14 Dec 2012 at 5:00
Whoops, just saw comment 10. We should probably fall back on looking for
THE_ONE. And this goes for our other uses of Unsafe as well. Also, I'm not
sure we ever actually wrote a benchmark proving that this Striped64 stuff was
really worth it in the first place.
Original comment by kevinb@google.com
on 14 Dec 2012 at 5:04
All works as expected now. I created a stub sun.misc.Unsafe so I could hack
Striped64 so I don't need -dontwarn anymore. If I find time I might write a
microbenchmark to see if atomics would cut the mustard ;)
Original comment by a...@radeski.net
on 14 Dec 2012 at 10:18
As promised a micro benchmark... https://github.com/al3ks/cachestatsmicrobench
Unless I stuffed my test up, it's orders of magnitude better with more threads
as per its design spec. So my guess it's worth keeping ;)
Striped64Bench A - Threads=8 - Duration=1.1620s
AtomicBench A - Threads=8 - Duration=14.3500s
Striped64Bench B - Threads=8 - Duration=1.1430s
AtomicBench B - Threads=8 - Duration=15.3570s
Original comment by alex.rad...@gmail.com
on 15 Dec 2012 at 10:46
Actually implemented a thread-local striped long that appears to perform even
better than Striped64. I was trying to mimic Striped64 behaviour without using
sun.misc.Unsafe and to my surprise it actually performs better under heavy
load. It's still available here if you want to have a look:
https://github.com/al3ks/cachestatsmicrobench
Original comment by alex.rad...@gmail.com
on 15 Dec 2012 at 12:22
@Alex:
- Why does the thread computing `LocalStripedLongAdder.sum()` see the changes
made by the others? AFAIK making `LongRef.value` volatile would guarantee it,
but also cause a slowdown by factor 2 (still the fastest in my benchmark).
- Each instance of `LocalStripedLongAdder` needs its own `ThreadLocal`, this
might be costly (or not; this should be analyzed).
- You're measuring the speed of `CacheStats` in isolation. Getting a 10x speed
ratio here may have a similar impact on the cache speed. Or none at all.
- I'd suggest using http://code.google.com/p/caliper; see also the tips there.
Original comment by Maaarti...@gmail.com
on 15 Dec 2012 at 9:00
@Maarti: Wasn't aware of Caliper... I'll take a look.
- No need to make it volatile, as I keep an array of all the instances of
objects created per thread in the 'adders' field. As this is a
CopyOnWriteArrayList, it can safely be used from any thread to grab a snapshot
of the thread-local counters.
- I don't believe the ThreadLocal object cost is high, worth a look but would
be surprised if it's a show stopper.
- I am focusing on CacheStats only, because actually this didn't start out as
me trying to improve performance, I was trying to move away from
sun.misc.Unsafe while not significantly losing performance. It just so happens
it went significantly faster and is much simpler code. I don't really expect
this to improve Cache performance overall, just not to do any harm when stats
are enabled.
Original comment by a...@radeski.net
on 15 Dec 2012 at 9:59
Ah I see what you mean now about LongRef.value and volatile... interesting.
Original comment by a...@radeski.net
on 15 Dec 2012 at 10:05
Not sure when you had a look on github but I've added two more version one with
a StripedLock and another with a StripedAtomic. Both perform to a similar level
as the StripedLocal, although the StripedAtomic is the most thread-safe and
consistent.
The StripedLocal and StripedLock implementations only offer quiescent
consistency over strict consistency. This probably isn't acceptable for Guava...
Original comment by a...@radeski.net
on 15 Dec 2012 at 10:19
Personally, I feel fairly comfortable trusting that Doug Lea's implementation
of LongAdder is going to be pretty effective, though I will do some
benchmarking myself.
In any event, I submitted a fix to make it work with sun.misc.Unsafe on
Android, as well as providing a fallback if sun.misc.Unsafe is unavailable,
which would apply IIRC on e.g. AppEngine. The fix should be mirrored out
externally in the next few days.
Original comment by wasserman.louis
on 15 Dec 2012 at 10:59
You're right, I have fixed up my micro benchmark and Doug's impl is the winner,
although my StripedLocal comes in close with much less code. In any case,
thanks for fixing the main issue.
Original comment by a...@radeski.net
on 15 Dec 2012 at 11:10
Original comment by lowas...@google.com
on 26 Dec 2012 at 5:03
We are also using Guava for Android (we being The Weather Channel) and I just
turned on CacheStats only to discover the same problem with Striped64.
Unfortunately, getting the fix is not an option for us because we are stuck on
this jdk5 version of Guava 13.0 (see link) due to the fact that Android 8
doesn't have NavigableSet (another long story).
http://mvnrepository.com/artifact/com.google.guava/guava-jdk5
So, can I please get a bit of advice? I would really love to be able to get
some stats on our Guava caches. Is there a way to do that that's not too
difficult without waiting for the fix mentioned above?
Thanks!
Original comment by nachobur...@gmail.com
on 2 Jan 2013 at 1:56
Well, Guava 14 will also get a 1.5 backport. Will upgrading to that when it's
out (soon!) be an option?
It may have been a mistake that we backported the Striped64 stuff at all; might
be better overall to roll back that change in the backport branch. Just a
suggestion, which Louis will have better judgment about.
I love weather.com, you guys are awesome. Though I have to say I was out
sledding here in Tahoe yesterday and just five more degrees or so would really
have been appreciated.
Original comment by kevinb@google.com
on 4 Jan 2013 at 8:29
If there is no other option other than to wait for the Guava 14 backport then I
suppose that's all we can do. How soon is soon? :)
If I had my way we would abandon Android API 8, but we still have enough
customers with older devices (barely) to make that not an option.
We have a completely revamped version of our app which is supposed to be code
complete soon (there's that word again) and I would feel all warm and fuzzy if
I could see the cache stats before it goes live, but you know what Mick Jagger
says about getting stuff you want.
We love Guava. It's been a year or two since we integrated it into our product
and it's been great. Especially the caching - which we use like crazy. Thanks!
Original comment by nachobur...@gmail.com
on 4 Jan 2013 at 11:26
This issue has been migrated to GitHub.
It can be found at https://github.com/google/guava/issues/<id>
Original comment by cgdecker@google.com
on 1 Nov 2014 at 4:13
Original comment by cgdecker@google.com
on 3 Nov 2014 at 9:08
Original issue reported on code.google.com by
alex.rad...@gmail.com
on 13 Dec 2012 at 10:12