-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[concurrency] Implement the Task allocator as bump-pointer allocator. #34880
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
Conversation
|
Cool! I was just implementing an allocator in the Swift runtime for use with automatic differentiation — I used |
|
@rxwei There are several differences, most importantly that the task allocator also can free memory (in a stack discipline). |
edfb23b to
5eb6027
Compare
These features would be needed for AutoDiff as well. I’m interested in using this to replace the allocator in #34886. Do you think it can be moved to a header file that we can import? |
5eb6027 to
676cd8b
Compare
|
@swift-ci benchmark |
Performance: -O
Code size: -OPerformance: -Osize
Code size: -OsizePerformance: -OnoneCode size: -swiftlibsHow to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview |
676cd8b to
367cc64
Compare
|
@swift-ci smoke test |
|
@rxwei makes sense. I moved it to a separate file. |
include/swift/ABI/Task.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every task, including child tasks, will have these 64 words of storage. I had thought of child tasks as being fairly lightweight abstractions, but this makes them significantly larger. Is that okay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't every task need at least one context allocation? If so, then this inline-buffer avoids an malloc call.
The size is TBD, though. 64 is just an initial random number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"its"
I think this initial slab buffer should not be in the main AsyncTask structure, which is ABI. If you want to have swift_task_create preallocate some space and then hand it off to the allocator during initialization, that seems fine, but you should figure out a runtime-private way of doing it. That does mean it won't be tail to the allocator, but I think you should be able to deal with that.
|
@swift-ci smoke test linux |
include/swift/ABI/Task.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"its"
I think this initial slab buffer should not be in the main AsyncTask structure, which is ABI. If you want to have swift_task_create preallocate some space and then hand it off to the allocator during initialization, that seems fine, but you should figure out a runtime-private way of doing it. That does mean it won't be tail to the allocator, but I think you should be able to deal with that.
unittests/runtime/TaskAlloc.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TaskAlloc.cpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slabs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slabs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, the linked list of slabs starts at the pre-allocated slab, and then next points to the slab allocated after the last, right? So starting at lastAllocation->slab means you actually don't check to see if there's any space in any of the older slabs. Is that intentional? It's certainly possible that you have plenty of space for small allocations in older slabs but you needed a big new slab for the last one. But maybe not looking in them is the right trade-off.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rjmccall Yes, this is intention. As you said, it's a trade-off.
367cc64 to
8220224
Compare
|
I pushed a new version, which now does not use a pre-allocated first slab - to keep the task allocation small. |
|
@swift-ci test |
|
Build failed |
|
Build failed |
8220224 to
635a89c
Compare
|
@swift-ci test |
|
Build failed |
|
Build failed |
A StackAllocator performs fast allocation and deallocation of memory by implementing a bump-pointer allocation strategy. In contrast to a pure bump-pointer allocator, it's possible to free memory. Allocations and deallocations must follow a strict stack discipline. In general, slabs which become unused are _not_ freed, but reused for subsequent allocations. The first slab can be placed into pre-allocated memory.
Use the StackAllocator as task allocator. TODO: we could pass an initial pre-allocated first slab to the allocator, which is allocated on the stack or with the parent task's allocator. rdar://problem/71157018
635a89c to
ec1490d
Compare
|
@swift-ci test |
rjmccall
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
The task allocator is implemented by using the new StackAllocator utility.
A StackAllocator performs fast allocation and deallocation of memory by implementing a bump-pointer allocation strategy. In contrast to a pure bump-pointer allocator, it's possible to free memory.
Allocations and deallocations must follow a strict stack discipline.
In general, slabs which become unused are not freed, but reused for subsequent allocations. The first slab is tail allocated to the StackAllocator.
TODO: we could pass an initial pre-allocated first slab to the allocator, which is allocated on the stack or with the parent task's allocator.
rdar://problem/71157018