blob: f2538e46c3f42b01cce07b3dd4d8d68daf76b8e1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.tracecompass.incubator.scripting.core.tests.perf;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.apache.commons.io.FileUtils;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.widgets.Display;
import org.eclipse.test.performance.Dimension;
import org.eclipse.test.performance.Performance;
import org.eclipse.test.performance.PerformanceMeter;
import org.eclipse.tracecompass.analysis.os.linux.core.tid.TidAnalysisModule;
import org.eclipse.tracecompass.ctf.core.tests.shared.CtfBenchmarkTrace;
import org.eclipse.tracecompass.incubator.internal.scripting.core.ScriptExecutionHelper;
import org.eclipse.tracecompass.incubator.scripting.core.trace.ScriptEventsIterator;
import org.eclipse.tracecompass.incubator.scripting.core.trace.TraceScriptingModule;
import org.eclipse.tracecompass.incubator.scripting.ui.tests.ActivatorTest;
import org.eclipse.tracecompass.incubator.scripting.ui.tests.TestModule;
import org.eclipse.tracecompass.internal.tmf.ui.project.model.TmfImportHelper;
import org.eclipse.tracecompass.lttng2.kernel.core.trace.LttngKernelTrace;
import org.eclipse.tracecompass.testtraces.ctf.CtfTestTrace;
import org.eclipse.tracecompass.tmf.analysis.xml.core.tests.common.TmfXmlTestUtils;
import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
import org.eclipse.tracecompass.tmf.core.analysis.TmfAbstractAnalysisModule;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.tests.shared.TmfTestHelper;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEvent;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectRegistry;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder;
import org.eclipse.tracecompass.tmf.ui.tests.shared.ProjectModelTestData;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import com.google.common.collect.ImmutableList;
/**
* Benchmarks EASE Scripting in native java, javascript (Nashorn and Rhino) and
* python (Jython and Py4j)
*
* @author Geneviève Bastien
*/
@RunWith(Parameterized.class)
public class ScriptingBenchmark {
/**
* Initial value for which to compute the Collatz sequence
*/
public static final int INITIAL_VALUE = 10;
/**
* Last value for which to compute the Collatz sequence
*/
public static final int LIMIT = 300000;
private static final String JAVASCRIPT_EXTENSION = ".js";
private static final String PYTHON_EXTENSION = ".py";
private static final String XML_EXTENSION = ".xml";
private static final String JAVA_PREFIX = "Java: ";
private static final String RHINO_PREFIX = "JS Rhino: ";
private static final String NASHORN_PREFIX = "JS Nashorn: ";
private static final String PY4J_PREFIX = "Py4j: ";
private static final String JYTHON_PREFIX = "Jython: ";
private static final String XML_PREFIX = "XML: ";
private static final int LOOP_COUNT = 25;
private static final int LOOP_COUNT_SMALL = 5;
private static final String DEFAULT_PROJECT = "Tracing";
private static final String JAVASCRIPT_PATH = "scripts/perf/javascript/";
private static final String PYTHON_PATH = "scripts/perf/python/";
private static final String XML_PATH = "scripts/perf/xml/";
private static final String RHINO_ENGINE = "org.eclipse.ease.javascript.rhino";
private static final String NASHORN_ENGINE = "org.eclipse.ease.javascript.nashorn";
private static final String JYTHON_ENGINE = "org.eclipse.ease.python.jython";
private static final String PY4J_ENGINE = "org.eclipse.ease.lang.python.py4j.engine";
private static final String XML_ANALYSIS_ID = "tracecompass.script.benchmark";
private static final CtfTestTrace SMALL_TRACE = CtfTestTrace.SYNC_DEST;
private static final CtfBenchmarkTrace LARGE_TRACE = CtfBenchmarkTrace.ALL_OS_ANALYSES;
private static TmfProjectElement sfProject;
/**
* @return The arrays of parameters
* @throws IOException
* Exception thrown initializing the traces
*/
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> getParameters() throws IOException {
return Arrays.asList(new Object[][] {
{ "Empty Script", EMPTY, "empty", null, LOOP_COUNT },
{ "Simple Computation", SIMPLE_COMPUTATION, "simpleComputation", null, LOOP_COUNT },
{ "Computation Through Java", JAVA_COMPUTATION, "computationJava", null, LOOP_COUNT },
{ "Computation Through Callback", CALLBACK_COMPUTATION, "computationCallback", null, LOOP_COUNT / 2 },
{ "Compute Values in Java", COMPUTE_EACH_VALUE, "computationEachValue", null, LOOP_COUNT_SMALL },
{ "Read trace events for os-events", READ_LARGE_TRACE, "readTrace", ImmutableList.of(String.valueOf(LARGE_TRACE.getTracePath().getFileName())), LOOP_COUNT_SMALL },
{ "Read trace events for small trace", READ_SMALL_TRACE, "readTrace", ImmutableList.of(String.valueOf(FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getName())), LOOP_COUNT },
{ "TID analysis for Os-events", TID_ANALYSIS_LARGE_TRACE, "tidAnalysis", ImmutableList.of(String.valueOf(LARGE_TRACE.getTracePath().getFileName())), LOOP_COUNT_SMALL },
{ "TID analysis for small trace", TID_ANALYSIS_SMALL_TRACE, "tidAnalysis", ImmutableList.of(String.valueOf(FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getName())), LOOP_COUNT },
});
}
private static final Runnable EMPTY = () -> {
// Do nothing much, to benchmark script initialization
int i = 0;
System.out.println(i);
};
private static final Runnable SIMPLE_COMPUTATION = () -> {
// Compute the Collatz Conjecture sequence for integers between INITIAL_VALUE and LIMIT
int base = INITIAL_VALUE;
long value = base;
while (base < LIMIT) {
if (value == 1) {
value = base++;
}
if (value % 2 == 0) {
value = value / 2;
} else {
value = 3 * value + 1;
}
}
};
private static final Runnable JAVA_COMPUTATION = () -> {
TestModule testModule = new TestModule();
testModule.doJavaLoop();
};
private static final Runnable CALLBACK_COMPUTATION = () -> {
TestModule testModule = new TestModule();
testModule.doLoopWithCallback(value -> {
if (value % 2 == 0) {
return value / 2;
}
return 3 * value + 1;
});
};
private static final Runnable COMPUTE_EACH_VALUE = () -> {
// Compute the Collatz Conjecture sequence for integers between INITIAL_VALUE and LIMIT
TestModule testModule = new TestModule();
testModule.doJavaLoop();
int base = INITIAL_VALUE;
long value = base;
while (base < 100000) {
if (value == 1) {
value = base++;
}
value = testModule.compute(value);
}
};
private static void readTrace(String absolutePathToTrace) {
LttngKernelTrace trace = new LttngKernelTrace();
try {
trace.initTrace(null, absolutePathToTrace, CtfTmfEvent.class);
TraceScriptingModule module = new TraceScriptingModule();
ScriptEventsIterator eventIterator = module.getEventIterator(trace);
eventIterator.addEvent("sched_switch");
int schedSwitchCnt = 0;
while (eventIterator.hasNext()) {
ITmfEvent event = eventIterator.next();
if (event.getName().equals("sched_switch")) {
schedSwitchCnt++;
}
}
System.out.println("Count sched switch: " + schedSwitchCnt);
} catch (TmfTraceException e) {
fail(e.getMessage());
} finally {
trace.dispose();
}
}
private static final Runnable READ_LARGE_TRACE = () -> {
readTrace(LARGE_TRACE.getTracePath().toString());
};
private static final Runnable READ_SMALL_TRACE = () -> {
try {
readTrace(FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getAbsolutePath());
} catch (IOException e) {
fail(e.getMessage());
}
};
private static void deleteSupplementaryFiles(@NonNull ITmfTrace trace) {
/*
* Delete the supplementary files at the end of the benchmarks
*/
File suppDir = new File(TmfTraceManager.getSupplementaryFileDir(trace));
for (File file : suppDir.listFiles()) {
file.delete();
}
}
private static void runTidAnalysis(String absolutePathtoFile) {
LttngKernelTrace trace = new LttngKernelTrace();
TidAnalysisModule analysisModule = null;
try {
trace.initTrace(null, absolutePathtoFile, CtfTmfEvent.class);
analysisModule = new TidAnalysisModule();
analysisModule.setTrace(trace);
TmfTestHelper.executeAnalysis(analysisModule);
} catch (TmfTraceException | TmfAnalysisException e) {
fail(e.getMessage());
} finally {
deleteSupplementaryFiles(trace);
if (analysisModule != null) {
analysisModule.dispose();
}
trace.dispose();
}
}
private static final Runnable TID_ANALYSIS_SMALL_TRACE = () -> {
try {
runTidAnalysis(FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getAbsolutePath());
} catch (IOException e) {
fail(e.getMessage());
}
};
private static final Runnable TID_ANALYSIS_LARGE_TRACE = () -> {
runTidAnalysis(LARGE_TRACE.getTracePath().toString());
};
private final String fName;
private final Runnable fJavaMethod;
private final String fScript;
private final @Nullable List<@NonNull String> fArguments;
private final int fLoopCount;
/**
* Constructor
*
* @param name
* The name of the test
* @param javaMethod
* The java runnable method to benchmark the java part
* @param script
* The name of the files for this test. Language-specific
* extension will be appended
* @param arguments
* The list of arguments to pass to the script
* @param loopCount
* The number of times the benchmark should run
*/
public ScriptingBenchmark(String name, Runnable javaMethod, String script, @Nullable List<@NonNull String> arguments, int loopCount) {
fName = name;
fJavaMethod = javaMethod;
fScript = script;
fArguments = arguments;
fLoopCount = loopCount;
}
/**
* Prepare the workspace by preloading the required traces
*
* @throws CoreException
* Exception preparing the traces
* @throws IOException
* Exception preparing the traces
*/
@BeforeClass
public static void prepareWorkspace() throws CoreException, IOException {
IProject project = TmfProjectRegistry.createProject(DEFAULT_PROJECT, null, null);
final TmfProjectElement projectElement = TmfProjectRegistry.getProject(project, true);
TmfTraceFolder tracesFolder = projectElement.getTracesFolder();
if (tracesFolder != null) {
IFolder traceFolder = tracesFolder.getResource();
/* Add the all os events trace from benchmark */
Path tracePath = LARGE_TRACE.getTracePath();
IPath pathString = new org.eclipse.core.runtime.Path(tracePath.toString());
IResource linkedTrace = TmfImportHelper.createLink(traceFolder, pathString, pathString.lastSegment());
if (!(linkedTrace != null && linkedTrace.exists())) {
throw new NullPointerException("Trace cannot be created");
}
linkedTrace.setPersistentProperty(TmfCommonConstants.TRACETYPE,
"org.eclipse.linuxtools.lttng2.kernel.tracetype");
/* Add the django test trace */
String absolutePath = FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getAbsolutePath();
pathString = new org.eclipse.core.runtime.Path(absolutePath);
linkedTrace = TmfImportHelper.createLink(traceFolder, pathString, pathString.lastSegment());
if (!(linkedTrace != null && linkedTrace.exists())) {
throw new NullPointerException("Trace cannot be created");
}
linkedTrace.setPersistentProperty(TmfCommonConstants.TRACETYPE,
"org.eclipse.linuxtools.lttng2.kernel.tracetype");
// Refresh the project model
tracesFolder.refresh();
for (TmfTraceElement traceElement : tracesFolder.getTraces()) {
traceElement.refreshTraceType();
}
}
projectElement.refresh();
sfProject = projectElement;
}
/**
* Delete project and traces at the end
*/
@AfterClass
public static void deleteProject() {
TmfProjectElement project = sfProject;
if (project != null) {
Display.getDefault().syncExec(() -> ProjectModelTestData.deleteProject(project));
}
}
/**
* Benchmark the java runnable
*/
@Test
public void javaTest() {
Performance perf = Performance.getDefault();
PerformanceMeter pmJava = perf.createPerformanceMeter(JAVA_PREFIX + fName);
perf.tagAsSummary(pmJava, JAVA_PREFIX + fName, Dimension.CPU_TIME);
for (int i = 0; i < fLoopCount; i++) {
pmJava.start();
fJavaMethod.run();
pmJava.stop();
}
pmJava.commit();
}
/**
* Benchmark the javascript rhino engine
*/
@Test
public void javaScriptRhinoTest() {
IPath absoluteFilePath = ActivatorTest.getAbsoluteFilePath(JAVASCRIPT_PATH + fScript + JAVASCRIPT_EXTENSION);
Performance perf = Performance.getDefault();
PerformanceMeter pmJavaScript = perf.createPerformanceMeter(RHINO_PREFIX + fName);
perf.tagAsSummary(pmJavaScript, RHINO_PREFIX + fName, Dimension.CPU_TIME);
for (int i = 0; i < fLoopCount; i++) {
pmJavaScript.start();
ScriptExecutionHelper.executeScript(Objects.requireNonNull(absoluteFilePath.toOSString()), RHINO_ENGINE, fArguments);
pmJavaScript.stop();
}
pmJavaScript.commit();
}
/**
* Benchmark the javascript nashorn engine
*/
@Test
public void javaScriptNashornTest() {
IPath absoluteFilePath = ActivatorTest.getAbsoluteFilePath(JAVASCRIPT_PATH + fScript + JAVASCRIPT_EXTENSION);
Performance perf = Performance.getDefault();
PerformanceMeter pmJavaScript = perf.createPerformanceMeter(NASHORN_PREFIX + fName);
perf.tagAsSummary(pmJavaScript, NASHORN_PREFIX + fName, Dimension.CPU_TIME);
for (int i = 0; i < fLoopCount; i++) {
pmJavaScript.start();
ScriptExecutionHelper.executeScript(Objects.requireNonNull(absoluteFilePath.toOSString()), NASHORN_ENGINE, fArguments);
pmJavaScript.stop();
}
pmJavaScript.commit();
}
/**
* Benchmark the python py4j engine
*/
@Test
public void py4jTest() {
// See if a specific file for py4j exists, otherwise, use the python
// script
IPath absoluteFilePath;
try {
absoluteFilePath = ActivatorTest.getAbsoluteFilePath(PYTHON_PATH + "py4j_" + fScript + PYTHON_EXTENSION);
} catch (NullPointerException e) {
absoluteFilePath = ActivatorTest.getAbsoluteFilePath(PYTHON_PATH + fScript + PYTHON_EXTENSION);
}
Performance perf = Performance.getDefault();
PerformanceMeter pmPython = perf.createPerformanceMeter(PY4J_PREFIX + fName);
perf.tagAsSummary(pmPython, PY4J_PREFIX + fName, Dimension.CPU_TIME);
for (int i = 0; i < fLoopCount; i++) {
pmPython.start();
ScriptExecutionHelper.executeScript(Objects.requireNonNull(absoluteFilePath.toOSString()), PY4J_ENGINE, fArguments);
pmPython.stop();
}
pmPython.commit();
}
/**
* Benchmark the python jython engine
*/
@Test
public void jythonTest() {
// See if a specific file for py4j exists, otherwise, use the python
// script
IPath absoluteFilePath;
try {
absoluteFilePath = ActivatorTest.getAbsoluteFilePath(PYTHON_PATH + "jython_" + fScript + PYTHON_EXTENSION);
} catch (NullPointerException e) {
absoluteFilePath = ActivatorTest.getAbsoluteFilePath(PYTHON_PATH + fScript + PYTHON_EXTENSION);
}
Performance perf = Performance.getDefault();
PerformanceMeter pmPython = perf.createPerformanceMeter(JYTHON_PREFIX + fName);
perf.tagAsSummary(pmPython, JYTHON_PREFIX + fName, Dimension.CPU_TIME);
for (int i = 0; i < fLoopCount; i++) {
pmPython.start();
ScriptExecutionHelper.executeScript(Objects.requireNonNull(absoluteFilePath.toOSString()), JYTHON_ENGINE, fArguments);
pmPython.stop();
System.out.println("Did iteration " + i);
}
pmPython.commit();
}
private static @NonNull ITmfTrace getTraceForXml(String traceName) {
String tracePath = null;
try {
if (String.valueOf(LARGE_TRACE.getTracePath().getFileName()).equals(traceName)) {
tracePath = LARGE_TRACE.getTracePath().toString();
} else if (String.valueOf(FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getName()).equals(traceName)) {
tracePath = FileUtils.toFile(FileLocator.toFileURL(SMALL_TRACE.getTraceURL())).getAbsolutePath();
}
} catch (IOException e1) {
throw new NullPointerException("Cannot initialize trace: " + e1.getMessage());
}
if (tracePath == null) {
throw new NullPointerException("Cannot find trace: " + traceName);
}
// The trace is one of the 2 traces small or large
LttngKernelTrace trace = new LttngKernelTrace();
try {
trace.initTrace(null, tracePath, CtfTmfEvent.class);
} catch (TmfTraceException e) {
trace.dispose();
throw new NullPointerException("can't open the trace: " + e.getMessage());
}
return trace;
}
/**
* Benchmark the xml analysis that does the same
*
* @throws TmfAnalysisException
* Exceptions thrown by setting the trace to the module
*/
@Test
public void xmlTest() throws TmfAnalysisException {
// See if an XML file exists for this script
IPath absoluteFilePath;
try {
absoluteFilePath = ActivatorTest.getAbsoluteFilePath(XML_PATH + fScript + XML_EXTENSION);
} catch (NullPointerException e) {
// There was no file, don't fail the test.
return;
}
Performance perf = Performance.getDefault();
PerformanceMeter pmXmlcript = perf.createPerformanceMeter(XML_PREFIX + fName);
perf.tagAsSummary(pmXmlcript, XML_PREFIX + fName, Dimension.CPU_TIME);
List<@NonNull String> arguments = fArguments;
if (arguments == null || arguments.isEmpty()) {
throw new NullPointerException("XML analysis set to run, but the arguments do not contain the path to the trace");
}
String tracePath = arguments.get(0);
ITmfTrace trace = getTraceForXml(tracePath);
try {
for (int i = 0; i < fLoopCount; i++) {
TmfAbstractAnalysisModule module = null;
try {
module = TmfXmlTestUtils.getModuleInFile(Objects.requireNonNull(absoluteFilePath.toOSString()), XML_ANALYSIS_ID);
module.setTrace(trace);
pmXmlcript.start();
TmfTestHelper.executeAnalysis(module);
pmXmlcript.stop();
} finally {
if (module != null) {
module.dispose();
}
}
deleteSupplementaryFiles(trace);
}
pmXmlcript.commit();
} finally {
deleteSupplementaryFiles(trace);
trace.dispose();
}
}
}