On Feb 25, 2014, at 9:38 PM, Brian Burkhalter <brian.burkhal...@oracle.com> 
wrote:

> 
> On Feb 20, 2014, at 1:42 AM, Paul Sandoz wrote:
> 
>> Not sure the static powerCache field, in the original code, needs to be 
>> volatile either:
>> 
>> 1137     private static volatile BigInteger[][] powerCache;
> 
> Is there consensus on whether "volatile" is necessary here?
> 

Looking back at the discussions i believe it was made volatile to ensure 
threads don't observe a partially updated and published cache lines.

Since we are already using Unsafe for deserialization I think it might be 
possible to do the following instead (warning: not tested!):

    /**
     * The cache of powers of each radix.  This allows us to not have to
     * recalculate powers of radix^(2^n) more than once.  This speeds
     * Schoenhage recursive base conversion significantly.
     */
    private static final BigInteger[][] powerCache;

...

    private static BigInteger getRadixConversionCache(int radix, int exponent) {
        // Relaxed read of cache line from power cache
        BigInteger[] cacheLine = powerCache[radix];
        if (exponent < cacheLine.length) {
            return cacheLine[exponent];
        }

        // Copy and expand the cache line up to and including the exponent
        int oldLength = cacheLine.length;
        cacheLine = Arrays.copyOf(cacheLine, exponent + 1);
        for (int i = oldLength; i <= exponent; i++) {
            cacheLine[i] = cacheLine[i - 1].pow(2);
        }

        // Lazy write of new cache line to power cache
        // Ensure all writes to the new cache line are ordered before
        // it's publication in the power cache
        UnsafeHolder.lazySetCacheLine(powerCache, radix, cacheLine);

        return cacheLine[exponent];
    }

    private static class UnsafeHolder {
        private static final sun.misc.Unsafe unsafe;
        private static final long signumOffset;
        private static final long magOffset;
        private static final int biaOffset;
        private static final int biaShift;

        static {
            try {
                unsafe = sun.misc.Unsafe.getUnsafe();
                signumOffset = unsafe.objectFieldOffset
                    (BigInteger.class.getDeclaredField("signum"));
                magOffset = unsafe.objectFieldOffset
                    (BigInteger.class.getDeclaredField("mag"));

                biaOffset = unsafe.arrayBaseOffset(BigInteger[][].class);
                int scale = unsafe.arrayIndexScale(BigInteger[][].class);
                if ((scale & (scale - 1)) != 0)
                    throw new Error("data type scale not a power of two");
                biaShift = 31 - Integer.numberOfLeadingZeros(scale);

            } catch (Exception ex) {
                throw new ExceptionInInitializerError(ex);
            }
        }

        static void putSign(BigInteger bi, int sign) {
            unsafe.putIntVolatile(bi, signumOffset, sign);
        }

        static void putMag(BigInteger bi, int[] magnitude) {
            unsafe.putObjectVolatile(bi, magOffset, magnitude);
        }

        static void lazySetCacheLine(BigInteger[][] cache, int radix,
                                        BigInteger[] cacheLine) {
            unsafe.putOrderedObject(cache, biaOffset + ((long) radix << 
biaShift),
                                    cacheLine);
        }
    }

Paul.

Reply via email to