Numeric

Doug Lea dl at cs.oswego.edu
Fri Sep 14 04:28:00 PDT 2012


Picking this up from a set of exchanges on old
lambda-lib list, and first recycling initial rationale:

The main reason for primitive specializations for bulk ops
is to avoid boxing inside inner loops. Unless optimized away
(which in practice is almost always too hard for compilers/JITs),
not only is it slow to begin with, it doesn't get much
faster under parallelism because it spews garbage and
disrupts memory locality.

The inner loop case is the most glaring problem, but the same
issues arise during any combination/reduction/collection
steps among a set of subtasks. As in
   r = combine(leftTask.join(), rightTask.join());
As well as client access of results, as in:
   r = p.invoke(...)
Depending on all sorts of things, these cases can be
as numerous and problematic as the inner-loop case.
Wishing that they weren't as problematic isn't a good solution.

Long-term, we need primitive specialization of generics.  But shorter
term, there's an intermediate solution that reduces both boxing and
combinatorial explosions of special forms. The tradeoff is to accept
some virtualness, plus a bit of tedium on the part of components
implementing it:

/**
  * Interface defining access methods for classes that provide numeric
  * results. A {@code Numeric} is not itself an instance of {@link
  * java.lang.Number}, but provides numeric methods to access its
  * primary result or property; normally via the method corresponding
  * to the listed {@code PreferredType} parameter. However, any
  * implementation of this interface must define the non-preferred
  * methods as well (typically by casting the results of the
  * preferred form).
  */
public interface Numeric<PreferredType extends Number> {
     long getLong();
     int getInt();
     short getShort();
     byte getByte();
     double getDouble();
     float getFloat();
}

So for example, someone could define
   class MyFutureDouble implements Future<Double>, Numeric<Double>;
which could then be used as
   f = new MyFutureDouble();
   // .. async process f ...
   double d = f.getDouble();

This is a slightly odd interface because the PreferredType parameter
exists just to communicate the preferred extraction method to
client programmers. Looking ahead though, it might also serve as a
heuristic guide for future efforts on automated generics specialization.

The required tedium is that any class implementing Numeric
will need to boringly implement five of the methods in terms
of the one version corresponding to the preferred type. For
example, class MyFutureDouble would need:
  public double getDouble() { return computeResult(); }
  public long getLong() { return (long)getDouble(); }
  public int getInt() { return (int)getDouble(); }
  public float getFloat() { return (float)getDouble(); }
  public short getShort() { return (short)getDouble(); }
  public byte getByte() { return (byte)getDouble(); }


It would be possible but probably not worthwhile to create
six subinterfaces that provide defaults of these forms.

Adopting this has a surprisingly widespread impact on the form of some
lambdaized/parallelized APIs (mainly internal ones), so it would be a
good idea to decide on it soon. For example. I could use this now
in methods like CHM.Parallel().reduceValuesToLong.

To be maximally useful, this should probably go into java.lang,
along side Number. (Otherwise, I'd just stick it in java.util.concurrent
for our needs and be done with it.)

It would be even nicer if there were a way to do something similar for
arguments to function-like classes in addition to their results, but
no solution along these lines applies.

-Doug


More information about the lambda-libs-spec-experts mailing list