|
@@ -0,0 +1,613 @@
|
|
|
+/**
|
|
|
+ * 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;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+
|
|
|
+import org.junit.Test;
|
|
|
+import static org.junit.Assert.*;
|
|
|
+
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.fs.FileSystem;
|
|
|
+import org.apache.hadoop.fs.Path;
|
|
|
+import org.apache.hadoop.mapreduce.StatusReporter;
|
|
|
+import org.apache.hadoop.mapreduce.TaskAttemptID;
|
|
|
+import org.apache.hadoop.mapreduce.TaskInputOutputContext;
|
|
|
+import org.apache.hadoop.mapreduce.TaskType;
|
|
|
+import org.apache.hadoop.mapreduce.server.tasktracker.TTConfig;
|
|
|
+import org.apache.hadoop.mapreduce.task.MapContextImpl;
|
|
|
+import org.apache.hadoop.mapreduce.util.ResourceCalculatorPlugin;
|
|
|
+import org.apache.hadoop.mapreduce.util.ResourceCalculatorPlugin.ProcResourceValues;
|
|
|
+import org.apache.hadoop.tools.rumen.ResourceUsageMetrics;
|
|
|
+import org.apache.hadoop.mapred.DummyResourceCalculatorPlugin;
|
|
|
+import org.apache.hadoop.mapred.gridmix.LoadJob.ResourceUsageMatcherRunner;
|
|
|
+import org.apache.hadoop.mapred.gridmix.emulators.resourceusage.CumulativeCpuUsageEmulatorPlugin;
|
|
|
+import org.apache.hadoop.mapred.gridmix.emulators.resourceusage.ResourceUsageEmulatorPlugin;
|
|
|
+import org.apache.hadoop.mapred.gridmix.emulators.resourceusage.ResourceUsageMatcher;
|
|
|
+import org.apache.hadoop.mapred.gridmix.emulators.resourceusage.CumulativeCpuUsageEmulatorPlugin.DefaultCpuUsageEmulator;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Test Gridmix's resource emulator framework and supported plugins.
|
|
|
+ */
|
|
|
+public class TestResourceUsageEmulators {
|
|
|
+ /**
|
|
|
+ * A {@link ResourceUsageEmulatorPlugin} implementation for testing purpose.
|
|
|
+ * It essentially creates a file named 'test' in the test directory.
|
|
|
+ */
|
|
|
+ static class TestResourceUsageEmulatorPlugin
|
|
|
+ implements ResourceUsageEmulatorPlugin {
|
|
|
+ static final Path rootTempDir =
|
|
|
+ new Path(System.getProperty("test.build.data", "/tmp"));
|
|
|
+ static final Path tempDir =
|
|
|
+ new Path(rootTempDir, "TestResourceUsageEmulatorPlugin");
|
|
|
+ static final String DEFAULT_IDENTIFIER = "test";
|
|
|
+
|
|
|
+ private Path touchPath = null;
|
|
|
+ private FileSystem fs = null;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void emulate() throws IOException, InterruptedException {
|
|
|
+ // add some time between 2 calls to emulate()
|
|
|
+ try {
|
|
|
+ Thread.sleep(1000); // sleep for 1s
|
|
|
+ } catch (Exception e){}
|
|
|
+
|
|
|
+ try {
|
|
|
+ fs.delete(touchPath, false); // delete the touch file
|
|
|
+ //TODO Search for a better touch utility
|
|
|
+ fs.create(touchPath).close(); // recreate it
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected String getIdentifier() {
|
|
|
+ return DEFAULT_IDENTIFIER;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Path getFilePath(String id) {
|
|
|
+ return new Path(tempDir, id);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Path getInitFilePath(String id) {
|
|
|
+ return new Path(tempDir, id + ".init");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void initialize(Configuration conf, ResourceUsageMetrics metrics,
|
|
|
+ ResourceCalculatorPlugin monitor, Progressive progress) {
|
|
|
+ // add some time between 2 calls to initialize()
|
|
|
+ try {
|
|
|
+ Thread.sleep(1000); // sleep for 1s
|
|
|
+ } catch (Exception e){}
|
|
|
+
|
|
|
+ try {
|
|
|
+ fs = FileSystem.getLocal(conf);
|
|
|
+
|
|
|
+ Path initPath = getInitFilePath(getIdentifier());
|
|
|
+ fs.delete(initPath, false); // delete the old file
|
|
|
+ fs.create(initPath).close(); // create a new one
|
|
|
+
|
|
|
+ touchPath = getFilePath(getIdentifier());
|
|
|
+ fs.delete(touchPath, false);
|
|
|
+ } catch (Exception e) {
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ if (fs != null) {
|
|
|
+ try {
|
|
|
+ fs.deleteOnExit(tempDir);
|
|
|
+ } catch (IOException ioe){}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // test if the emulation framework successfully loaded this plugin
|
|
|
+ static long testInitialization(String id, Configuration conf)
|
|
|
+ throws IOException {
|
|
|
+ Path testPath = getInitFilePath(id);
|
|
|
+ FileSystem fs = FileSystem.getLocal(conf);
|
|
|
+ return fs.exists(testPath)
|
|
|
+ ? fs.getFileStatus(testPath).getModificationTime()
|
|
|
+ : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // test if the emulation framework successfully loaded this plugin
|
|
|
+ static long testEmulation(String id, Configuration conf)
|
|
|
+ throws IOException {
|
|
|
+ Path testPath = getFilePath(id);
|
|
|
+ FileSystem fs = FileSystem.getLocal(conf);
|
|
|
+ return fs.exists(testPath)
|
|
|
+ ? fs.getFileStatus(testPath).getModificationTime()
|
|
|
+ : 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test implementation of {@link ResourceUsageEmulatorPlugin} which creates
|
|
|
+ * a file named 'others' in the test directory.
|
|
|
+ */
|
|
|
+ static class TestOthers extends TestResourceUsageEmulatorPlugin {
|
|
|
+ static final String ID = "others";
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected String getIdentifier() {
|
|
|
+ return ID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test implementation of {@link ResourceUsageEmulatorPlugin} which creates
|
|
|
+ * a file named 'cpu' in the test directory.
|
|
|
+ */
|
|
|
+ static class TestCpu extends TestResourceUsageEmulatorPlugin {
|
|
|
+ static final String ID = "cpu";
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected String getIdentifier() {
|
|
|
+ return ID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link ResourceUsageMatcher}.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testResourceUsageMatcher() throws Exception {
|
|
|
+ ResourceUsageMatcher matcher = new ResourceUsageMatcher();
|
|
|
+ Configuration conf = new Configuration();
|
|
|
+ conf.setClass(ResourceUsageMatcher.RESOURCE_USAGE_EMULATION_PLUGINS,
|
|
|
+ TestResourceUsageEmulatorPlugin.class,
|
|
|
+ ResourceUsageEmulatorPlugin.class);
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+
|
|
|
+ matcher.configure(conf, null, null, null);
|
|
|
+
|
|
|
+ matcher.matchResourceUsage();
|
|
|
+
|
|
|
+ String id = TestResourceUsageEmulatorPlugin.DEFAULT_IDENTIFIER;
|
|
|
+ long result =
|
|
|
+ TestResourceUsageEmulatorPlugin.testInitialization(id, conf);
|
|
|
+ assertTrue("Resource usage matcher failed to initialize the configured"
|
|
|
+ + " plugin", result > currentTime);
|
|
|
+ result = TestResourceUsageEmulatorPlugin.testEmulation(id, conf);
|
|
|
+ assertTrue("Resource usage matcher failed to load and emulate the"
|
|
|
+ + " configured plugin", result > currentTime);
|
|
|
+
|
|
|
+ // test plugin order to first emulate cpu and then others
|
|
|
+ conf.setStrings(ResourceUsageMatcher.RESOURCE_USAGE_EMULATION_PLUGINS,
|
|
|
+ TestCpu.class.getName() + "," + TestOthers.class.getName());
|
|
|
+
|
|
|
+ matcher.configure(conf, null, null, null);
|
|
|
+
|
|
|
+ // test the initialization order
|
|
|
+ long time1 =
|
|
|
+ TestResourceUsageEmulatorPlugin.testInitialization(TestCpu.ID, conf);
|
|
|
+ long time2 =
|
|
|
+ TestResourceUsageEmulatorPlugin.testInitialization(TestOthers.ID,
|
|
|
+ conf);
|
|
|
+ assertTrue("Resource usage matcher failed to initialize the configured"
|
|
|
+ + " plugins in order", time1 < time2);
|
|
|
+
|
|
|
+ matcher.matchResourceUsage();
|
|
|
+
|
|
|
+ // Note that the cpu usage emulator plugin is configured 1st and then the
|
|
|
+ // others plugin.
|
|
|
+ time1 =
|
|
|
+ TestResourceUsageEmulatorPlugin.testInitialization(TestCpu.ID, conf);
|
|
|
+ time2 =
|
|
|
+ TestResourceUsageEmulatorPlugin.testInitialization(TestOthers.ID,
|
|
|
+ conf);
|
|
|
+ assertTrue("Resource usage matcher failed to load the configured plugins",
|
|
|
+ time1 < time2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Fakes the cumulative usage using {@link FakeCpuUsageEmulatorCore}.
|
|
|
+ */
|
|
|
+ static class FakeResourceUsageMonitor extends DummyResourceCalculatorPlugin {
|
|
|
+ private FakeCpuUsageEmulatorCore core;
|
|
|
+
|
|
|
+ public FakeResourceUsageMonitor(FakeCpuUsageEmulatorCore core) {
|
|
|
+ this.core = core;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * A dummy CPU usage monitor. Every call to
|
|
|
+ * {@link ResourceCalculatorPlugin#getCumulativeCpuTime()} will return the
|
|
|
+ * value of {@link FakeCpuUsageEmulatorCore#getNumCalls()}.
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public long getCumulativeCpuTime() {
|
|
|
+ return core.getCpuUsage();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a {@link ProcResourceValues} with cumulative cpu usage
|
|
|
+ * computed using {@link #getCumulativeCpuTime()}.
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public ProcResourceValues getProcResourceValues() {
|
|
|
+ long usageValue = getCumulativeCpuTime();
|
|
|
+ return new ProcResourceValues(usageValue, -1, -1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * A dummy {@link Progressive} implementation that allows users to set the
|
|
|
+ * progress for testing. The {@link Progressive#getProgress()} call will
|
|
|
+ * return the last progress value set using
|
|
|
+ * {@link FakeProgressive#setProgress(float)}.
|
|
|
+ */
|
|
|
+ static class FakeProgressive implements Progressive {
|
|
|
+ private float progress = 0F;
|
|
|
+ @Override
|
|
|
+ public float getProgress() {
|
|
|
+ return progress;
|
|
|
+ }
|
|
|
+
|
|
|
+ void setProgress(float progress) {
|
|
|
+ this.progress = progress;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * A dummy reporter for {@link LoadJob.ResourceUsageMatcherRunner}.
|
|
|
+ */
|
|
|
+ private static class DummyReporter extends StatusReporter {
|
|
|
+ private Progressive progress;
|
|
|
+
|
|
|
+ DummyReporter(Progressive progress) {
|
|
|
+ this.progress = progress;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public org.apache.hadoop.mapreduce.Counter getCounter(Enum<?> name) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public org.apache.hadoop.mapreduce.Counter getCounter(String group,
|
|
|
+ String name) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void progress() {
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public float getProgress() {
|
|
|
+ return progress.getProgress();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setStatus(String status) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Extends ResourceUsageMatcherRunner for testing.
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private static class FakeResourceUsageMatcherRunner
|
|
|
+ extends ResourceUsageMatcherRunner {
|
|
|
+ FakeResourceUsageMatcherRunner(TaskInputOutputContext context,
|
|
|
+ ResourceUsageMetrics metrics) {
|
|
|
+ super(context, metrics);
|
|
|
+ }
|
|
|
+
|
|
|
+ // test ResourceUsageMatcherRunner
|
|
|
+ void test() throws Exception {
|
|
|
+ super.match();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link LoadJob.ResourceUsageMatcherRunner}.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public void testResourceUsageMatcherRunner() throws Exception {
|
|
|
+ Configuration conf = new Configuration();
|
|
|
+ FakeProgressive progress = new FakeProgressive();
|
|
|
+
|
|
|
+ // set the resource calculator plugin
|
|
|
+ conf.setClass(TTConfig.TT_RESOURCE_CALCULATOR_PLUGIN,
|
|
|
+ DummyResourceCalculatorPlugin.class,
|
|
|
+ ResourceCalculatorPlugin.class);
|
|
|
+ // set the resources
|
|
|
+ // set the resource implementation class
|
|
|
+ conf.setClass(ResourceUsageMatcher.RESOURCE_USAGE_EMULATION_PLUGINS,
|
|
|
+ TestResourceUsageEmulatorPlugin.class,
|
|
|
+ ResourceUsageEmulatorPlugin.class);
|
|
|
+
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+
|
|
|
+ // initialize the matcher class
|
|
|
+ TaskAttemptID id = new TaskAttemptID("test", 1, TaskType.MAP, 1, 1);
|
|
|
+ StatusReporter reporter = new DummyReporter(progress);
|
|
|
+ TaskInputOutputContext context =
|
|
|
+ new MapContextImpl(conf, id, null, null, null, reporter, null);
|
|
|
+ FakeResourceUsageMatcherRunner matcher =
|
|
|
+ new FakeResourceUsageMatcherRunner(context, null);
|
|
|
+
|
|
|
+ // check if the matcher initialized the plugin
|
|
|
+ String identifier = TestResourceUsageEmulatorPlugin.DEFAULT_IDENTIFIER;
|
|
|
+ long initTime =
|
|
|
+ TestResourceUsageEmulatorPlugin.testInitialization(identifier, conf);
|
|
|
+ assertTrue("ResourceUsageMatcherRunner failed to initialize the"
|
|
|
+ + " configured plugin", initTime > currentTime);
|
|
|
+
|
|
|
+ // check the progress
|
|
|
+ assertEquals("Progress mismatch in ResourceUsageMatcherRunner",
|
|
|
+ 0, progress.getProgress(), 0D);
|
|
|
+
|
|
|
+ // call match() and check progress
|
|
|
+ progress.setProgress(0.01f);
|
|
|
+ currentTime = System.currentTimeMillis();
|
|
|
+ matcher.test();
|
|
|
+ long emulateTime =
|
|
|
+ TestResourceUsageEmulatorPlugin.testEmulation(identifier, conf);
|
|
|
+ assertTrue("ProgressBasedResourceUsageMatcher failed to load and emulate"
|
|
|
+ + " the configured plugin", emulateTime > currentTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link CumulativeCpuUsageEmulatorPlugin}'s core CPU usage emulation
|
|
|
+ * engine.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testCpuUsageEmulator() throws IOException {
|
|
|
+ // test CpuUsageEmulator calibration with fake resource calculator plugin
|
|
|
+ long target = 100000L; // 100 secs
|
|
|
+ int unitUsage = 50;
|
|
|
+ FakeCpuUsageEmulatorCore fakeCpuEmulator = new FakeCpuUsageEmulatorCore();
|
|
|
+ fakeCpuEmulator.setUnitUsage(unitUsage);
|
|
|
+ FakeResourceUsageMonitor fakeMonitor =
|
|
|
+ new FakeResourceUsageMonitor(fakeCpuEmulator);
|
|
|
+
|
|
|
+ // calibrate for 100ms
|
|
|
+ fakeCpuEmulator.calibrate(fakeMonitor, target);
|
|
|
+
|
|
|
+ // by default, CpuUsageEmulator.calibrate() will consume 100ms of CPU usage
|
|
|
+ assertEquals("Fake calibration failed",
|
|
|
+ 100, fakeMonitor.getCumulativeCpuTime());
|
|
|
+ assertEquals("Fake calibration failed",
|
|
|
+ 100, fakeCpuEmulator.getCpuUsage());
|
|
|
+ // by default, CpuUsageEmulator.performUnitComputation() will be called
|
|
|
+ // twice
|
|
|
+ assertEquals("Fake calibration failed",
|
|
|
+ 2, fakeCpuEmulator.getNumCalls());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This is a dummy class that fakes CPU usage.
|
|
|
+ */
|
|
|
+ private static class FakeCpuUsageEmulatorCore
|
|
|
+ extends DefaultCpuUsageEmulator {
|
|
|
+ private int numCalls = 0;
|
|
|
+ private int unitUsage = 1;
|
|
|
+ private int cpuUsage = 0;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void performUnitComputation() {
|
|
|
+ ++numCalls;
|
|
|
+ cpuUsage += unitUsage;
|
|
|
+ }
|
|
|
+
|
|
|
+ int getNumCalls() {
|
|
|
+ return numCalls;
|
|
|
+ }
|
|
|
+
|
|
|
+ int getCpuUsage() {
|
|
|
+ return cpuUsage;
|
|
|
+ }
|
|
|
+
|
|
|
+ void reset() {
|
|
|
+ numCalls = 0;
|
|
|
+ cpuUsage = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void setUnitUsage(int unitUsage) {
|
|
|
+ this.unitUsage = unitUsage;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Creates a ResourceUsageMetrics object from the target usage
|
|
|
+ private static ResourceUsageMetrics createMetrics(long target) {
|
|
|
+ ResourceUsageMetrics metrics = new ResourceUsageMetrics();
|
|
|
+ metrics.setCumulativeCpuUsage(target);
|
|
|
+ metrics.setVirtualMemoryUsage(target);
|
|
|
+ metrics.setPhysicalMemoryUsage(target);
|
|
|
+ metrics.setHeapUsage(target);
|
|
|
+ return metrics;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test {@link CumulativeCpuUsageEmulatorPlugin}.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testCumulativeCpuUsageEmulatorPlugin() throws Exception {
|
|
|
+ Configuration conf = new Configuration();
|
|
|
+ long targetCpuUsage = 1000L;
|
|
|
+ int unitCpuUsage = 50;
|
|
|
+
|
|
|
+ // fake progress indicator
|
|
|
+ FakeProgressive fakeProgress = new FakeProgressive();
|
|
|
+
|
|
|
+ // fake cpu usage generator
|
|
|
+ FakeCpuUsageEmulatorCore fakeCore = new FakeCpuUsageEmulatorCore();
|
|
|
+ fakeCore.setUnitUsage(unitCpuUsage);
|
|
|
+
|
|
|
+ // a cumulative cpu usage emulator with fake core
|
|
|
+ CumulativeCpuUsageEmulatorPlugin cpuPlugin =
|
|
|
+ new CumulativeCpuUsageEmulatorPlugin(fakeCore);
|
|
|
+
|
|
|
+ // test with invalid or missing resource usage value
|
|
|
+ ResourceUsageMetrics invalidUsage = createMetrics(0);
|
|
|
+ cpuPlugin.initialize(conf, invalidUsage, null, null);
|
|
|
+
|
|
|
+ // test if disabled cpu emulation plugin's emulate() call is a no-operation
|
|
|
+ // this will test if the emulation plugin is disabled or not
|
|
|
+ int numCallsPre = fakeCore.getNumCalls();
|
|
|
+ long cpuUsagePre = fakeCore.getCpuUsage();
|
|
|
+ cpuPlugin.emulate();
|
|
|
+ int numCallsPost = fakeCore.getNumCalls();
|
|
|
+ long cpuUsagePost = fakeCore.getCpuUsage();
|
|
|
+
|
|
|
+ // test if no calls are made cpu usage emulator core
|
|
|
+ assertEquals("Disabled cumulative CPU usage emulation plugin works!",
|
|
|
+ numCallsPre, numCallsPost);
|
|
|
+
|
|
|
+ // test if no calls are made cpu usage emulator core
|
|
|
+ assertEquals("Disabled cumulative CPU usage emulation plugin works!",
|
|
|
+ cpuUsagePre, cpuUsagePost);
|
|
|
+
|
|
|
+ // test with valid resource usage value
|
|
|
+ ResourceUsageMetrics metrics = createMetrics(targetCpuUsage);
|
|
|
+
|
|
|
+ // fake monitor
|
|
|
+ ResourceCalculatorPlugin monitor = new FakeResourceUsageMonitor(fakeCore);
|
|
|
+
|
|
|
+ // test with default emulation interval
|
|
|
+ testEmulationAccuracy(conf, fakeCore, monitor, metrics, cpuPlugin,
|
|
|
+ targetCpuUsage, targetCpuUsage / unitCpuUsage);
|
|
|
+
|
|
|
+ // test with custom value for emulation interval of 20%
|
|
|
+ conf.setFloat(CumulativeCpuUsageEmulatorPlugin.CPU_EMULATION_FREQUENCY,
|
|
|
+ 0.2F);
|
|
|
+ testEmulationAccuracy(conf, fakeCore, monitor, metrics, cpuPlugin,
|
|
|
+ targetCpuUsage, targetCpuUsage / unitCpuUsage);
|
|
|
+
|
|
|
+ // test if emulation interval boundary is respected (unit usage = 1)
|
|
|
+ // test the case where the current progress is less than threshold
|
|
|
+ fakeProgress = new FakeProgressive(); // initialize
|
|
|
+ fakeCore.reset();
|
|
|
+ fakeCore.setUnitUsage(1);
|
|
|
+ conf.setFloat(CumulativeCpuUsageEmulatorPlugin.CPU_EMULATION_FREQUENCY,
|
|
|
+ 0.25F);
|
|
|
+ cpuPlugin.initialize(conf, metrics, monitor, fakeProgress);
|
|
|
+ // take a snapshot after the initialization
|
|
|
+ long initCpuUsage = monitor.getCumulativeCpuTime();
|
|
|
+ long initNumCalls = fakeCore.getNumCalls();
|
|
|
+ // test with 0 progress
|
|
|
+ testEmulationBoundary(0F, fakeCore, fakeProgress, cpuPlugin, initCpuUsage,
|
|
|
+ initNumCalls, "[no-op, 0 progress]");
|
|
|
+ // test with 24% progress
|
|
|
+ testEmulationBoundary(0.24F, fakeCore, fakeProgress, cpuPlugin,
|
|
|
+ initCpuUsage, initNumCalls, "[no-op, 24% progress]");
|
|
|
+ // test with 25% progress
|
|
|
+ // target = 1000ms, target emulation at 25% = 250ms,
|
|
|
+ // weighed target = 1000 * 0.25^4 (we are using progress^4 as the weight)
|
|
|
+ // ~ 4
|
|
|
+ // but current usage = init-usage = 100, hence expected = 100
|
|
|
+ testEmulationBoundary(0.25F, fakeCore, fakeProgress, cpuPlugin,
|
|
|
+ initCpuUsage, initNumCalls, "[op, 25% progress]");
|
|
|
+
|
|
|
+ // test with 80% progress
|
|
|
+ // target = 1000ms, target emulation at 80% = 800ms,
|
|
|
+ // weighed target = 1000 * 0.25^4 (we are using progress^4 as the weight)
|
|
|
+ // ~ 410
|
|
|
+ // current-usage = init-usage = 100, hence expected-usage = 410
|
|
|
+ testEmulationBoundary(0.80F, fakeCore, fakeProgress, cpuPlugin, 410, 410,
|
|
|
+ "[op, 80% progress]");
|
|
|
+
|
|
|
+ // now test if the final call with 100% progress ramps up the CPU usage
|
|
|
+ testEmulationBoundary(1F, fakeCore, fakeProgress, cpuPlugin, targetCpuUsage,
|
|
|
+ targetCpuUsage, "[op, 100% progress]");
|
|
|
+
|
|
|
+ // test if emulation interval boundary is respected (unit usage = 50)
|
|
|
+ // test the case where the current progress is less than threshold
|
|
|
+ fakeProgress = new FakeProgressive(); // initialize
|
|
|
+ fakeCore.reset();
|
|
|
+ fakeCore.setUnitUsage(unitCpuUsage);
|
|
|
+ conf.setFloat(CumulativeCpuUsageEmulatorPlugin.CPU_EMULATION_FREQUENCY,
|
|
|
+ 0.40F);
|
|
|
+ cpuPlugin.initialize(conf, metrics, monitor, fakeProgress);
|
|
|
+ // take a snapshot after the initialization
|
|
|
+ initCpuUsage = monitor.getCumulativeCpuTime();
|
|
|
+ initNumCalls = fakeCore.getNumCalls();
|
|
|
+ // test with 0 progress
|
|
|
+ testEmulationBoundary(0F, fakeCore, fakeProgress, cpuPlugin, initCpuUsage,
|
|
|
+ initNumCalls, "[no-op, 0 progress]");
|
|
|
+ // test with 39% progress
|
|
|
+ testEmulationBoundary(0.39F, fakeCore, fakeProgress, cpuPlugin,
|
|
|
+ initCpuUsage, initNumCalls, "[no-op, 39% progress]");
|
|
|
+ // test with 40% progress
|
|
|
+ // target = 1000ms, target emulation at 40% = 4000ms,
|
|
|
+ // weighed target = 1000 * 0.40^4 (we are using progress^4 as the weight)
|
|
|
+ // ~ 26
|
|
|
+ // current-usage = init-usage = 100, hence expected-usage = 100
|
|
|
+ testEmulationBoundary(0.40F, fakeCore, fakeProgress, cpuPlugin,
|
|
|
+ initCpuUsage, initNumCalls, "[op, 40% progress]");
|
|
|
+
|
|
|
+ // test with 90% progress
|
|
|
+ // target = 1000ms, target emulation at 90% = 900ms,
|
|
|
+ // weighed target = 1000 * 0.90^4 (we are using progress^4 as the weight)
|
|
|
+ // ~ 657
|
|
|
+ // current-usage = init-usage = 100, hence expected-usage = 657 but
|
|
|
+ // the fake-core increases in steps of 50, hence final target = 700
|
|
|
+ testEmulationBoundary(0.90F, fakeCore, fakeProgress, cpuPlugin, 700,
|
|
|
+ 700 / unitCpuUsage, "[op, 90% progress]");
|
|
|
+
|
|
|
+ // now test if the final call with 100% progress ramps up the CPU usage
|
|
|
+ testEmulationBoundary(1F, fakeCore, fakeProgress, cpuPlugin, targetCpuUsage,
|
|
|
+ targetCpuUsage / unitCpuUsage, "[op, 100% progress]");
|
|
|
+ }
|
|
|
+
|
|
|
+ // test whether the CPU usage emulator achieves the desired target using
|
|
|
+ // desired calls to the underling core engine.
|
|
|
+ private static void testEmulationAccuracy(Configuration conf,
|
|
|
+ FakeCpuUsageEmulatorCore fakeCore,
|
|
|
+ ResourceCalculatorPlugin monitor,
|
|
|
+ ResourceUsageMetrics metrics,
|
|
|
+ CumulativeCpuUsageEmulatorPlugin cpuPlugin,
|
|
|
+ long expectedTotalCpuUsage, long expectedTotalNumCalls)
|
|
|
+ throws Exception {
|
|
|
+ FakeProgressive fakeProgress = new FakeProgressive();
|
|
|
+ fakeCore.reset();
|
|
|
+ cpuPlugin.initialize(conf, metrics, monitor, fakeProgress);
|
|
|
+ int numLoops = 0;
|
|
|
+ while (fakeProgress.getProgress() < 1) {
|
|
|
+ ++numLoops;
|
|
|
+ float progress = (float)numLoops / 100;
|
|
|
+ fakeProgress.setProgress(progress);
|
|
|
+ cpuPlugin.emulate();
|
|
|
+ }
|
|
|
+
|
|
|
+ // test if the resource plugin shows the expected invocations
|
|
|
+ assertEquals("Cumulative cpu usage emulator plugin failed (num calls)!",
|
|
|
+ expectedTotalNumCalls, fakeCore.getNumCalls(), 0L);
|
|
|
+ // test if the resource plugin shows the expected usage
|
|
|
+ assertEquals("Cumulative cpu usage emulator plugin failed (total usage)!",
|
|
|
+ expectedTotalCpuUsage, fakeCore.getCpuUsage(), 0L);
|
|
|
+ }
|
|
|
+
|
|
|
+ // tests if the CPU usage emulation plugin emulates only at the expected
|
|
|
+ // progress gaps
|
|
|
+ private static void testEmulationBoundary(float progress,
|
|
|
+ FakeCpuUsageEmulatorCore fakeCore, FakeProgressive fakeProgress,
|
|
|
+ CumulativeCpuUsageEmulatorPlugin cpuPlugin, long expectedTotalCpuUsage,
|
|
|
+ long expectedTotalNumCalls, String info) throws Exception {
|
|
|
+ fakeProgress.setProgress(progress);
|
|
|
+ cpuPlugin.emulate();
|
|
|
+
|
|
|
+ assertEquals("Emulation interval test for cpu usage failed " + info + "!",
|
|
|
+ expectedTotalCpuUsage, fakeCore.getCpuUsage(), 0L);
|
|
|
+ assertEquals("Emulation interval test for num calls failed " + info + "!",
|
|
|
+ expectedTotalNumCalls, fakeCore.getNumCalls(), 0L);
|
|
|
+ }
|
|
|
+}
|