Hash Surprises with Fixnum, #hash, and #eql?

R

Robert Klemme

In this case, I'm not using a synchronized, atomic, *or* boolean
field. Because of the rarity of Fixnum and Float modification and the
potential for heavy perf impact, I'm considering redefinition of
methods in one thread while another thread is calling those methods as
somewhat undefined, at least for Fixnum and Float. That's not perfect
(JVM could optimize such that one thread's modifications never are
seen by another thread), but it's closer.

Is redefinition per thread a standard JRuby feature or is this
something you would add? If it was something that would need adding I
wouldn't bother to do it. That sounds like a major change.
It's also worth pointing out that usually modifications to Fixnum or
Float are done for DSL purposes, where there's less likelihood of
heavy threading effects.

You're right, though...if I made that field volatile (it doesn't need
to be Atomic, since I only ever read *or* write, never both), the perf
impact would be higher.

Right you are. That occurred to me after posting as well but I didn't
bother to correct myself as the memory effects are identical. The
only difference is one more dereferencing (which can make a difference
as you pointed out).
I operate at too low a level to see the 10000-foot view of application
performance. In other words, I spend my time optimizing individual
core methods, individual Ruby language features, and runtime-level
operations like calls and constant lookup...rather than really looking
at full app performance. Once you get to the scale of a real app, the
performance bottlenecks from badly-written code, slow IO, excessive
object creation, slow libraries and other userland issues almost
always trump runtime-level speed. As an example, I point at the fact
that Ruby 1.9 is almost always much faster than Ruby 1.8, but Rails
under Ruby 1.9 is only marginally faster than Rails on Ruby 1.8 (or so
I've seen when people try to measure it).

That exactly demonstrates your point: Rails will spend most of its
time doing IO (to and from the database, to and from network clients).
OTOH the small difference shows that Rails code cannot be awfully
written because otherwise you would likely notice a bigger difference.
:)
The benefit of a faster and faster runtime is often outweighed by
writing better Ruby code in the first place. But I don't live in the
application world...I work on JRuby low-level performance. You have to
do the rest :)

Will do. :) I'd also love to lend JRuby a hand but unfortunately I
can't find the time right now.

Cheers

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
C

Charles Oliver Nutter

Is redefinition per thread a standard JRuby feature or is this
something you would add? =C2=A0If it was something that would need adding= I
wouldn't bother to do it. =C2=A0That sounds like a major change.

It's something I've thought about, a la Groovy's "categories"
(thread-downstream selector namespacing, basically), but no, there's
no support for this currently in JRuby (and even in Groovy it is an
oft-maligned feature, since it imposes a perf penalty even if you
aren't using it since you have to constantly check a thread-local for
an installed category.)
Will do. :) =C2=A0I'd also love to lend JRuby a hand but unfortunately I
can't find the time right now.

Well, you can always help by submitting docs, editing wiki, answering
JRuby ML questions, answering StackOverflow questions, submitting
JRuby talks to conferences, presenting at user groups and
clubs...perhaps there's something that fits your schedule better :)

- Charlie
 
C

Clifford Heath

Well, it has to get the value from somewhere. ... it's two field
dereferences and one (inlined) virtual method invocation

Ouch. No wonder it hurts. I hadn't looked into the internals of JRuby,
but I assumed that you had some native C (JNI or whatever) in there,
which could make this feasible.

I can totally understand you not wanting any native extensions though!

It seems that a collection of such flags at known offsets inside a
singleton instance could make this a lot quicker. There's a finite
need for such things, so it's not as though it would pervade all of
the interpreter.

Your argument (from a previous response) that cross-thread effects of
monkey-patching was "somewhat undefined" was my thinking also, but
contrary to John's accusation, thought it could be (mostly?) hidden
under the existing synchronisation around method definition.
In general we have followed the path of matching MRI in such cases,
except when there's a very strong argument not to do so. If you can
convince Ruby core, or if you think you have a strong enough case for
deviating from MRI's behavior, go ahead and file the bug and we can
continue exploring it there.

I think the argument that "folk don't do it, therefore they wouldn't"
isn't a strong one. Look for example at Rail's HashWithIndifferentAccess,
which makes string and symbol keys interchangeable. I merely want to
do the same thing with Fixnums and Floats.

If you can't make it quicker (than you outlined), best to drop it I
guess. I've mostly worked around the need for my current library.

Clifford Heath.
 
C

Charles Oliver Nutter

Ouch. No wonder it hurts. I hadn't looked into the internals of JRuby,
but I assumed that you had some native C (JNI or whatever) in there,
which could make this feasible.

I can totally understand you not wanting any native extensions though!

None of the JRuby runtime is native code, and obviously that's how
we'd like to keep it. Even if we were to do it in C, we would still
need to do some object-walking, since JRuby supports having multiple
JRuby instances in the same process (that's how a single JRuby process
can serve many different applications at the same time).
It seems that a collection of such flags at known offsets inside a
singleton instance could make this a lot quicker. There's a finite
need for such things, so it's not as though it would pervade all of
the interpreter.

Such a singleton would still need to be rooted to a specific JRuby
instance. The logic as it stands now is pretty much a
JRuby-instance-global flag.
Your argument (from a previous response) that cross-thread effects of
monkey-patching was "somewhat undefined" was my thinking also, but
contrary to John's accusation, thought it could be (mostly?) hidden
under the existing synchronisation around method definition.

The JVM memory model allows for the JVM to optimize non-volatile
memory accesses away if it can prove the memory location is never
modified by the same thread. Only when specifing volatility will it
guarantee the memory access is always performed with full CPU cache
semantics.
I think the argument that "folk don't do it, therefore they wouldn't"
isn't a strong one. Look for example at Rail's HashWithIndifferentAccess,
which makes string and symbol keys interchangeable. I merely want to
do the same thing with Fixnums and Floats.

Certainly...but at the moment no implementations agree on what is
correct. Your vision might be correct, but until it's "standard" we
would probably not implement it.
If you can't make it quicker (than you outlined), best to drop it I
guess. I've mostly worked around the need for my current library.

It's probably possible to reduce the cost under Java 7, which includes
capabilities to have nearly guard-free dynamic invocation. But there's
no way I know of to make this completely cost-free under Java 6, which
we still support.

- Charlie
 

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

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,737
Latest member
Georgeengab

Latest Threads

Top