Replies: 16 comments
-
Summarize what you did.
Explain how you know your implementation works—how did you test it? Which test inputs did you use? Do you have any quantitative results to report?
What was the hardest part of the task? How did you solve this problem?
|
Beta Was this translation helpful? Give feedback.
-
Summarytest directory that contains the brench results and some handwritten tests. Implementation & TestingFor this assignment, I implemented a simple reference counter garbage collector for the brili typescript interpreter. I did this by adding a field to each memory object that indicates its reference count. It was fairly straightforward to do this following our discussion in class, with some trickiness in catching all the cases of incrementing and decrementing (I elaborate in the challenges section). To test, I had some simple hand written cases that I made sure worked. The program will print at the end of execution if there is any memory that is still allocated on the heap. I then used brench and compared the following two configurations to check for equality:
After getting all of the above to work, I am pretty confident in my solution. ChallengesThere were lots of tricky cases to consider, some of which I missed which lead to spurious errors. For example, you have to increment the ref count of arguments passed to functions, increment ref count of pointers that are returned from a function before they’re cleaned up by cleanup pass that happens at the end of functions, recursively decrement the refcount if a pointer points to a pointer and is freed, etc. There is also the case where you set a variable to the same pointer (such as This assignment certainly gave me an appreciation for how careful you must be with memory management and how easy it is to get it wrong. |
Beta Was this translation helpful? Give feedback.
-
SummaryI, @evanmwilliams, @emwangs, worked together on this assignment. We chose to use Implementation & TestingWe implemented a simple reference counter by adding an extra map to the We added a brench.toml: extract = 'total_dyn_inst: (\d+)'
benchmarks = 'benchmarks/mem/*.bril'
[runs.baseline]
pipeline = [
"bril2json",
"brili -p {args}",
]
[runs.gc]
pipeline = [
"bril2json",
"brilirs --gc -p {args}",
] Results
ChallengesThe biggest challenge we faced was understanding the brilirs codebase. We had to read through the codebase to understand how the brilirs compiler worked and how we could implement our reference counter. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Will and I worked on lesson 11 together. Summary
Implementation details
What was the hardest part of the task? How did you solve this problem?
|
Beta Was this translation helpful? Give feedback.
-
SummaryDetails/TestingI modified the To test that the implementation doesn't leak memory, I wrote a utility to remove all DifficultiesI didn't encounter any major difficulties for this task, other than simply reading the reference interpreter carefully to find all the places where reference counting operations needed to be inserted. |
Beta Was this translation helpful? Give feedback.
-
Summary
Implementation Details
TestingFor testing, we used turnt to test all of the bril benchmarks to make sure that adding our garbage collector didn’t change the behavior of any of the bril programs. As our baseline we ran turnt on all of the bril benchmarks without any garbage collection behavior (keeping the free instruction). We then ran turnt with our reference counting logic (with free as a no-op), and saw that brili printed out the same results for each bril program. To further test our implementation, we also made sure that after the program finishes, we print the heap to verify that all the garbage that should be collected has been collected and that there are no memory leaks. Anything hard or difficult?Considering all of the cases when decrementRefCount and incrementRefCount was a bit challenging. One obstacle we ran into was correctly implementing the base case in the recursive calls to decrementRefCount. Overall, this was a fun project and it’s cool to see how we can implement a relatively simple algorithm which converts language from being a manual memory-management language to a garbage-collected language. |
Beta Was this translation helpful? Give feedback.
-
Summary
DetailsI implemented a reference counting based garbage collector for Bril. I modified the existing Typescript bril interpreter to get a new I create a CountedX class which is essentially a generic wrapper that contains a value and a counter. The counter is used to keep track of the number of references to the value. When the counter reaches 0, the value is freed. The CountedX class is used to wrap the values in the interpreter state. The interpreter state is a map of variable names to values. The interpreter state is modified to use CountedX instead of the raw values. I went through each line of the interpreter and injected the reference counting logic. Testing/EvaluationTo test, I added a --gc flag that can be used to enable/disable the garbage collector. I also added a --sf flag that, when toggled, will skip over all the I used brench to compare the baseline
extract = 'remaining_heap_size: (\d+)'
benchmarks = '../../benchmarks/mem/*.bril'
[runs.baseline]
pipeline = [
"bril2json",
"brili -p {args}",
]
[runs.skipfree]
pipeline = [
"bril2json",
"brili-gc --sf -p {args}",
]
[runs.skipfree_gc]
pipeline = [
"bril2json",
"brili-gc --sf --gc -p {args}",
]
DifficultiesI was stuck for a while because I would double increment the reference of the pointer when I intially alloced it, this lead to the GC doing nothing and I was confused as I tripled checked my logic (though in this process I fixed other issues). Eventually when I was about to scratch my CounterX class and just use a seperate map I found the issue. I intially tested on a handcrafted small example that would allocate a ptr and immediately free it (though this free instruction would be ignored when running with the --sf flag). After handling all the instr level logic with points, I ran the brench tests and found that I had a lot of remaining counts in the heap. I then realized that I was not handling the passing of ptrs across function calls (increment when passed in, and decrement when the stack is popped). After fixing this, I was able to get the remaining heap size down to 0 for all the benchmarks. |
Beta Was this translation helpful? Give feedback.
-
@xalbt and I worked together on lesson 11. SummaryImplementation
Our
Difficulties
TestingResults:
|
Beta Was this translation helpful? Give feedback.
-
Summary
Implementation
Testing
ChallengesThe most challenging part is to account for all possible cases that may change the reference counts. Especially, it could be hard to realize that the pointers produced by |
Beta Was this translation helpful? Give feedback.
-
I have implemented a simple reference counting garbage collector for Bril. It works correctly for all of the benchmarks in the canonical The implementation was fairly straightforward once I found my way around |
Beta Was this translation helpful? Give feedback.
-
SummaryI implemented a tracing, generational garbage collector. The nursery generation used a bump allocator and the mature generation used a hash map mapping "pointers" to Rust-allocated memory buffers. One slight modification from traditional generational garbage collection is that each generation can be collected independently. Thus, having multiple, smaller generations would bring the behavior closer to an incremental collector on the granularity of generations. The current implementation just uses two, however it is as simple as changing a constant and adding a new object into an array to change the number and size of the generations. ImplementationI modified the For the remembered set, I took some ideas from card tables and the unified GC paper. I had each cross-generation pointer map surjectively to a reference count. This mapping is done by simply taking the remainder of the pointer divided by the number of reference counts being kept track of. Any pointer that maps to a nonzero reference count would be considered a root. Therefore, this implementation uses tracing within a generation but reference counting for cross-generation pointers. This also means that cross-generation cycles cannot be collected, but this isn't too big of a problem because in this case, they'd all eventually end up in the final generation and be cleaned up there. TestingFor testing, I made sure that all benchmarks with the GC had the correct behavior. Moreover, I introduced memory statistics which keeps track of things like number of promotions, number of live values in the generation, remembered set size, allocated memory in a generation etc. I created a few small tests to ensure that things like allocated values and remembered sets were being updated correctly. ChallengesOne difficulty was correctly handling the promotion of allocations and the case when we want to allocate memory to a generation but there isn't enough space. In this case, we need to first run a collection, promoting any values into allocations of an older generation which may trigger more collections. Then, we must retry the allocation. If this fails again, then we have to pass the allocation to an older generation, hoping that they have more space. If we keep passing along an allocation until there is no more generations, we must then return an error. Overall, this logic was a bit involved. Another challenge was implementing the write barrier to correctly update the reference counts for cross-generation references. Another key thing not to forget is to update the roots and other generations for the new pointer location when a promotion occurs. |
Beta Was this translation helpful? Give feedback.
-
Summary
Implementation
Testing
Difficulties
|
Beta Was this translation helpful? Give feedback.
-
SummaryImplementation of Garbage Collection using reference pointer gc-brili.ts We choose reference counting for bril typescript interpreter because it most straightforward. For Bril there are few cases to consider as what type of instructions would constitute as necessary to update the reference counter. After discussion we think these are the cases
Another concept we spent some time to figure out is what happens when sth goes out of scope, e.g. exit from function call, and how to deal with those variables. We used a pretty brute-force approach by keeping a pointer_stack that keeps track of the pointers before entering and after finishing the function call. Then we go through the stack and decrease the reference count of pointers. An edge case we encountered here is that for functions that return pointers, the pointer value should still remain and its reference count shouldn't be decreased to zero and removed. To do this the sequence of updating the reference count matters (we need to first increment the RC of the returned ptr and then iterate through the stack and decrement the rc of ptrs on the stack) Testwe mainly take advantage of the existing benchmark bril test, and also added a few small test cases for the corner cases and debug purposes. Main tools we use are 'turnt' to compare the experimental outcomes with the manual free brili.
Hardest Partwe encountered some problem with typescript first since it has some weird behavior when trying to access a map using the bracket '[]' operator, causing the values we read become undefined. In addition, it is quite hard to figure out the corner cases. For instance, while developing we also find out that the main function call procedure is different from normal function calls, and should be dealt quite differently. |
Beta Was this translation helpful? Give feedback.
-
SummaryModify brili.ts to support reference counting for garbage collection Implementation detailsI added several functions to the TestingI used brench to test my implementation using benchmarks. And I used profiling to print the heap size after program executing to compare between the original brili and my brili-gc. My brili-gc will free all objects on the heap that were otherwise not freed by the original brili. ChallengeLearning TypeScript is one of the difficulties I had when I started the task, but later I got the hang of it. The task per se is straightforward but requires careful thought on incrementing/decrementing the pointer count for every possible cases. In addition, function calls and |
Beta Was this translation helpful? Give feedback.
-
I worked with @SanjitBasker on this project. SummaryImplementation detailsWe modified brili.ts to support garbage collection. To do so, we implemented a version of reference counting in which memory blocks are placed in a queue to be freed at a later time once their reference count hits zero. Then, whenever a new object is allocated after a given object is freed, the garbage collector will go through the queue of blocks and free up the blocks until enough space is freed to support the new memory block before allocating the memory block. (Admittedly, from the way the brili interpreter allocates memory, this is a bit superfluous, but we wanted to simulate how a real garbage collector would work when interacting with memory directly.) TestingTo test, we took all the benchmarks that use memory, ignored all the free instructions, and ran our new interpreter to ensure that no blocks were left unfreed by the end of the program. To do so, we used brench to ensure that the two programs produced equivalent outputs for all the valid test cases and they did not error for a lack of removing all the objects on the heap. ChallengesA particular challenge we faced was how to deal with functions that return a pointer; our initial approach was to decrement the count of all the local pointers when returning from a function, but it turns out that we did not want to do this exactly for pointers that were returned. To solve this, we still decremented the reference count of pointers that were returned, but we did not schedule it to be freed if the reference count reached zero (since it would escape the function). This proved to be sufficient for the benchmarks given. |
Beta Was this translation helpful? Give feedback.
-
These tasks are about implementing a garbage collector!
Beta Was this translation helpful? Give feedback.
All reactions