This is an automated email from the ASF dual-hosted git repository.
mboehm7 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/systemds.git
The following commit(s) were added to refs/heads/main by this push:
new cdff385e6b [SYSTEMDS-3704] New resource-aware operator scheduling
cdff385e6b is described below
commit cdff385e6b64ee1ec0048f3bb73c8a69154240a3
Author: Anton Pötzsch <[email protected]>
AuthorDate: Mon Feb 3 07:31:30 2025 +0100
[SYSTEMDS-3704] New resource-aware operator scheduling
Closes #2197.
---
.../linearization/IDagLinearizerFactory.java | 13 +-
.../linearization/LinearizerResourceAwareFast.java | 318 +++++++++++++++++++++
.../LinearizerResourceAwareOptimal.java | 202 +++++++++++++
.../functions/linearization/ILinearizeTest.java | 16 +-
.../SystemDS-config-resource-aware-fast.xml | 22 ++
.../SystemDS-config-resource-aware-optimal.xml | 22 ++
6 files changed, 588 insertions(+), 5 deletions(-)
diff --git
a/src/main/java/org/apache/sysds/lops/compile/linearization/IDagLinearizerFactory.java
b/src/main/java/org/apache/sysds/lops/compile/linearization/IDagLinearizerFactory.java
index dcbd005b88..ea533e60dc 100644
---
a/src/main/java/org/apache/sysds/lops/compile/linearization/IDagLinearizerFactory.java
+++
b/src/main/java/org/apache/sysds/lops/compile/linearization/IDagLinearizerFactory.java
@@ -26,16 +26,17 @@ import org.apache.sysds.conf.ConfigurationManager;
public class IDagLinearizerFactory {
public static Log LOG =
LogFactory.getLog(IDagLinearizerFactory.class.getName());
-
+
public enum DagLinearizer {
- DEPTH_FIRST, BREADTH_FIRST, MIN_INTERMEDIATE, MAX_PARALLELIZE,
AUTO, PIPELINE_DEPTH_FIRST;
+ DEPTH_FIRST, BREADTH_FIRST, MIN_INTERMEDIATE, MAX_PARALLELIZE,
AUTO,
+ PIPELINE_DEPTH_FIRST, RESOURCE_AWARE_FAST,
RESOURCE_AWARE_OPTIMAL;
}
public static IDagLinearizer createDagLinearizer() {
DagLinearizer type =
ConfigurationManager.getLinearizationOrder();
return createDagLinearizer(type);
}
-
+
public static IDagLinearizer createDagLinearizer(DagLinearizer type) {
switch(type) {
case AUTO:
@@ -50,8 +51,12 @@ public class IDagLinearizerFactory {
return new LinearizerMinIntermediates();
case PIPELINE_DEPTH_FIRST:
return new LinearizerPipelineAware();
+ case RESOURCE_AWARE_FAST:
+ return new LinearizerResourceAwareFast();
+ case RESOURCE_AWARE_OPTIMAL:
+ return new LinearizerResourceAwareOptimal();
default:
- LOG.warn("Invalid DAG_LINEARIZATION: "+type+",
falling back to DEPTH_FIRST ordering");
+ LOG.warn("Invalid DAG_LINEARIZATION: " + type +
", falling back to DEPTH_FIRST ordering");
return new LinearizerDepthFirst();
}
}
diff --git
a/src/main/java/org/apache/sysds/lops/compile/linearization/LinearizerResourceAwareFast.java
b/src/main/java/org/apache/sysds/lops/compile/linearization/LinearizerResourceAwareFast.java
new file mode 100644
index 0000000000..996a4eeb48
--- /dev/null
+++
b/src/main/java/org/apache/sysds/lops/compile/linearization/LinearizerResourceAwareFast.java
@@ -0,0 +1,318 @@
+/*
+ * 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.sysds.lops.compile.linearization;
+
+import org.apache.sysds.lops.Lop;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public class LinearizerResourceAwareFast extends IDagLinearizer {
+
+ static class Dependency {
+ int nodeIndex;
+ int sequenceIndex;
+ List<Integer> dependencies;
+
+ Dependency(int sequenceIndex, int nodeIndex, List<Integer>
dependencies) {
+ this.sequenceIndex = sequenceIndex;
+ this.nodeIndex = nodeIndex;
+ this.dependencies = dependencies;
+ }
+
+ public int getSequenceIndex() {
+ return sequenceIndex;
+ }
+
+ public int getNodeIndex() {
+ return nodeIndex;
+ }
+
+ public List<Integer> getDependencies() {
+ return dependencies;
+ }
+ }
+
+ static class Item {
+ List<Integer> steps;
+ List<Integer> current;
+ Set<Intermediate> intermediates;
+ double maxMemoryUsage;
+
+ Item(List<Integer> steps, List<Integer> current,
Set<Intermediate> intermediates, double maxMemoryUsage) {
+ this.steps = steps;
+ this.current = current;
+ this.intermediates = intermediates;
+ this.maxMemoryUsage = maxMemoryUsage;
+ }
+
+ public List<Integer> getSteps() {
+ return steps;
+ }
+
+ public List<Integer> getCurrent() {
+ return current;
+ }
+
+ public double getMaxMemoryUsage() {
+ return maxMemoryUsage;
+ }
+
+ public Set<Intermediate> getIntermediates() {
+ return intermediates;
+ }
+ }
+
+ static class Intermediate {
+ List<Long> lopIDs;
+ double memoryUsage;
+
+ Intermediate(List<Long> lopIDs, double memoryUsage) {
+ this.lopIDs = lopIDs;
+ this.memoryUsage = memoryUsage;
+ }
+
+ void remove(long ID) {
+ lopIDs.remove(ID);
+ }
+
+ public List<Long> getLopIDs() {
+ return lopIDs;
+ }
+
+ public double getMemoryUsage() {
+ return memoryUsage;
+ }
+ }
+
+ List<Lop> remaining;
+
+ @Override
+ public List<Lop> linearize(List<Lop> dag) {
+ List<List<Lop>> sequences = new ArrayList<>();
+ remaining = new ArrayList<>(dag);
+
+ List<Lop> outputNodes = remaining.stream().filter(node ->
node.getOutputs().isEmpty())
+ .collect(Collectors.toList());
+
+ for(Lop outputNode : outputNodes) {
+ sequences.add(findSequence(outputNode));
+ }
+
+ while(!remaining.isEmpty()) {
+ int maxLevel =
remaining.stream().mapToInt(Lop::getLevel).max().getAsInt();
+ Lop node = remaining.stream().filter(n -> n.getLevel()
== maxLevel).findFirst().orElseThrow();
+ sequences.add(findSequence(node));
+ }
+
+ return scheduleSequences(sequences);
+ }
+
+ List<Lop> scheduleSequences(List<List<Lop>> sequences) {
+ Set<List<Integer>> visited = new HashSet<>();
+ List<Item> scheduledItems = new ArrayList<>();
+
+ Set<Dependency> dependencies = getDependencies(sequences);
+ List<Integer> sequencesMaxIndex = sequences.stream().map(entry
-> entry.size() - 1)
+ .collect(Collectors.toList());
+
+ Item currentItem = new Item(new ArrayList<>(),
Collections.nCopies(sequences.size(), -1), new HashSet<>(), 0.0);
+
+ while(!currentItem.getCurrent().equals(sequencesMaxIndex)) {
+
+ for(int i = 0; i < sequences.size(); i++) {
+
+ List<Lop> sequence = sequences.get(i);
+
+ if(currentItem.getCurrent().get(i) + 1 <
sequence.size()) {
+ List<Integer> newCurrent = new
ArrayList<>(currentItem.getCurrent());
+ newCurrent.set(i, newCurrent.get(i) +
1);
+
+ if(!visited.contains(newCurrent)) {
+ Set<Dependency>
filteredDependencies = dependencies.stream()
+ .filter(entry ->
entry.getNodeIndex() == newCurrent.get(entry.getSequenceIndex()))
+
.collect(Collectors.toSet());
+
+ boolean dependencyIssue =
filteredDependencies.parallelStream().anyMatch(
+ dependency ->
IntStream.range(0, newCurrent.size()).anyMatch(
+ j -> j !=
dependency.getSequenceIndex() &&
+
newCurrent.get(j) < dependency.getDependencies().get(j)));
+
+ if(!dependencyIssue) {
+ Set<Intermediate>
newIntermediates = new HashSet<>(currentItem.getIntermediates());
+
+ Lop nextLop =
sequence.get(newCurrent.get(i));
+
+ Iterator<Intermediate>
intermediateIter = newIntermediates.iterator();
+
+
while(intermediateIter.hasNext()) {
+ Intermediate
entry = intermediateIter.next();
+
entry.remove(nextLop.getID());
+
if(entry.getLopIDs().isEmpty())
+
intermediateIter.remove();
+ }
+
+
newIntermediates.add(new Intermediate(
+
nextLop.getOutputs().stream().map(Lop::getID).collect(Collectors.toList()),
+
nextLop.getOutputMemoryEstimate()));
+
+ List<Integer> newSteps
= new ArrayList<>(currentItem.getSteps());
+ newSteps.add(i);
+
+ double mem =
newIntermediates.stream().map(Intermediate::getMemoryUsage)
+
.reduce((double) 0, Double::sum);
+
+ Item newItem = new
Item(newSteps, newCurrent, newIntermediates,
+ Math.max(mem,
currentItem.getMaxMemoryUsage()));
+
+ int index =
Collections.binarySearch(scheduledItems, newItem,
+
Comparator.comparing(Item::getMaxMemoryUsage));
+
+ if(index < 0) {
+ index = -index
- 1;
+ }
+
+
scheduledItems.add(index, newItem);
+ }
+ visited.add(newCurrent);
+ }
+ }
+ }
+
+ currentItem = scheduledItems.remove(0);
+ }
+
+ return walkPath(sequences, currentItem.getSteps());
+ }
+
+ List<Lop> walkPath(List<List<Lop>> sequences, List<Integer> path) {
+ Iterator<Integer> iterator = path.iterator();
+ List<Lop> sequence = new ArrayList<>();
+
+ while(iterator.hasNext()) {
+ sequence.add(sequences.get(iterator.next()).remove(0));
+ }
+
+ return sequence;
+ }
+
+ List<Lop> findSequence(Lop startNode) {
+ List<Lop> sequence = new ArrayList<>();
+ Lop currentNode = startNode;
+ sequence.add(currentNode);
+ remaining.remove(currentNode);
+
+ while(currentNode.getInputs().size() == 1) {
+ if(remaining.contains(currentNode.getInput(0))) {
+ currentNode = currentNode.getInput(0);
+ sequence.add(currentNode);
+ remaining.remove(currentNode);
+ }
+ else {
+ Collections.reverse(sequence);
+ return sequence;
+ }
+ }
+
+ Collections.reverse(sequence);
+
+ List<Lop> children = currentNode.getInputs();
+
+ if(children.isEmpty()) {
+ return sequence;
+ }
+
+ List<List<Lop>> childSequences = new ArrayList<>();
+
+ for(Lop child : children) {
+ if(remaining.contains(child)) {
+ childSequences.add(findSequence(child));
+ }
+ }
+
+ List<Lop> finalSequence = scheduleSequences(childSequences);
+
+ return Stream.concat(finalSequence.stream(),
sequence.stream()).collect(Collectors.toList());
+ }
+
+ Set<Dependency> getDependencies(List<List<Lop>> sequences) {
+ Set<Dependency> dependencies = new HashSet<>();
+
+ // Get IDs of each Lop in each sequence for faster lookup
+ List<List<Long>> sequencesLopIDs = sequences.stream()
+ .map(sequence ->
sequence.stream().map(Lop::getID).collect(Collectors.toList()))
+ .collect(Collectors.toList());
+
+ int lastSequenceWithOutput = -1;
+
+ // Go through each sequence and check for dependencies
+ for(int j = 0; j < sequences.size(); j++) {
+ List<Lop> sequence = sequences.get(j);
+ int sequenceSize = sequence.size();
+ int sequenceIndex = j;
+
+ // Check if the current sequence depends on other
sequences
+ sequence.get(0).getInputs().forEach(input -> {
+ long inputID = input.getID();
+ List<Integer> dependencyIndices =
sequencesLopIDs.stream()
+ .map(list -> list.contains(inputID) ?
list.indexOf(inputID) : -1).collect(Collectors.toList());
+
+ dependencies.add(new Dependency(sequenceIndex,
0, dependencyIndices));
+ });
+
+ // Check for Lops that depends on Lops from other
sequences
+ for(int k = 0; k < sequenceSize; k++) {
+ int finalK = k;
+ int finalJ = j;
+ sequence.get(k).getInputs().forEach(input -> {
+ long inputID = input.getID();
+
if(!sequencesLopIDs.get(finalJ).contains(inputID)) {
+ List<Integer> dependencyIndices
= sequencesLopIDs.stream()
+ .map(list ->
list.contains(inputID) ? list.indexOf(inputID) : -1)
+
.collect(Collectors.toList());
+
+ dependencies.add(new
Dependency(finalJ, finalK, dependencyIndices));
+ }
+ });
+ }
+
+ // Dependency chain between output Lops so that the
outputs are in the correct order
+ if(sequence.get(sequenceSize -
1).getOutputs().isEmpty()) {
+ if(lastSequenceWithOutput != -1) {
+ List<Integer> dependencyList = new
ArrayList<>(Collections.nCopies(sequences.size(), -1));
+
dependencyList.set(lastSequenceWithOutput,
+
sequences.get(lastSequenceWithOutput).size() - 1);
+ dependencies.add(new Dependency(j,
sequenceSize - 1, dependencyList));
+ }
+ lastSequenceWithOutput = j;
+ }
+ }
+
+ return dependencies;
+ }
+}
diff --git
a/src/main/java/org/apache/sysds/lops/compile/linearization/LinearizerResourceAwareOptimal.java
b/src/main/java/org/apache/sysds/lops/compile/linearization/LinearizerResourceAwareOptimal.java
new file mode 100644
index 0000000000..3c10903658
--- /dev/null
+++
b/src/main/java/org/apache/sysds/lops/compile/linearization/LinearizerResourceAwareOptimal.java
@@ -0,0 +1,202 @@
+/*
+ * 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.sysds.lops.compile.linearization;
+
+import java.util.List;
+
+import org.apache.sysds.lops.Lop;
+
+public class LinearizerResourceAwareOptimal extends IDagLinearizer {
+
+ @Override
+ public List<Lop> linearize(List<Lop> v) {
+ return v;
+ }
+
+// FIXME this implementation does not terminate (even for toy examples)
+
+// static class OptimalList {
+// List<Lop> optimalSequence;
+// double maxMemoryUsage;
+//
+// public OptimalList() {
+// optimalSequence = new ArrayList<>();
+// maxMemoryUsage = -1;
+// }
+//
+// public void setNewValues(List<Lop> optimalSequence, double
maxMemoryUsage) {
+// this.optimalSequence = optimalSequence;
+// this.maxMemoryUsage = maxMemoryUsage;
+// }
+//
+// public double getMaxMemoryUsage() {
+// return maxMemoryUsage;
+// }
+//
+// public List<Lop> getOptimalSequence() {
+// return optimalSequence;
+// }
+// }
+//
+// static class Dependency {
+// Lop node;
+// Lop dependsOn;
+//
+// public Dependency(Lop node, Lop dependency) {
+// this.node = node;
+// this.dependsOn = dependency;
+// }
+//
+// public Lop getDependency() {
+// return dependsOn;
+// }
+//
+// public Lop getNode() {
+// return node;
+// }
+// }
+//
+// static class MemoryEntry {
+// Set<Long> targets;
+// double requiredMemory;
+//
+// public MemoryEntry(Set<Long> targets, double requiredMemory) {
+// this.targets = targets;
+// this.requiredMemory = requiredMemory;
+// }
+//
+// public Set<Long> getTargets() {
+// return targets;
+// }
+//
+// public void removeTarget(long target) {
+// targets.remove(target);
+// }
+//
+// public double getRequiredMemory() {
+// return requiredMemory;
+// }
+// }
+//
+// Set<Dependency> dependencies = new HashSet<>();
+// OptimalList optimalList = new OptimalList();
+//
+// @Override
+// public List<Lop> linearize(List<Lop> v) {
+// // At first, we want to find all transitive dependencies
between all nodes
+// // We will take every node and recursively search for all lops
our node depends on.
+// // Then we create a set with all does dependencies.
+// v.forEach(lop -> {
+// Set<Lop> dependencyNodes = getDependenciesOfNode(lop);
+// dependencyNodes.forEach(dependencyNode -> {
+// dependencies.add(new Dependency(lop,
dependencyNode));
+// });
+// });
+//
+// // The Lops without outputs need to stay in order because they
could be f.e. prints.
+// // Therefore, we create a dependency chain between all lops
without outputs to let them stay in order.
+// List<Lop> outputLops = v.stream().filter(lop ->
lop.getOutputs().isEmpty()).collect(Collectors.toList());
+// for(int i = 0; i < outputLops.size() - 1; i++) {
+// dependencies.add(new Dependency(outputLops.get(i + 1),
outputLops.get(i)));
+// }
+//
+// // In the next step we want to discover all possible
permutations
+// // and find the one with the least memory requirement.
+// findBestSequence(new ArrayList<>(), v);
+//
+// return optimalList.getOptimalSequence();
+// }
+//
+// Set<Lop> getDependenciesOfNode(Lop node) {
+// Set<Lop> dependencyNodes = new HashSet<>();
+//
+// node.getInputs().forEach(input -> {
+// dependencyNodes.addAll(getDependenciesOfNode(input));
+// });
+// dependencyNodes.addAll(node.getInputs());
+//
+// return dependencyNodes;
+// }
+//
+// boolean isItemInDependencyList(Dependency item) {
+// return dependencies.stream().anyMatch(i ->
i.getDependency().getID() == item.getDependency().getID() &&
+// i.getNode().getID() == item.getNode().getID());
+// }
+//
+// void findBestSequence(List<Lop> visitedLops, List<Lop> remainingLops) {
+// if(remainingLops.isEmpty()) {
+// double maxMemoryUsage = getMemoryUsage(visitedLops);
+// if(optimalList.getOptimalSequence().isEmpty() ||
maxMemoryUsage < optimalList.getMaxMemoryUsage()) {
+// optimalList.setNewValues(visitedLops,
maxMemoryUsage);
+// }
+// }
+//
+// remainingLops.parallelStream().forEach(entry -> {
+// boolean dependencyViolation = false;
+//
+// for(Lop remainingLop : remainingLops) {
+// if(entry.getID() != remainingLop.getID() &&
+// isItemInDependencyList(new
Dependency(entry, remainingLop))) {
+// dependencyViolation = true;
+// break;
+// }
+// }
+//
+// if(!dependencyViolation) {
+// List<Lop> newVisitedLops = new
ArrayList<>(visitedLops);
+// List<Lop> newRemainingLops = new
ArrayList<>(remainingLops);
+//
+// newVisitedLops.add(entry);
+// newRemainingLops.remove(entry);
+//
+// findBestSequence(newVisitedLops,
newRemainingLops);
+// }
+// });
+// }
+//
+// double getMemoryUsage(List<Lop> list) {
+// Set<MemoryEntry> intermediates = new HashSet<>();
+// double memoryEstimate = 0;
+//
+// for(Lop lop : list) {
+// Iterator<MemoryEntry> intermediateIter =
intermediates.iterator();
+//
+// while(intermediateIter.hasNext()) {
+// MemoryEntry entry = intermediateIter.next();
+// entry.removeTarget(lop.getID());
+// if (entry.getTargets().isEmpty())
intermediateIter.remove();
+// }
+//
+// Set<Long> outputIDs =
lop.getOutputs().stream().map(Lop::getID).collect(Collectors.toSet());
+// intermediates.add(new MemoryEntry(outputIDs,
lop.getOutputMemoryEstimate()));
+//
+// double requiredMemory =
intermediates.stream().map(MemoryEntry::getRequiredMemory)
+// .reduce((double) 0, Double::sum);
+//
+// memoryEstimate = Math.max(requiredMemory,
memoryEstimate);
+//
+// if(optimalList.getMaxMemoryUsage() != -1 &&
memoryEstimate > optimalList.getMaxMemoryUsage()) {
+// return memoryEstimate;
+// }
+// }
+//
+// return memoryEstimate;
+// }
+}
diff --git
a/src/test/java/org/apache/sysds/test/functions/linearization/ILinearizeTest.java
b/src/test/java/org/apache/sysds/test/functions/linearization/ILinearizeTest.java
index 2b2e179217..90033a8037 100644
---
a/src/test/java/org/apache/sysds/test/functions/linearization/ILinearizeTest.java
+++
b/src/test/java/org/apache/sysds/test/functions/linearization/ILinearizeTest.java
@@ -34,6 +34,7 @@ import org.apache.sysds.test.AutomatedTestBase;
import org.apache.sysds.test.TestConfiguration;
import org.apache.sysds.test.TestUtils;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
//additional imports
@@ -61,7 +62,9 @@ public class ILinearizeTest extends AutomatedTestBase {
DEPTH_FIRST("SystemDS-config-depth-first.xml"),
MIN_INTERMEDIATE("SystemDS-config-min-intermediate.xml"),
MAX_PARALLELIZE("SystemDS-config-max-parallelize.xml"),
-
PIPELINE_DEPTH_FIRST("SystemDS-config-pipeline-depth-first.xml");
+
PIPELINE_DEPTH_FIRST("SystemDS-config-pipeline-depth-first.xml"),
+ RESOURCE_AWARE_FAST("SystemDS-config-resource-aware-fast.xml"),
+
RESOURCE_AWARE_OPTIMAL("SystemDS-config-resource-aware-optimal.xml");
public final String filePath;
@@ -212,6 +215,17 @@ public class ILinearizeTest extends AutomatedTestBase {
runLinearizationTest(Config.MIN_INTERMEDIATE);
}
+ @Test
+ public void testResourceAwareFastLinearization() {
+ runLinearizationTest(Config.RESOURCE_AWARE_FAST);
+ }
+
+ @Test
+ @Ignore
+ public void testResourceAwareOptimalLinearization() {
+ runLinearizationTest(Config.RESOURCE_AWARE_OPTIMAL);
+ }
+
private void runLinearizationTest(Config config) {
//load config
CONF_FILE = getConfPath(config);
diff --git
a/src/test/scripts/functions/linearization/SystemDS-config-resource-aware-fast.xml
b/src/test/scripts/functions/linearization/SystemDS-config-resource-aware-fast.xml
new file mode 100644
index 0000000000..a2a1daa0c1
--- /dev/null
+++
b/src/test/scripts/functions/linearization/SystemDS-config-resource-aware-fast.xml
@@ -0,0 +1,22 @@
+<!--
+ * 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.
+-->
+
+<root>
+
<sysds.compile.linearization>RESOURCE_AWARE_FAST</sysds.compile.linearization>
+</root>
diff --git
a/src/test/scripts/functions/linearization/SystemDS-config-resource-aware-optimal.xml
b/src/test/scripts/functions/linearization/SystemDS-config-resource-aware-optimal.xml
new file mode 100644
index 0000000000..22289898f2
--- /dev/null
+++
b/src/test/scripts/functions/linearization/SystemDS-config-resource-aware-optimal.xml
@@ -0,0 +1,22 @@
+<!--
+ * 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.
+-->
+
+<root>
+
<sysds.compile.linearization>RESOURCE_AWARE_OPTIMAL</sysds.compile.linearization>
+</root>