Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[llvm] [sparse] Fix a memory leak bug in GC's free list on CPU #1079

Merged
merged 5 commits into from
May 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 60 additions & 3 deletions taichi/runtime/llvm/internal_function.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
// Note that TI_ASSERT provided by runtime doesn't check fail immediately. As
// a hack, we just check the last error code whenever we call TI_ASSERT.
#define TI_TEST_CHECK(cond, r) \
do { \
TI_ASSERT(cond); \
if ((r)->error_code) { \
taichi_printf((r), "%s", (r)->error_message_buffer); \
abort(); \
} \
} while (0)

i32 do_nothing(Context *context) {
return 0;
}
Expand Down Expand Up @@ -28,7 +39,7 @@ i32 test_list_manager(Context *context) {
list->append(&j);
}
for (int i = 0; i < 320; i++) {
TI_ASSERT(list->get<i32>(i) == i + 5);
TI_TEST_CHECK(list->get<i32>(i) == i + 5, runtime);
}
return 0;
}
Expand All @@ -54,14 +65,60 @@ i32 test_node_allocator(Context *context) {
ptrs[i] = nodes->allocate();
}
for (int i = 5; i < 19; i++) {
TI_ASSERT(nodes->locate(ptrs[i]) == i);
TI_TEST_CHECK(nodes->locate(ptrs[i]) == i, runtime);
}

for (int i = 19; i < 24; i++) {
auto idx = nodes->locate(ptrs[i]);
taichi_printf(runtime, "i %d", i);
taichi_printf(runtime, "idx %d", idx);
TI_ASSERT(idx == i - 19);
TI_TEST_CHECK(idx == i - 19, runtime);
}
return 0;
}

i32 test_node_allocator_gc_cpu(Context *context) {
auto runtime = context->runtime;
taichi_printf(runtime, "LLVMRuntime %p\n", runtime);
auto nodes = context->runtime->create<NodeManager>(runtime, sizeof(i64), 4);
constexpr int kN = 24;
constexpr int kHalfN = kN / 2;
Ptr ptrs[kN];
// Initially |free_list| is empty
TI_TEST_CHECK(nodes->free_list->size() == 0, runtime);
for (int i = 0; i < kN; i++) {
taichi_printf(runtime, "[1] allocating %d\n", i);
ptrs[i] = nodes->allocate();
taichi_printf(runtime, "[1] ptr %p\n", ptrs[i]);
}
for (int i = 0; i < kN; i++) {
taichi_printf(runtime, "[1] deallocating %d\n", i);
taichi_printf(runtime, "[1] ptr %p\n", ptrs[i]);
nodes->recycle(ptrs[i]);
}
TI_TEST_CHECK(nodes->free_list->size() == 0, runtime);
nodes->gc_serial();
// After the first round GC, |free_list| should have |kN| items.
TI_TEST_CHECK(nodes->free_list->size() == kN, runtime);

// In the second round, all items should come from |free_list|.
for (int i = 0; i < kHalfN; i++) {
taichi_printf(runtime, "[2] allocating %d\n", i);
ptrs[i] = nodes->allocate();
taichi_printf(runtime, "[2] ptr %p\n", ptrs[i]);
}
TI_TEST_CHECK(nodes->free_list_used == kHalfN, runtime);
for (int i = 0; i < kHalfN; i++) {
taichi_printf(runtime, "[2] deallocating %d\n", i);
taichi_printf(runtime, "[2] ptr %p\n", ptrs[i]);
nodes->recycle(ptrs[i]);
}
nodes->gc_serial();
// After GC, all items should be returned to |free_list|.
printf("free_list_size=%d\n", nodes->free_list->size());
TI_TEST_CHECK(nodes->free_list->size() == kN, runtime);

return 0;
}

#undef TI_TEST_CHECK
7 changes: 6 additions & 1 deletion taichi/runtime/llvm/runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,10 @@ struct ListManager {
num_elements = 0;
}

void resize(i32 n) {
num_elements = n;
}

Ptr get_element_ptr(i32 i) {
return chunks[i >> log2chunk_num_elements] +
element_size * (i & ((1 << log2chunk_num_elements) - 1));
Expand Down Expand Up @@ -612,8 +616,9 @@ struct NodeManager {
free_list->get<list_data_type>(i - free_list_used) =
free_list->get<list_data_type>(i);
}
const i32 num_unused = max_i32(free_list->size() - free_list_used, 0);
free_list_used = 0;
free_list->clear();
free_list->resize(num_unused);

// zero-fill recycled and push to free list
for (int i = 0; i < recycled_list->size(); i++) {
Expand Down
8 changes: 8 additions & 0 deletions tests/python/test_internal_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,11 @@ def test():

test()
test()


def test_node_manager_gc():
@ti.kernel
def test_cpu():
ti.call_internal("test_node_allocator_gc_cpu")

test_cpu()