[External] : Re: New candidate JEP: 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal

John Rose john.r.rose at oracle.com
Fri May 3 21:18:03 UTC 2024


Some of these sorts of use cases could be covered by somehow discovering
a platform-specific parameter N such that a[I] and a[I+N] are not likely
to have false sharing in a data cache, for any I.  (Or a series of N0,
N1, … that prevent a[N0], a[N1]… from being falsely shared.)  David is
right that this parameter depends on the physical array stride,
and that depends on (1) compressed oops or not, and/or (2) flattening
of specific element types.  It depends on cache architecture also.
Throw Leyden into the mix, and it is not clear that just querying
a number is the right answer, either, since it might be predicted
one way by Leyden and end up differently in application deployment.

But this is fragile and error-prone, I think.  It might be nicer
to encapsulate it as folks suggest, even though its users are
power users.  (They are probably power users in a different
domain from VM implementation.)

So I also would prefer to see, rather than a fragile idiom for using
arrays, an API that features an opaque container object and a varhandle
for accessing its (un-)contended “lanes”. The thing would feel like
an “array with varhandles”.  And the opaque object might even be
an array of some sort, if you looked at it closely.  I don’t think
it needs to be wrapped in an envelope to hide its nature.  Power
users ought to know not to look too closely at the container object.

It seems to me that this should be built first on top of the JDK,
not inside the JDK, as a way to shake out the design before the JDK
commits to such a design.

The @Contended feature does not ensure that (un-)contended fields
fall at the start of a cache line, because the VM does not align
objects that precisely (by default).  Likewise, an indexed version
like we are talking about here would not guarantee any particular
offset that the data lanes would fall in (within cache lines).
It would simply set unrelated ones far enough apart to ensure that
that they cannot fall on the same cache line; this requires that
the platform guarantee an appropriate minimum distance.

BTW, the @Contended feature allows fields to be grouped.  A good
corresponding feature for arrays (with varhandles) should allow
for grouping similarly.

So, a first draft API might look something like this:

class ContendedArrayFactory {
  ContendedArrayFactory(Class<?> elementType, int groupSize);
  VarHandle accessor(int which);  // which < groupSize
  ContendedArrayFactory(Class<?> elementType); // default groupSize=1
  VarHandle accessor();  // default which=0
  Object make(int length);  // storage len = roundup(groupSize,D$len)*length
}

In a Leyden setting, each ContendedArrayFactory, and the arrays it
makes, should be (re)generated fresh at startup, if there is suspicion
that data cache size could change.  Frankly, I don’t think there will
be such a suspicion.  Leyden tends to “bake in” environmental settings
such as whether oop compression is enabled.

(The real core libs people should weigh in; I’m just brainstorming here.)

On 3 May 2024, at 11:20, Ron Pressler wrote:

>> On 3 May 2024, at 18:33, David Lloyd <david.lloyd at redhat.com> wrote:
>>
>>
>> On Fri, May 3, 2024 at 10:12 AM Mark Reinhold <mark.reinhold at oracle.com> wrote:
>> https://openjdk.org/jeps/471
>>
>>   Summary: Deprecate the memory-access methods in sun.misc.Unsafe for
>>   removal in a future release.
>>
>>
>> We still use Unsafe fairly often in various Red Hat products (primarily because our baseline support JDK for these products is typically 11 or 17 at present), in a variety of ways for a variety of reasons. Most of these uses of Unsafe should be transitionable to `MemorySegment` using multi-release JARs, and a bit of exploratory work has already been done on this. However there is one unfortunate exception (that I know of).
>>
>> In order to avoid false sharing in certain specific high-concurrency situations, I have lately used arrays to space out certain value locations by using the smallest data cache line size (which is detected via an existing library) and dividing it by the array scale to determine the length of array to allocate in order to accommodate these values. I then use multiples of the cache line size (in bytes), offset from the array base, to locate the elements to access.
>>
>> It is possible to continue this more or less as-is for primitive types (at least, it is if one assumes certain facts around primitive data type size and alignment to be true), but for objects, without knowing their size, we can't know how much padding to reserve around the value location to ensure that the contended values are not falsely shared.
>>
>> I seem to recall (years ago now so I might be a bit fuzzy on it) that the lack of public API around `@Contended` was mildly controversial in the past. The proposed remedy was to use arrays for this purpose, if I recall correctly. However there does not seem to be any good way to do this anymore (at least for objects) without simply guessing, and this seems like a small but significant hole in this plan as it stands for now.
>>
>> It seems to me that the JDK could fill this gap by introducing some API which can construct and provide access to an array or something like it, with striding and/or alignment guarantees that each element will reside on a separate data cache line (or barring that, perhaps using a minimum per-element size and/or alignment that is given as an argument to the factory), and with the gamut of atomic accessors via a `VarHandle` or similar. This could be especially valuable if/when objects start coming in a variety of shapes and sizes in memory, once value types hit.
>>
>> Could such a thing be added into the plan?
>>
>> -- 
>> - DML • he/him
>
> [redirecting to core-libs]
>
> Adding some VarHandle operation that takes into account the cache lines size is interesting — although preserving cache-line *alignment* could be tricky as the GC relocates arrays, so an array element that’s at the start of a cache line at time t0 might not be at the start of a cache line at time t1 — but that’s unrelated to this JEP.
>
> What is related to this JEP is that you’re using Unsafe to determine the size of an oop (in particular, to tell if oops are compressed or no)t. Is that what you’re asking for?
>
> — Ron


More information about the core-libs-dev mailing list