Skip to content

concurrency: alloc an async-let task with the parent's allocator. #36993

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

Merged
merged 1 commit into from
Apr 27, 2021

Conversation

eeckstein
Copy link
Contributor

@eeckstein eeckstein commented Apr 21, 2021

Also, do this for the initial slab for the task's allocator itself.
This avoids memory allocations for async-lets.
In case the async-task's memory demand does not exceed the initial slab size, it is now completely malloc-free.

The refcount bits of an async-let task are initialized to "immortal" so that ARC operations don't have an effect on the task.

@eeckstein eeckstein changed the title [DNM] concurrency: alloc an async-let task with the parent's allocator. concurrency: alloc an async-let task with the parent's allocator. Apr 22, 2021
@eeckstein eeckstein requested review from rjmccall and ktoso and removed request for ktoso April 22, 2021 10:47
@eeckstein
Copy link
Contributor Author

@swift-ci test

class alignas(2 * alignof(void*)) Job :
// For async-let tasks, the refcount bits are initialized as "immortal"
// because such a task is allocated with the parent's stack allocator.
public HeapObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, thanks for the comment!

@@ -165,7 +164,13 @@ static void swift_asyncLet_endImpl(AsyncLet *alet) {
// TODO: we need to implicitly await either before the end or here somehow.

// and finally, release the task and free the async-let
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// and finally, release the task and free the async-let
// and finally deallocate the async-let

no releasing anymore (yay!)

static void destroyTask(SWIFT_CONTEXT HeapObject *obj) {
auto task = static_cast<AsyncTask*>(obj);

task->~AsyncTask();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no idea if doing a destructor is nicer... am I missing something?
so far we've mostly had all destroyX things explicitly (but I'm by no means C++ or this codebase guru...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a matter of taste

static AsyncTaskAndContext swift_task_create_group_future_commonImpl(
JobFlags flags, TaskGroup *group,
const Metadata *futureResultType,
FutureAsyncSignature::FunctionType *function,
void *closureContext, bool owningClosureContext,
void *closureContext, bool isAsyncLetTask,
size_t initialContextSize) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ABI wise my impression was we wanted something like [TaskCreateRecords] to be passed in and e.g. check their .kind == .isAsyncLet or similar to be future proof about adding more smarts to creating tasks.

But maybe we want to do that in a separate PR? (totally find by me to do later on!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, let's do that in a separate PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okey 👍

&completeTask);
assert(parent);
void *initialSlab = (char*)allocation + amountToAllocate;
_swift_task_alloc_initialize_with_slab(task, initialSlab, initialSlabSize);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool! so even a bunch of tiny task-local allocs inside the child have a chance to stay in the initial slab... <3

Copy link
Contributor

@ktoso ktoso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read through in detail and looks fantastic to me :)

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - f61e959d72e1b843efcd049ad10aa78c4829ac63

@eeckstein
Copy link
Contributor Author

This PR needs #37010 to land first

…ator.

Also, do this for the initial slab for the task's allocator itself.
This avoids memory allocations for async-lets.
In case the async-task's memory demand does not exceed the initial slab size, it is now completely malloc-free.

The refcount bits of an async-let task are initialized to "immortal" so that ARC operations don't have an effect on the task.
@eeckstein
Copy link
Contributor Author

@swift-ci test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 075ad87

@eeckstein
Copy link
Contributor Author

@swift-ci smoke test macOS

1 similar comment
@eeckstein
Copy link
Contributor Author

@swift-ci smoke test macOS

@eeckstein eeckstein merged commit 056024a into swiftlang:main Apr 27, 2021
@eeckstein eeckstein deleted the task-allocation branch April 27, 2021 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants