Skip to content
This repository was archived by the owner on May 9, 2024. It is now read-only.

Commit b154e86

Browse files
committed
WIP sketch of pooling in ExecutorMemoryManager
1 parent 2f22424 commit b154e86

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

unsafe/src/main/java/org/apache/spark/unsafe/memory/ExecutorMemoryManager.java

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717

1818
package org.apache.spark.unsafe.memory;
1919

20+
import java.lang.ref.SoftReference;
21+
import java.util.HashMap;
22+
import java.util.LinkedList;
23+
import java.util.Map;
24+
import javax.annotation.concurrent.GuardedBy;
25+
2026
/**
2127
* Manages memory for an executor. Individual operators / tasks allocate memory through
2228
* {@link TaskMemoryManager} objects, which obtain their memory from ExecutorMemoryManager.
@@ -33,6 +39,12 @@ public class ExecutorMemoryManager {
3339
*/
3440
final boolean inHeap;
3541

42+
@GuardedBy("this")
43+
private final Map<Long, LinkedList<SoftReference<MemoryBlock>>> bufferPoolsBySize =
44+
new HashMap<Long, LinkedList<SoftReference<MemoryBlock>>>();
45+
46+
private static final int POOLING_THRESHOLD_BYTES = 1024 * 1024;
47+
3648
/**
3749
* Construct a new ExecutorMemoryManager.
3850
*
@@ -43,16 +55,57 @@ public ExecutorMemoryManager(MemoryAllocator allocator) {
4355
this.allocator = allocator;
4456
}
4557

58+
/**
59+
* Returns true if allocations of the given size should go through the pooling mechanism and
60+
* false otherwise.
61+
*/
62+
private boolean shouldPool(long size) {
63+
// Very small allocations are less likely to benefit from pooling.
64+
// At some point, we should explore supporting pooling for off-heap memory, but for now we'll
65+
// ignore that case in the interest of simplicity.
66+
return size >= POOLING_THRESHOLD_BYTES && allocator instanceof HeapMemoryAllocator;
67+
}
68+
4669
/**
4770
* Allocates a contiguous block of memory. Note that the allocated memory is not guaranteed
4871
* to be zeroed out (call `zero()` on the result if this is necessary).
4972
*/
5073
MemoryBlock allocate(long size) throws OutOfMemoryError {
51-
return allocator.allocate(size);
74+
if (shouldPool(size)) {
75+
synchronized (this) {
76+
final LinkedList<SoftReference<MemoryBlock>> pool = bufferPoolsBySize.get(size);
77+
if (pool != null) {
78+
while (!pool.isEmpty()) {
79+
final SoftReference<MemoryBlock> blockReference = pool.pop();
80+
final MemoryBlock memory = blockReference.get();
81+
if (memory != null) {
82+
assert (memory.size() == size);
83+
return memory;
84+
}
85+
}
86+
bufferPoolsBySize.remove(size);
87+
}
88+
}
89+
return allocator.allocate(size);
90+
} else {
91+
return allocator.allocate(size);
92+
}
5293
}
5394

5495
void free(MemoryBlock memory) {
55-
allocator.free(memory);
96+
final long size = memory.size();
97+
if (shouldPool(size)) {
98+
synchronized (this) {
99+
LinkedList<SoftReference<MemoryBlock>> pool = bufferPoolsBySize.get(size);
100+
if (pool == null) {
101+
pool = new LinkedList<SoftReference<MemoryBlock>>();
102+
bufferPoolsBySize.put(size, pool);
103+
}
104+
pool.add(new SoftReference<MemoryBlock>(memory));
105+
}
106+
} else {
107+
allocator.free(memory);
108+
}
56109
}
57110

58111
}

0 commit comments

Comments
 (0)