Skip to content

Commit 3eb1d11

Browse files
committed
[GR-37247] Reduce memory footprint.
PullRequest: graal/11454
2 parents 4f2702d + aa1e104 commit 3eb1d11

File tree

18 files changed

+138
-33
lines changed

18 files changed

+138
-33
lines changed

compiler/src/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Edges.java

+9
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ public void copy(Node fromNode, Node toNode) {
204204
}
205205
}
206206

207+
void minimizeSize(Node node) {
208+
for (int i = getDirectCount(); i < getCount(); i++) {
209+
NodeList<Node> list = getNodeList(node, offsets, i);
210+
if (list != null) {
211+
list.minimizeSize();
212+
}
213+
}
214+
}
215+
207216
/**
208217
* Sets the value of a given edge without notifying the new and old nodes on the other end of
209218
* the edge of the change.

compiler/src/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Graph.java

+51-6
Original file line numberDiff line numberDiff line change
@@ -948,15 +948,31 @@ protected void afterNodeIdChange(Node node, Object value) {
948948
* of nodes is compressed such that all non-null entries precede all null entries while
949949
* preserving the ordering between the nodes within the list.
950950
*/
951-
public boolean maybeCompress() {
952-
if (debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod()) {
951+
public final boolean maybeCompress() {
952+
return compress(false);
953+
}
954+
955+
/**
956+
* Minimize the memory occupied by the graph by trimming all node arrays to the minimum size.
957+
* Note that this can make subsequent optimization phases run slower, because additions to the
958+
* graph must re-allocate larger arrays again. So invoking this method is only beneficial if a
959+
* graph is alive for a long time.
960+
*/
961+
public final void minimizeSize() {
962+
compress(true);
963+
}
964+
965+
protected boolean compress(boolean minimizeSize) {
966+
if (!minimizeSize && (debug.isDumpEnabledForMethod() || debug.isLogEnabledForMethod())) {
953967
return false;
954968
}
955969
int liveNodeCount = getNodeCount();
956-
int liveNodePercent = liveNodeCount * 100 / nodesSize;
957-
int compressionThreshold = Options.GraphCompressionThreshold.getValue(options);
958-
if (compressionThreshold == 0 || liveNodePercent >= compressionThreshold) {
959-
return false;
970+
if (!minimizeSize) {
971+
int liveNodePercent = liveNodeCount * 100 / nodesSize;
972+
int compressionThreshold = Options.GraphCompressionThreshold.getValue(options);
973+
if (compressionThreshold == 0 || liveNodePercent >= compressionThreshold) {
974+
return false;
975+
}
960976
}
961977
GraphCompressions.increment(debug);
962978
int nextId = 0;
@@ -984,9 +1000,38 @@ public boolean maybeCompress() {
9841000
compressions++;
9851001
nodesDeletedBeforeLastCompression += nodesDeletedSinceLastCompression;
9861002
nodesDeletedSinceLastCompression = 0;
1003+
1004+
if (minimizeSize) {
1005+
/* Trim the array of all alive nodes itself. */
1006+
nodes = trimArrayToNewSize(nodes, nextId, NodeList.EMPTY_NODE_ARRAY);
1007+
/* Remove deleted nodes from the linked list of Node.typeCacheNext. */
1008+
recomputeIterableNodeLists();
1009+
/* Trim node arrays used within each node. */
1010+
for (Node node : nodes) {
1011+
node.extraUsages = trimArrayToNewSize(node.extraUsages, node.extraUsagesCount, NodeList.EMPTY_NODE_ARRAY);
1012+
node.getNodeClass().getInputEdges().minimizeSize(node);
1013+
node.getNodeClass().getSuccessorEdges().minimizeSize(node);
1014+
}
1015+
}
9871016
return true;
9881017
}
9891018

1019+
static <T> T[] trimArrayToNewSize(T[] input, int newSize, T[] emptyArray) {
1020+
assert emptyArray.length == 0;
1021+
if (input.length == newSize) {
1022+
return input;
1023+
}
1024+
for (int i = newSize; i < input.length; i++) {
1025+
GraalError.guarantee(input[i] == null, "removing non-null element");
1026+
}
1027+
1028+
if (newSize == 0) {
1029+
return emptyArray;
1030+
} else {
1031+
return Arrays.copyOf(input, newSize);
1032+
}
1033+
}
1034+
9901035
/**
9911036
* Returns an {@link Iterable} providing all the live nodes whose type is compatible with
9921037
* {@code type}.

compiler/src/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/NodeList.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ protected NodeList(Node self, int initialSize) {
7676
checkMaxSize(initialSize);
7777
this.size = initialSize;
7878
this.initialSize = initialSize;
79-
this.nodes = new Node[initialSize];
79+
if (initialSize == 0) {
80+
this.nodes = EMPTY_NODE_ARRAY;
81+
} else {
82+
this.nodes = new Node[initialSize];
83+
}
8084
}
8185

8286
protected NodeList(Node self, T[] elements) {
@@ -232,8 +236,13 @@ public void initialize(int index, Node node) {
232236
void copy(NodeList<? extends Node> other) {
233237
self.incModCount();
234238
incModCount();
235-
Node[] newNodes = new Node[other.size];
236-
System.arraycopy(other.nodes, 0, newNodes, 0, newNodes.length);
239+
Node[] newNodes;
240+
if (other.size == 0) {
241+
newNodes = EMPTY_NODE_ARRAY;
242+
} else {
243+
newNodes = new Node[other.size];
244+
System.arraycopy(other.nodes, 0, newNodes, 0, newNodes.length);
245+
}
237246
nodes = newNodes;
238247
size = other.size;
239248
}
@@ -279,6 +288,10 @@ void clearWithoutUpdate() {
279288
size = 0;
280289
}
281290

291+
void minimizeSize() {
292+
nodes = Graph.trimArrayToNewSize(nodes, size, EMPTY_NODE_ARRAY);
293+
}
294+
282295
@Override
283296
@SuppressWarnings("unchecked")
284297
public boolean remove(Object node) {

compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/AbstractForeignCallStub.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ protected final StructuredGraph getGraph(DebugContext debug, CompilationIdentifi
221221
ResolvedJavaMethod getAndClearObjectResult = foreignCallSnippets.getAndClearObjectResult.getMethod();
222222
ResolvedJavaMethod verifyObject = foreignCallSnippets.verifyObject.getMethod();
223223
ResolvedJavaMethod thisMethod = getGraphMethod();
224-
GraphKit kit = new GraphKit(debug, thisMethod, providers, wordTypes, providers.getGraphBuilderPlugins(), compilationId, toString(), false);
224+
GraphKit kit = new GraphKit(debug, thisMethod, providers, wordTypes, providers.getGraphBuilderPlugins(), compilationId, toString(), false, true);
225225
StructuredGraph graph = kit.getGraph();
226226
graph.disableFrameStateVerification();
227227
ReadRegisterNode thread = kit.append(new ReadRegisterNode(providers.getRegisters().getThreadRegister(), wordTypes.getWordKind(), true, false));

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -560,8 +560,8 @@ protected void afterNodeIdChange(Node node, Object value) {
560560
}
561561

562562
@Override
563-
public boolean maybeCompress() {
564-
if (super.maybeCompress()) {
563+
protected boolean compress(boolean minimizeSize) {
564+
if (super.compress(minimizeSize)) {
565565
/*
566566
* The schedule contains a NodeMap which is unusable after compression.
567567
*/

compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ protected abstract static class Structure {
106106
}
107107

108108
public GraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, Plugins graphBuilderPlugins, CompilationIdentifier compilationId, String name,
109-
boolean trackNodeSourcePosition) {
109+
boolean trackNodeSourcePosition, boolean recordInlinedMethods) {
110110
super(providers);
111-
StructuredGraph.Builder builder = new StructuredGraph.Builder(debug.getOptions(), debug).compilationId(compilationId).profileProvider(null);
111+
StructuredGraph.Builder builder = new StructuredGraph.Builder(debug.getOptions(), debug).recordInlinedMethods(recordInlinedMethods).compilationId(compilationId).profileProvider(null);
112112
if (name != null) {
113113
builder.name(name);
114114
} else {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ public static AnalysisParsedGraph parseBytecode(BigBang bb, AnalysisMethod metho
114114
return EMPTY;
115115
}
116116

117-
graph = new StructuredGraph.Builder(options, debug).method(method).build();
117+
graph = new StructuredGraph.Builder(options, debug)
118+
.method(method)
119+
.recordInlinedMethods(false)
120+
.build();
118121
try (DebugContext.Scope s = debug.scope("ClosedWorldAnalysis", graph, method)) {
119122

120123
// enable this logging to get log output in compilation passes

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ private boolean parse() {
205205
* needed after static analysis.
206206
*/
207207
if (bb.strengthenGraalGraphs()) {
208+
graph.minimizeSize();
208209
method.setAnalyzedGraph(graph);
209210
}
210211

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/CompletionExecutor.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private enum State {
6060
private final AtomicReference<State> state;
6161
private final LongAdder postedOperations;
6262
private final LongAdder completedOperations;
63-
private final List<DebugContextRunnable> postedBeforeStart;
63+
private List<DebugContextRunnable> postedBeforeStart;
6464
private final CopyOnWriteArrayList<Throwable> exceptions = new CopyOnWriteArrayList<>();
6565

6666
private ExecutorService executorService;
@@ -90,7 +90,6 @@ public CompletionExecutor(BigBang bb, ForkJoinPool forkJoin, Runnable heartbeatC
9090
state = new AtomicReference<>(State.UNUSED);
9191
postedOperations = new LongAdder();
9292
completedOperations = new LongAdder();
93-
postedBeforeStart = new ArrayList<>();
9493
startingThread = Thread.currentThread();
9594
}
9695

@@ -103,7 +102,7 @@ public void init(Timing newTiming) {
103102
setState(State.BEFORE_START);
104103
postedOperations.reset();
105104
completedOperations.reset();
106-
postedBeforeStart.clear();
105+
postedBeforeStart = new ArrayList<>();
107106
vmConfig = bb.getHostVM().getConfiguration();
108107
}
109108

@@ -213,7 +212,7 @@ public void start() {
213212

214213
setState(State.STARTED);
215214
postedBeforeStart.forEach(this::execute);
216-
postedBeforeStart.clear();
215+
postedBeforeStart = null;
217216
}
218217

219218
private void setState(State newState) {
@@ -271,7 +270,7 @@ public long complete() throws InterruptedException {
271270
}
272271

273272
public long getPostedOperations() {
274-
return postedOperations.sum() + postedBeforeStart.size();
273+
return postedOperations.sum() + (postedBeforeStart == null ? 0 : postedBeforeStart.size());
275274
}
276275

277276
public boolean isSequential() {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateReplacements.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,12 @@ public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod
220220

221221
EncodedGraph encodedGraph = new EncodedGraph(snippetEncoding, startOffset, snippetObjects, snippetNodeClasses, null, null, false, trackNodeSourcePosition);
222222
try (DebugContext debug = openSnippetDebugContext("SVMSnippet_", method, options)) {
223-
StructuredGraph result = new StructuredGraph.Builder(options, debug).method(method).trackNodeSourcePosition(trackNodeSourcePosition).setIsSubstitution(true).build();
223+
StructuredGraph result = new StructuredGraph.Builder(options, debug)
224+
.method(method)
225+
.trackNodeSourcePosition(trackNodeSourcePosition)
226+
.recordInlinedMethods(false)
227+
.setIsSubstitution(true)
228+
.build();
224229
PEGraphDecoder graphDecoder = new PEGraphDecoder(ConfigurationValues.getTarget().arch, result, providers, null, snippetInvocationPlugins, new InlineInvokePlugin[0], parameterPlugin, null,
225230
null, null, new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), true) {
226231

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public class SubstrateGraphKit extends GraphKit {
9898

9999
public SubstrateGraphKit(DebugContext debug, ResolvedJavaMethod stubMethod, Providers providers, WordTypes wordTypes, GraphBuilderConfiguration.Plugins graphBuilderPlugins,
100100
CompilationIdentifier compilationId) {
101-
super(debug, stubMethod, providers, wordTypes, graphBuilderPlugins, compilationId, null, SubstrateOptions.parseOnce());
101+
super(debug, stubMethod, providers, wordTypes, graphBuilderPlugins, compilationId, null, SubstrateOptions.parseOnce(), false);
102102
assert wordTypes != null : "Support for Word types is mandatory";
103103
frameState = new FrameStateBuilder(this, stubMethod, graph);
104104
frameState.disableKindVerification();

substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,13 @@ public static StructuredGraph decodeGraph(DebugContext debug, String name, Compi
324324
}
325325

326326
boolean isSubstitution = method.getAnnotation(Snippet.class) != null;
327-
StructuredGraph graph = new StructuredGraph.Builder(debug.getOptions(), debug).name(name).method(method).compilationId(compilationId).setIsSubstitution(isSubstitution).build();
327+
StructuredGraph graph = new StructuredGraph.Builder(debug.getOptions(), debug)
328+
.name(name)
329+
.method(method)
330+
.recordInlinedMethods(false)
331+
.compilationId(compilationId)
332+
.setIsSubstitution(isSubstitution)
333+
.build();
328334
GraphDecoder decoder = new GraphDecoder(ConfigurationValues.getTarget().arch, graph);
329335
decoder.decode(encodedGraph);
330336
return graph;

substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,10 @@ private void processMethod(CallTreeNode node, Deque<CallTreeNode> worklist, BigB
534534
return;
535535
}
536536
parse = true;
537-
graph = new StructuredGraph.Builder(debug.getOptions(), debug, AllowAssumptions.YES).method(method).build();
537+
graph = new StructuredGraph.Builder(debug.getOptions(), debug, AllowAssumptions.YES)
538+
.method(method)
539+
.recordInlinedMethods(false)
540+
.build();
538541
}
539542

540543
try (DebugContext.Scope scope = debug.scope("RuntimeCompile", graph)) {
@@ -730,6 +733,9 @@ public void beforeCompilation(BeforeCompilationAccess c) {
730733
GraalSupport.setGraphEncoding(config, graphEncoder.getEncoding(), graphEncoder.getObjects(), graphEncoder.getNodeClasses());
731734

732735
objectReplacer.updateDataDuringAnalysis();
736+
737+
/* All the temporary data structures used during encoding are no longer necessary. */
738+
graphEncoder = null;
733739
}
734740

735741
private static void removeUnreachableInvokes(CallTreeNode node) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/EarlyClassInitializerAnalysis.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ private boolean canInitializeWithoutSideEffects(ResolvedJavaMethod clinit, Set<C
161161

162162
GraphBuilderConfiguration graphBuilderConfig = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
163163

164-
StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(clinit).build();
164+
StructuredGraph graph = new StructuredGraph.Builder(options, debug)
165+
.method(clinit)
166+
.recordInlinedMethods(false)
167+
.build();
165168
graph.setGuardsStage(GuardsStage.FIXED_DEOPTS);
166169
GraphBuilderPhase.Instance builderPhase = new ClassInitializerGraphBuilderPhase(context, graphBuilderConfig, context.getOptimisticOptimizations());
167170

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ public interface CompileFunction {
219219

220220
private SnippetReflectionProvider snippetReflection;
221221
private final FeatureHandler featureHandler;
222+
private final OptionValues compileOptions;
222223

223224
private volatile boolean inliningProgress;
224225

@@ -391,6 +392,7 @@ public CompileQueue(DebugContext debug, FeatureHandler featureHandler, HostedUni
391392
this.executor = new CompletionExecutor(universe.getBigBang(), executorService, universe.getBigBang().getHeartbeatCallback());
392393
this.featureHandler = featureHandler;
393394
this.snippetReflection = snippetReflection;
395+
this.compileOptions = getCustomizedOptions(debug);
394396

395397
callForReplacements(debug, runtimeConfig);
396398
}
@@ -689,6 +691,7 @@ private void doInlineTrivial(DebugContext debug, final HostedMethod method) {
689691
* Publish the new graph, it can be picked up immediately by other threads
690692
* trying to inline this method.
691693
*/
694+
graph.minimizeSize();
692695
method.compilationInfo.setGraph(graph);
693696
checkTrivial(method);
694697
inliningProgress = true;
@@ -835,13 +838,12 @@ private StructuredGraph transplantGraph(DebugContext debug, HostedMethod hMethod
835838
*/
836839
aMethod.setAnalyzedGraph(null);
837840

838-
OptionValues options = getCustomizedOptions(debug);
839841
/*
840842
* The static analysis always needs NodeSourcePosition. But for AOT compilation, we only
841843
* need to preserve them when explicitly enabled, to reduce memory pressure.
842844
*/
843-
boolean trackNodeSourcePosition = GraalOptions.TrackNodeSourcePosition.getValue(options);
844-
StructuredGraph graph = aGraph.copy(universe.lookup(aGraph.method()), options, debug, trackNodeSourcePosition);
845+
boolean trackNodeSourcePosition = GraalOptions.TrackNodeSourcePosition.getValue(compileOptions);
846+
StructuredGraph graph = aGraph.copy(universe.lookup(aGraph.method()), compileOptions, debug, trackNodeSourcePosition);
845847

846848
transplantEscapeAnalysisState(graph);
847849

@@ -1077,7 +1079,7 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi
10771079
Bytecode code = new ResolvedJavaMethodBytecode(method);
10781080
// DebugContext debug = new DebugContext(options,
10791081
// providers.getSnippetReflection());
1080-
graph = new SubstrateIntrinsicGraphBuilder(getCustomizedOptions(debug), debug, providers,
1082+
graph = new SubstrateIntrinsicGraphBuilder(compileOptions, debug, providers,
10811083
code).buildGraph(plugin);
10821084
}
10831085
}
@@ -1087,7 +1089,10 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi
10871089
}
10881090
if (graph == null) {
10891091
needParsing = true;
1090-
graph = new StructuredGraph.Builder(getCustomizedOptions(debug), debug).method(method).build();
1092+
graph = new StructuredGraph.Builder(compileOptions, debug)
1093+
.method(method)
1094+
.recordInlinedMethods(false)
1095+
.build();
10911096
}
10921097
}
10931098
try (DebugContext.Scope s = debug.scope("Parsing", graph, method, this)) {
@@ -1107,6 +1112,7 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi
11071112
afterParseSuite.apply(method.compilationInfo.graph, new HighTierContext(providers, afterParseSuite, getOptimisticOpts()));
11081113
assert GraphOrder.assertSchedulableGraph(method.compilationInfo.getGraph());
11091114

1115+
graph.minimizeSize();
11101116
method.compilationInfo.numNodesAfterParsing = graph.getNodeCount();
11111117
if (!parseOnce) {
11121118
UninterruptibleAnnotationChecker.checkAfterParsing(method, graph);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/IntrinsifyMethodHandlesInvocationPlugin.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,10 @@ private boolean processInvokeWithMethodHandle(GraphBuilderContext b, Replacement
535535
GraphBuilderPhase.Instance graphBuilder = new GraphBuilderPhase.Instance(parsingProviders, graphBuilderConfig, OptimisticOptimizations.NONE, null);
536536

537537
DebugContext debug = b.getDebug();
538-
StructuredGraph graph = new StructuredGraph.Builder(b.getOptions(), debug).method(NativeImageUtil.toOriginal(methodHandleMethod)).build();
538+
StructuredGraph graph = new StructuredGraph.Builder(b.getOptions(), debug)
539+
.method(NativeImageUtil.toOriginal(methodHandleMethod))
540+
.recordInlinedMethods(false)
541+
.build();
539542
try (DebugContext.Scope s = debug.scope("IntrinsifyMethodHandles", graph)) {
540543
graphBuilder.apply(graph);
541544
/*

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,10 @@ private boolean isAliased(ResolvedJavaType type) {
10881088
private StructuredGraph getStaticInitializerGraph(ResolvedJavaMethod clinit, OptionValues options, DebugContext debug) {
10891089
assert clinit.hasBytecodes();
10901090

1091-
StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(clinit).build();
1091+
StructuredGraph graph = new StructuredGraph.Builder(options, debug)
1092+
.method(clinit)
1093+
.recordInlinedMethods(false)
1094+
.build();
10921095
HighTierContext context = new HighTierContext(GraalAccess.getOriginalProviders(), null, OptimisticOptimizations.NONE);
10931096
graph.setGuardsStage(GuardsStage.FIXED_DEOPTS);
10941097

0 commit comments

Comments
 (0)