/*
* Copyright Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ehcache.impl.internal.store.offheap;
import org.ehcache.impl.internal.store.offheap.MemorySizeParser;
import org.terracotta.offheapstore.util.DebuggingUtils;
import static org.ehcache.impl.internal.store.offheap.OffHeapStoreUtils.getAdvancedBooleanConfigProperty;
import static org.ehcache.impl.internal.store.offheap.OffHeapStoreUtils.getAdvancedMemorySizeConfigProperty;
/**
* Configuration class for sizing offheap.
*/
public class HeuristicConfiguration {
private static final String MINIMUM_MAX_MEMORY = "1M";
private static final long MINIMUM_MAX_MEMORY_IN_BYTES = MemorySizeParser.parse(MINIMUM_MAX_MEMORY);
private static final int IDEAL_MAX_SEGMENT_SIZE = 32 * 1024 * 1024;
private static final int MAXIMUM_CHUNK_SIZE = 1 * 1024 * 1024 * 1024;
private static final int MINIMUM_SEGMENT_COUNT = 16;
private static final int MAXIMUM_SEGMENT_COUNT = 16 * 1024;
private static final int MAXIMAL_SEGMENT_SIZE_RATIO = 4;
private static final int INITIAL_SEGMENT_SIZE_RATIO = 16;
private static final int ASSUMED_KEY_VALUE_SIZE = 1024;
private static final int AGGRESSIVE_INITIAL_SEGMENT_SIZE_RATIO = 1;
private final long maximumSize;
private final int idealMaxSegmentSize;
private final int maximumChunkSize;
private final int minimumSegmentCount;
private final int maximumSegmentCount;
private final int maximalSegmentSizeRatio;
private final int initialSegmentSizeRatio;
private final int assumedKeyValueSize;
public HeuristicConfiguration(long maximumSize) {
if (maximumSize < MINIMUM_MAX_MEMORY_IN_BYTES) {
throw new IllegalArgumentException("The value of maxBytesLocalOffHeap is less than the minimum allowed value of " + MINIMUM_MAX_MEMORY +
". Reconfigure maxBytesLocalOffHeap in ehcache.xml or programmatically.");
}
this.maximumSize = maximumSize;
if (getAdvancedBooleanConfigProperty("aggressive", false)) {
this.idealMaxSegmentSize = (int) getAdvancedMemorySizeConfigProperty("idealMaxSegmentSize", IDEAL_MAX_SEGMENT_SIZE);
this.maximumChunkSize = (int) getAdvancedMemorySizeConfigProperty("maximumChunkSize", MAXIMUM_CHUNK_SIZE);
this.minimumSegmentCount = (int) getAdvancedMemorySizeConfigProperty("minimumSegmentCount", MINIMUM_SEGMENT_COUNT);
this.maximumSegmentCount = (int) getAdvancedMemorySizeConfigProperty("maximumSegmentCount", MAXIMUM_SEGMENT_COUNT);
this.maximalSegmentSizeRatio = (int) getAdvancedMemorySizeConfigProperty("maximalSegmentSizeRatio", MAXIMAL_SEGMENT_SIZE_RATIO);
this.initialSegmentSizeRatio = (int) getAdvancedMemorySizeConfigProperty("initialSegmentSizeRatio", AGGRESSIVE_INITIAL_SEGMENT_SIZE_RATIO);
this.assumedKeyValueSize = (int) getAdvancedMemorySizeConfigProperty("assumedKeyValueSize", ASSUMED_KEY_VALUE_SIZE);
} else {
this.idealMaxSegmentSize = (int) getAdvancedMemorySizeConfigProperty("idealMaxSegmentSize", IDEAL_MAX_SEGMENT_SIZE);
this.maximumChunkSize = (int) getAdvancedMemorySizeConfigProperty("maximumChunkSize", MAXIMUM_CHUNK_SIZE);
this.minimumSegmentCount = (int) getAdvancedMemorySizeConfigProperty("minimumSegmentCount", MINIMUM_SEGMENT_COUNT);
this.maximumSegmentCount = (int) getAdvancedMemorySizeConfigProperty("maximumSegmentCount", MAXIMUM_SEGMENT_COUNT);
this.maximalSegmentSizeRatio = (int) getAdvancedMemorySizeConfigProperty("maximalSegmentSizeRatio", MAXIMAL_SEGMENT_SIZE_RATIO);
this.initialSegmentSizeRatio = (int) getAdvancedMemorySizeConfigProperty("initialSegmentSizeRatio", INITIAL_SEGMENT_SIZE_RATIO);
this.assumedKeyValueSize = (int) getAdvancedMemorySizeConfigProperty("assumedKeyValueSize", ASSUMED_KEY_VALUE_SIZE);
}
}
public long getMaximumSize() {
return maximumSize;
}
public int getMinimumChunkSize() {
return (int) Math.min(maximumChunkSize, maximalSegmentSizeRatio * (getMaximumSize() / getConcurrency()));
}
public int getMaximumChunkSize() {
return (int) Math.min(getMaximumSize(), maximumChunkSize);
}
public int getConcurrency() {
return Integer.highestOneBit((int) Math.min(maximumSegmentCount, Math.max(minimumSegmentCount, getMaximumSize() / idealMaxSegmentSize)));
}
public int getInitialSegmentTableSize() {
return Math.max(1, getSegmentDataPageSize() / assumedKeyValueSize);
}
public int getSegmentDataPageSize() {
return Integer.highestOneBit((int) Math.min(getMinimumChunkSize(), getInitialSegmentCapacity() * assumedKeyValueSize));
}
private long getInitialSegmentCapacity() {
return getMaximumSize() / (getConcurrency() * initialSegmentSizeRatio * (assumedKeyValueSize + 16));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Heuristic Configuration: \n");
sb.append("Maximum Size (specified) : ").append(DebuggingUtils.toBase2SuffixedString(getMaximumSize())).append("B\n");
sb.append("Minimum Chunk Size : ").append(DebuggingUtils.toBase2SuffixedString(getMinimumChunkSize())).append("B\n");
sb.append("Maximum Chunk Size : ").append(DebuggingUtils.toBase2SuffixedString(getMaximumChunkSize())).append("B\n");
sb.append("Concurrency : ").append(getConcurrency()).append("\n");
sb.append("Initial Segment Table Size : ").append(DebuggingUtils.toBase2SuffixedString(getInitialSegmentTableSize())).append(" slots\n");
sb.append("Segment Data Page Size : ").append(DebuggingUtils.toBase2SuffixedString(getSegmentDataPageSize())).append("B\n");
return sb.toString();
}
}