blob: 4cfe1b533a27aece17881e3457eddcdf458e203c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.hadoop.mapred.gridmix.emulators.resourceusage;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.mapred.gridmix.Progressive;
import org.apache.hadoop.tools.rumen.ResourceUsageMetrics;
import org.apache.hadoop.yarn.util.ResourceCalculatorPlugin;
/**
* <p>A {@link ResourceUsageEmulatorPlugin} that emulates the total heap
* usage by loading the JVM heap memory. Adding smaller chunks of data to the
* heap will essentially use up some heap space thus forcing the JVM to expand
* its heap and thus resulting into increase in the heap usage.</p>
*
* <p>{@link TotalHeapUsageEmulatorPlugin} emulates the heap usage in steps.
* The frequency of emulation can be configured via
* {@link #HEAP_EMULATION_PROGRESS_INTERVAL}.
* Heap usage values are matched via emulation only at specific interval
* boundaries.
* </p>
*
* {@link TotalHeapUsageEmulatorPlugin} is a wrapper program for managing
* the heap usage emulation feature. It internally uses an emulation algorithm
* (called as core and described using {@link HeapUsageEmulatorCore}) for
* performing the actual emulation. Multiple calls to this core engine should
* use up some amount of heap.
*/
public class TotalHeapUsageEmulatorPlugin
implements ResourceUsageEmulatorPlugin {
// Configuration parameters
// the core engine to emulate heap usage
protected HeapUsageEmulatorCore emulatorCore;
// the progress bar
private Progressive progress;
// decides if this plugin can emulate heap usage or not
private boolean enabled = true;
// the progress boundaries/interval where emulation should be done
private float emulationInterval;
// target heap usage to emulate
private long targetHeapUsageInMB = 0;
/**
* The frequency (based on task progress) with which memory-emulation code is
* run. If the value is set to 0.1 then the emulation will happen at 10% of
* the task's progress. The default value of this parameter is
* {@link #DEFAULT_EMULATION_PROGRESS_INTERVAL}.
*/
public static final String HEAP_EMULATION_PROGRESS_INTERVAL =
"gridmix.emulators.resource-usage.heap.emulation-interval";
// Default value for emulation interval
private static final float DEFAULT_EMULATION_PROGRESS_INTERVAL = 0.1F; // 10 %
private float prevEmulationProgress = 0F;
/**
* The minimum buffer reserved for other non-emulation activities.
*/
public static final String MIN_HEAP_FREE_RATIO =
"gridmix.emulators.resource-usage.heap.min-free-ratio";
private float minFreeHeapRatio;
private static final float DEFAULT_MIN_FREE_HEAP_RATIO = 0.3F;
/**
* Determines the unit increase per call to the core engine's load API. This
* is expressed as a percentage of the difference between the expected total
* heap usage and the current usage.
*/
public static final String HEAP_LOAD_RATIO =
"gridmix.emulators.resource-usage.heap.load-ratio";
private float heapLoadRatio;
private static final float DEFAULT_HEAP_LOAD_RATIO = 0.1F;
public static final int ONE_MB = 1024 * 1024;
/**
* Defines the core heap usage emulation algorithm. This engine is expected
* to perform certain memory intensive operations to consume some
* amount of heap. {@link #load(long)} should load the current heap and
* increase the heap usage by the specified value. This core engine can be
* initialized using the {@link #initialize(ResourceCalculatorPlugin, long)}
* API to suit the underlying hardware better.
*/
public interface HeapUsageEmulatorCore {
/**
* Performs some memory intensive operations to use up some heap.
*/
public void load(long sizeInMB);
/**
* Initialize the core.
*/
public void initialize(ResourceCalculatorPlugin monitor,
long totalHeapUsageInMB);
/**
* Reset the resource usage
*/
public void reset();
}
/**
* This is the core engine to emulate the heap usage. The only responsibility
* of this class is to perform certain memory intensive operations to make
* sure that some desired value of heap is used.
*/
public static class DefaultHeapUsageEmulator
implements HeapUsageEmulatorCore {
// store the unit loads in a list
protected static final ArrayList<Object> heapSpace =
new ArrayList<Object>();
/**
* Increase heap usage by current process by the given amount.
* This is done by creating objects each of size 1MB.
*/
public void load(long sizeInMB) {
for (long i = 0; i < sizeInMB; ++i) {
// Create another String object of size 1MB
heapSpace.add((Object)new byte[ONE_MB]);
}
}
/**
* This will initialize the core and check if the core can emulate the
* desired target on the underlying hardware.
*/
public void initialize(ResourceCalculatorPlugin monitor,
long totalHeapUsageInMB) {
long maxPhysicalMemoryInMB = monitor.getPhysicalMemorySize() / ONE_MB ;
if(maxPhysicalMemoryInMB < totalHeapUsageInMB) {
throw new RuntimeException("Total heap the can be used is "
+ maxPhysicalMemoryInMB
+ " bytes while the emulator is configured to emulate a total of "
+ totalHeapUsageInMB + " bytes");
}
}
/**
* Clear references to all the GridMix-allocated special objects so that
* heap usage is reduced.
*/
@Override
public void reset() {
heapSpace.clear();
}
}
public TotalHeapUsageEmulatorPlugin() {
this(new DefaultHeapUsageEmulator());
}
/**
* For testing.
*/
public TotalHeapUsageEmulatorPlugin(HeapUsageEmulatorCore core) {
emulatorCore = core;
}
protected long getTotalHeapUsageInMB() {
return Runtime.getRuntime().totalMemory() / ONE_MB;
}
protected long getMaxHeapUsageInMB() {
return Runtime.getRuntime().maxMemory() / ONE_MB;
}
@Override
public float getProgress() {
return enabled
? Math.min(1f, ((float)getTotalHeapUsageInMB())/targetHeapUsageInMB)
: 1.0f;
}
@Override
public void emulate() throws IOException, InterruptedException {
if (enabled) {
float currentProgress = progress.getProgress();
if (prevEmulationProgress < currentProgress
&& ((currentProgress - prevEmulationProgress) >= emulationInterval
|| currentProgress == 1)) {
long maxHeapSizeInMB = getMaxHeapUsageInMB();
long committedHeapSizeInMB = getTotalHeapUsageInMB();
// Increase committed heap usage, if needed
// Using a linear weighing function for computing the expected usage
long expectedHeapUsageInMB =
Math.min(maxHeapSizeInMB,
(long) (targetHeapUsageInMB * currentProgress));
if (expectedHeapUsageInMB < maxHeapSizeInMB
&& committedHeapSizeInMB < expectedHeapUsageInMB) {
long bufferInMB = (long)(minFreeHeapRatio * expectedHeapUsageInMB);
long currentDifferenceInMB =
expectedHeapUsageInMB - committedHeapSizeInMB;
long currentIncrementLoadSizeInMB =
(long)(currentDifferenceInMB * heapLoadRatio);
// Make sure that at least 1 MB is incremented.
currentIncrementLoadSizeInMB =
Math.max(1, currentIncrementLoadSizeInMB);
while (committedHeapSizeInMB + bufferInMB < expectedHeapUsageInMB) {
// add blocks in order of X% of the difference, X = 10% by default
emulatorCore.load(currentIncrementLoadSizeInMB);
committedHeapSizeInMB = getTotalHeapUsageInMB();
}
}
// store the emulation progress boundary
prevEmulationProgress = currentProgress;
}
// reset the core so that the garbage is reclaimed
emulatorCore.reset();
}
}
@Override
public void initialize(Configuration conf, ResourceUsageMetrics metrics,
ResourceCalculatorPlugin monitor,
Progressive progress) {
this.progress = progress;
// get the target heap usage
targetHeapUsageInMB = metrics.getHeapUsage() / ONE_MB;
if (targetHeapUsageInMB <= 0 ) {
enabled = false;
return;
} else {
// calibrate the core heap-usage utility
emulatorCore.initialize(monitor, targetHeapUsageInMB);
enabled = true;
}
emulationInterval =
conf.getFloat(HEAP_EMULATION_PROGRESS_INTERVAL,
DEFAULT_EMULATION_PROGRESS_INTERVAL);
minFreeHeapRatio = conf.getFloat(MIN_HEAP_FREE_RATIO,
DEFAULT_MIN_FREE_HEAP_RATIO);
heapLoadRatio = conf.getFloat(HEAP_LOAD_RATIO, DEFAULT_HEAP_LOAD_RATIO);
prevEmulationProgress = 0;
}
}