Skip to content

Commit 7e0c7c4

Browse files
committed
Try harder to free native memory.
1 parent 7d5fb53 commit 7e0c7c4

File tree

3 files changed

+35
-73
lines changed

3 files changed

+35
-73
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PythonCextBuiltins.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,13 +3250,13 @@ abstract static class PyTruffleTraceMallocTrack extends PythonBuiltinNode {
32503250

32513251
@Specialization(guards = {"domain == cachedDomain"}, limit = "3")
32523252
int doCachedDomainIdx(VirtualFrame frame, @SuppressWarnings("unused") long domain, Object pointerObject, long size,
3253+
@CachedContext(PythonLanguage.class) PythonContext context,
32533254
@Cached("domain") @SuppressWarnings("unused") long cachedDomain,
3254-
@Cached("lookupDomain(domain)") int cachedDomainIdx,
3255-
@Cached BranchProfile profile) {
3255+
@Cached("lookupDomain(domain)") int cachedDomainIdx) {
32563256

32573257
CApiContext cApiContext = getContext().getCApiContext();
32583258
cApiContext.getTraceMallocDomain(cachedDomainIdx).track(pointerObject, size);
3259-
cApiContext.increaseMemoryPressure(frame, size, profile);
3259+
cApiContext.increaseMemoryPressure(frame, context, this, size);
32603260
if (LOGGER.isLoggable(Level.FINE)) {
32613261
LOGGER.fine(() -> String.format("Tracking memory (size: %d): %s", size, CApiContext.asHex(pointerObject)));
32623262
}
@@ -3265,8 +3265,8 @@ int doCachedDomainIdx(VirtualFrame frame, @SuppressWarnings("unused") long domai
32653265

32663266
@Specialization(replaces = "doCachedDomainIdx")
32673267
int doGeneric(VirtualFrame frame, int domain, Object pointerObject, long size,
3268-
@Cached BranchProfile profile) {
3269-
return doCachedDomainIdx(frame, domain, pointerObject, size, domain, lookupDomain(domain), profile);
3268+
@CachedContext(PythonLanguage.class) PythonContext context) {
3269+
return doCachedDomainIdx(frame, domain, pointerObject, size, context, domain, lookupDomain(domain));
32703270
}
32713271

32723272
int lookupDomain(long domain) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java

Lines changed: 29 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@
7070
import com.oracle.graal.python.builtins.objects.frame.PFrame;
7171
import com.oracle.graal.python.builtins.objects.function.PArguments;
7272
import com.oracle.graal.python.builtins.objects.function.Signature;
73+
import com.oracle.graal.python.nodes.IndirectCallNode;
7374
import com.oracle.graal.python.nodes.PRootNode;
7475
import com.oracle.graal.python.nodes.call.GenericInvokeNode;
7576
import com.oracle.graal.python.runtime.AsyncHandler;
7677
import com.oracle.graal.python.runtime.ExecutionContext.CalleeContext;
78+
import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext;
7779
import com.oracle.graal.python.runtime.PythonContext;
7880
import com.oracle.graal.python.runtime.PythonOptions;
7981
import com.oracle.truffle.api.CompilerAsserts;
@@ -95,6 +97,8 @@ public final class CApiContext extends CExtContext {
9597

9698
public static final long REFERENCE_COUNT_BITS = Integer.SIZE;
9799
public static final long REFERENCE_COUNT_MARKER = (1L << REFERENCE_COUNT_BITS);
100+
/* a random number between 1 and 20 */
101+
private static final int MAX_COLLECTION_RETRIES = 17;
98102

99103
/** Total amount of allocated native memory (in bytes). */
100104
private long allocatedMemory = 0;
@@ -108,7 +112,6 @@ public final class CApiContext extends CExtContext {
108112
private Map<Object, AllocInfo> freedNativeMemory;
109113

110114
@CompilationFinal private RootCallTarget referenceCleanerCallTarget;
111-
@CompilationFinal private RootCallTarget triggerAsyncActionsCallTarget;
112115

113116
/**
114117
* This cache is used to cache native wrappers for frequently used primitives. This is strictly
@@ -208,14 +211,6 @@ private RootCallTarget getReferenceCleanerCallTarget() {
208211
return referenceCleanerCallTarget;
209212
}
210213

211-
RootCallTarget getTriggerAsyncActionsCallTarget() {
212-
if (triggerAsyncActionsCallTarget == null) {
213-
CompilerDirectives.transferToInterpreterAndInvalidate();
214-
triggerAsyncActionsCallTarget = Truffle.getRuntime().createCallTarget(new TriggerAsyncActionsRootNode(getContext()));
215-
}
216-
return triggerAsyncActionsCallTarget;
217-
}
218-
219214
public TraceMallocDomain getTraceMallocDomain(int domainIdx) {
220215
return traceMallocDomains[domainIdx];
221216
}
@@ -419,43 +414,6 @@ public void execute(PythonContext context) {
419414
}
420415
}
421416

422-
private static final class TriggerAsyncActionsRootNode extends PRootNode {
423-
private static final Signature SIGNATURE = new Signature(-1, false, -1, false, new String[0], new String[0]);
424-
425-
@Child private CalleeContext calleeContext = CalleeContext.create();
426-
427-
private final ConditionProfile customLocalsProfile = ConditionProfile.createBinaryProfile();
428-
private final BranchProfile asyncProfile = BranchProfile.create();
429-
private final PythonContext context;
430-
431-
protected TriggerAsyncActionsRootNode(PythonContext context) {
432-
super(context.getLanguage());
433-
this.context = context;
434-
}
435-
436-
@Override
437-
public Object execute(VirtualFrame frame) {
438-
CalleeContext.enter(frame, customLocalsProfile);
439-
try {
440-
doGc();
441-
context.triggerAsyncActions(frame, asyncProfile);
442-
} finally {
443-
calleeContext.exit(frame, this);
444-
}
445-
return 0;
446-
}
447-
448-
@Override
449-
public Signature getSignature() {
450-
return SIGNATURE;
451-
}
452-
453-
@Override
454-
public boolean isPythonInternal() {
455-
return true;
456-
}
457-
}
458-
459417
public NativeObjectReference lookupNativeObjectReference(int idx) {
460418
return nativeObjectWrapperList.get(idx);
461419
}
@@ -615,47 +573,53 @@ public boolean isAllocated(Object ptr) {
615573
return true;
616574
}
617575

618-
public void increaseMemoryPressure(GenericInvokeNode invokeNode, long size) {
576+
public void increaseMemoryPressure(long size) {
619577
if (allocatedMemory <= getContext().getOption(PythonOptions.MaxNativeMemory)) {
620578
allocatedMemory += size;
621579
return;
622580
}
623-
624-
// Triggering the GC and the async actions is hidden behind a call target to keep this
625-
// method slim.
626-
invokeNode.execute(getTriggerAsyncActionsCallTarget(), PArguments.create());
627-
628-
if (allocatedMemory + size > getContext().getOption(PythonOptions.MaxNativeMemory)) {
629-
throw new OutOfMemoryError("native memory");
630-
}
631-
allocatedMemory += size;
581+
triggerGC(size);
632582
}
633583

634-
public void increaseMemoryPressure(VirtualFrame frame, long size, BranchProfile asyncProfile) {
635-
if (allocatedMemory <= getContext().getOption(PythonOptions.MaxNativeMemory)) {
584+
public void increaseMemoryPressure(VirtualFrame frame, PythonContext context, IndirectCallNode caller, long size) {
585+
if (allocatedMemory + size <= getContext().getOption(PythonOptions.MaxNativeMemory)) {
636586
allocatedMemory += size;
637587
return;
638588
}
639589

640-
doGc();
641-
getContext().triggerAsyncActions(frame, asyncProfile);
590+
Object savedState = IndirectCallContext.enter(frame, context, caller);
591+
try {
592+
triggerGC(size);
593+
} finally {
594+
IndirectCallContext.exit(frame, context, savedState);
595+
}
596+
}
642597

643-
if (allocatedMemory + size > getContext().getOption(PythonOptions.MaxNativeMemory)) {
644-
throw new OutOfMemoryError("native memory");
598+
@TruffleBoundary
599+
private void triggerGC(long size) {
600+
long delay = 0;
601+
for (int retries = 0; retries < MAX_COLLECTION_RETRIES; retries++) {
602+
delay += 50;
603+
doGc(delay);
604+
getContext().triggerAsyncActions(null, BranchProfile.getUncached());
605+
if (allocatedMemory + size <= getContext().getOption(PythonOptions.MaxNativeMemory)) {
606+
allocatedMemory += size;
607+
return;
608+
}
645609
}
646-
allocatedMemory += size;
610+
throw new OutOfMemoryError("native memory");
647611
}
648612

649613
public void reduceMemoryPressure(long size) {
650614
allocatedMemory -= size;
651615
}
652616

653617
@TruffleBoundary
654-
private static void doGc() {
618+
private static void doGc(long millis) {
655619
LOGGER.fine("full GC due to native memory");
656620
System.gc();
657621
try {
658-
Thread.sleep(50);
622+
Thread.sleep(millis);
659623
} catch (InterruptedException x) {
660624
// Restore interrupt status
661625
Thread.currentThread().interrupt();

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyTruffleObjectAlloc.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444

4545
import com.oracle.graal.python.PythonLanguage;
4646
import com.oracle.graal.python.builtins.objects.frame.PFrame;
47-
import com.oracle.graal.python.nodes.call.GenericInvokeNode;
4847
import com.oracle.graal.python.nodes.frame.GetCurrentFrameRef;
4948
import com.oracle.graal.python.nodes.util.CannotCastException;
5049
import com.oracle.graal.python.nodes.util.CastToJavaLongLossyNode;
@@ -77,7 +76,6 @@ Object execute(Object[] arguments,
7776
@Cached CastToJavaLongLossyNode castToJavaLongNode,
7877
@Cached GetCurrentFrameRef getCurrentFrameRef,
7978
@CachedContext(PythonLanguage.class) ContextReference<PythonContext> contextRef,
80-
@Cached GenericInvokeNode invokeNode,
8179
@CachedLibrary(limit = "3") InteropLibrary lib,
8280
@Cached(value = "getAllocationReporter(contextRef)", allowUncached = true) AllocationReporter reporter) throws ArityException {
8381
if (arguments.length != 2) {
@@ -98,7 +96,7 @@ Object execute(Object[] arguments,
9896
// memory management
9997
PythonContext context = contextRef.get();
10098
CApiContext cApiContext = context.getCApiContext();
101-
cApiContext.increaseMemoryPressure(invokeNode, objectSize);
99+
cApiContext.increaseMemoryPressure(objectSize);
102100

103101
boolean isLoggable = LOGGER.isLoggable(Level.FINER);
104102
boolean traceNativeMemory = context.getOption(PythonOptions.TraceNativeMemory);

0 commit comments

Comments
 (0)