Primitive Queue<any T> considerations
Brian Goetz
brian.goetz at oracle.com
Wed Nov 18 16:26:03 UTC 2015
Having null as a universal sentinel is a darn convenient thing. Having
that taken away is going to make life harder for some data structures.
You've already identified a few ways to do that; pick a different
algorithm, use a side-table to indicate special conditions like empty,
or widen the data to include enough space to stuff a boolean. (Optional
has this same issue; the footprint of Optional can be a single ref but
needs an extra bit for primitives like int which use all their bit
patterns.)
Some value types have unused bit patterns which could be pressed into
service as sentinels, but that doesn't help us with int, long, xy-point,
complex, tuple of ints, etc -- these all use all the bit patterns to
encode valid values.
As Richard points out, .NET has a default(T) -- which gives you the
all-zero bit pattern for a given type (the value to which array elements
and fields are initialized) -- and we will have the same (currently we
spell this T.default.) For some situations, this might be enough of a
sentinel and requires no extra hoop jumping (though obviously zero is a
useful integer and so it might not be suitable.)
Our current thoughts are to support some sort of
hand-written-specialization for specific instantiations of a
parameterized type, possibly at both method and whole-class
granularities. For example, one can write an ArrayList<any T> class,
but one might (or might not, there's room for disagreement) want
ArrayList<boolean> to use a bit vector rather than a boolean[].
Similarly, the literature is full of specialized algorithms for hash
maps from int to int.
Applying this to Optional, which is similar to your situation, there
could be a generic implementation (which used a side boolean to indicate
presence) and a specialized implementation for Optional<ref T>, which
omits the boolean and uses null as the sentinel.
tl;dr: we're aware of this issue and have integrated it into the
requirements set :)
On 11/18/2015 8:27 AM, Dávid Karnok wrote:
> Hello,
>
> We are using the SpscArrayQueue (extends Queue<T>) from JCTools quite often
> in our library which exploits null as an indicator if the queue is full or
> not. However, this won't work in case of a Queue<any T> because for
> primitives, zero is a valid value.
>
> I see two options:
>
> - use wider primitive and atomics-acessible types: byte -> int, char ->
> int, short -> int, int -> long, float -> long, double -> 2 slots long and
> long -> 2 slots of long
> - don't use fast-flow and go back to producer-consumer index comparison.
>
> In either case, the internals and the usage have to be changed drastically,
> i.e., switch to AtomicLongArray, how to indicate the queue is actually
> empty when calling poll(), etc. Perhaps a new queue interface is necessary
> where poll returns a boolean indicator and calls a Consumer if the data is
> there.
>
> The examples I saw regarding valhalla showed List<any T> where I'd guess
> the internals of the ArrayList can be altered in a straightforward manner
> but I don't see how this could happen if the algorithm itself has to change
> based on what primitive T is.
>
> What are the plans for supporting such kind of specializations?
>
More information about the valhalla-dev
mailing list