1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.marlin;
  27 
  28 import java.util.Arrays;
  29 import static sun.java2d.marlin.MarlinUtils.logInfo;
  30 
  31 public final class ArrayCacheConst implements MarlinConst {
  32 
  33     static final int BUCKETS = 8;
  34     static final int MIN_ARRAY_SIZE = 4096;
  35     // maximum array size
  36     static final int MAX_ARRAY_SIZE;
  37     // threshold below to grow arrays by 4
  38     static final int THRESHOLD_SMALL_ARRAY_SIZE = 4 * 1024 * 1024;
  39     // threshold to grow arrays only by (3/2) instead of 2
  40     static final int THRESHOLD_ARRAY_SIZE;
  41     // threshold to grow arrays only by (5/4) instead of (3/2)
  42     static final long THRESHOLD_HUGE_ARRAY_SIZE;
  43     static final int[] ARRAY_SIZES = new int[BUCKETS];
  44 
  45     static {
  46         // initialize buckets for int/float arrays
  47         int arraySize = MIN_ARRAY_SIZE;
  48 
  49         int inc_lg = 2; // x4
  50 
  51         for (int i = 0; i < BUCKETS; i++, arraySize <<= inc_lg) {
  52             ARRAY_SIZES[i] = arraySize;
  53 
  54             if (DO_TRACE) {
  55                 logInfo("arraySize[" + i + "]: " + arraySize);
  56             }
  57 
  58             if (arraySize >= THRESHOLD_SMALL_ARRAY_SIZE) {
  59                 inc_lg = 1; // x2
  60             }
  61         }
  62         MAX_ARRAY_SIZE = arraySize >> inc_lg;
  63 
  64         if (MAX_ARRAY_SIZE <= 0) {
  65             throw new IllegalStateException("Invalid max array size !");
  66         }
  67 
  68         THRESHOLD_ARRAY_SIZE       =  16  * 1024 * 1024; // >16M
  69         THRESHOLD_HUGE_ARRAY_SIZE  =  48L * 1024 * 1024; // >48M
  70 
  71         if (DO_STATS || DO_MONITORS) {
  72             logInfo("ArrayCache.BUCKETS        = " + BUCKETS);
  73             logInfo("ArrayCache.MIN_ARRAY_SIZE = " + MIN_ARRAY_SIZE);
  74             logInfo("ArrayCache.MAX_ARRAY_SIZE = " + MAX_ARRAY_SIZE);
  75             logInfo("ArrayCache.ARRAY_SIZES = "
  76                     + Arrays.toString(ARRAY_SIZES));
  77             logInfo("ArrayCache.THRESHOLD_ARRAY_SIZE = "
  78                     + THRESHOLD_ARRAY_SIZE);
  79             logInfo("ArrayCache.THRESHOLD_HUGE_ARRAY_SIZE = "
  80                     + THRESHOLD_HUGE_ARRAY_SIZE);
  81         }
  82     }
  83 
  84     private ArrayCacheConst() {
  85         // Utility class
  86     }
  87 
  88     // small methods used a lot (to be inlined / optimized by hotspot)
  89 
  90     static int getBucket(final int length) {
  91         for (int i = 0; i < ARRAY_SIZES.length; i++) {
  92             if (length <= ARRAY_SIZES[i]) {
  93                 return i;
  94             }
  95         }
  96         return -1;
  97     }
  98 
  99     /**
 100      * Return the new array size (~ x2)
 101      * @param curSize current used size
 102      * @param needSize needed size
 103      * @return new array size
 104      */
 105     public static int getNewSize(final int curSize, final int needSize) {
 106         // check if needSize is negative or integer overflow:
 107         if (needSize < 0) {
 108             // hard overflow failure - we can't even accommodate
 109             // new items without overflowing
 110             throw new ArrayIndexOutOfBoundsException(
 111                           "array exceeds maximum capacity !");
 112         }
 113         assert curSize >= 0;
 114         final int initial = curSize;
 115         int size;
 116         if (initial > THRESHOLD_ARRAY_SIZE) {
 117             size = initial + (initial >> 1); // x(3/2)
 118         } else {
 119             size = (initial << 1); // x2
 120         }
 121         // ensure the new size is >= needed size:
 122         if (size < needSize) {
 123             // align to 4096 (may overflow):
 124             size = ((needSize >> 12) + 1) << 12;
 125         }
 126         // check integer overflow:
 127         if (size < 0) {
 128             // resize to maximum capacity:
 129             size = Integer.MAX_VALUE;
 130         }
 131         return size;
 132     }
 133 
 134     /**
 135      * Return the new array size (~ x2)
 136      * @param curSize current used size
 137      * @param needSize needed size
 138      * @return new array size
 139      */
 140     public static long getNewLargeSize(final long curSize, final long needSize) {
 141         // check if needSize is negative or integer overflow:
 142         if ((needSize >> 31L) != 0L) {
 143             // hard overflow failure - we can't even accommodate
 144             // new items without overflowing
 145             throw new ArrayIndexOutOfBoundsException(
 146                           "array exceeds maximum capacity !");
 147         }
 148         assert curSize >= 0L;
 149         long size;
 150         if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) {
 151             size = curSize + (curSize >> 2L); // x(5/4)
 152         } else if (curSize > THRESHOLD_ARRAY_SIZE) {
 153             size = curSize + (curSize >> 1L); // x(3/2)
 154         } else if (curSize > THRESHOLD_SMALL_ARRAY_SIZE) {
 155             size = (curSize << 1L); // x2
 156         } else {
 157             size = (curSize << 2L); // x4
 158         }
 159         // ensure the new size is >= needed size:
 160         if (size < needSize) {
 161             // align to 4096:
 162             size = ((needSize >> 12L) + 1L) << 12L;
 163         }
 164         // check integer overflow:
 165         if (size > Integer.MAX_VALUE) {
 166             // resize to maximum capacity:
 167             size = Integer.MAX_VALUE;
 168         }
 169         return size;
 170     }
 171 
 172     static final class CacheStats {
 173         final String name;
 174         final BucketStats[] bucketStats;
 175         int resize = 0;
 176         int oversize = 0;
 177         long totalInitial = 0L;
 178 
 179         CacheStats(final String name) {
 180             this.name = name;
 181 
 182             bucketStats = new BucketStats[BUCKETS];
 183             for (int i = 0; i < BUCKETS; i++) {
 184                 bucketStats[i] = new BucketStats();
 185             }
 186         }
 187 
 188         void reset() {
 189             resize = 0;
 190             oversize = 0;
 191 
 192             for (int i = 0; i < BUCKETS; i++) {
 193                 bucketStats[i].reset();
 194             }
 195         }
 196 
 197         long dumpStats() {
 198             long totalCacheBytes = 0L;
 199 
 200             if (DO_STATS) {
 201                 for (int i = 0; i < BUCKETS; i++) {
 202                     final BucketStats s = bucketStats[i];
 203 
 204                     if (s.maxSize != 0) {
 205                         totalCacheBytes += getByteFactor()
 206                                            * (s.maxSize * ARRAY_SIZES[i]);
 207                     }
 208                 }
 209 
 210                 if (totalInitial != 0L || totalCacheBytes != 0L
 211                     || resize != 0 || oversize != 0)
 212                 {
 213                     logInfo(name + ": resize: " + resize
 214                             + " - oversize: " + oversize
 215                             + " - initial: " + getTotalInitialBytes()
 216                             + " bytes (" + totalInitial + " elements)"
 217                             + " - cache: " + totalCacheBytes + " bytes"
 218                     );
 219                 }
 220 
 221                 if (totalCacheBytes != 0L) {
 222                     logInfo(name + ": usage stats:");
 223 
 224                     for (int i = 0; i < BUCKETS; i++) {
 225                         final BucketStats s = bucketStats[i];
 226 
 227                         if (s.getOp != 0) {
 228                             logInfo("  Bucket[" + ARRAY_SIZES[i] + "]: "
 229                                     + "get: " + s.getOp
 230                                     + " - put: " + s.returnOp
 231                                     + " - create: " + s.createOp
 232                                     + " :: max size: " + s.maxSize
 233                             );
 234                         }
 235                     }
 236                 }
 237             }
 238             return totalCacheBytes;
 239         }
 240 
 241         private int getByteFactor() {
 242             int factor = 1;
 243             if (name.contains("Int") || name.contains("Float")) {
 244                 factor = 4;
 245             }
 246             return factor;
 247         }
 248 
 249         long getTotalInitialBytes() {
 250             return getByteFactor() * totalInitial;
 251         }
 252     }
 253 
 254     static final class BucketStats {
 255         int getOp = 0;
 256         int createOp = 0;
 257         int returnOp = 0;
 258         int maxSize = 0;
 259 
 260         void reset() {
 261             getOp = 0;
 262             createOp = 0;
 263             returnOp = 0;
 264             maxSize = 0;
 265         }
 266 
 267         void updateMaxSize(final int size) {
 268             if (size > maxSize) {
 269                 maxSize = size;
 270             }
 271         }
 272     }
 273 }