Repository navigation
Defer string formatting in asyncio Task creation #103793
Description
Activity
- addedtype-featureA feature request or enhancementA feature request or enhancement
on Apr 24, 2023 I am surprised that string formatting is so slow to make a difference here.
If we are implementing this optimization then I suggest to use a tagged pointer and a union for task name and counter combined into a single word with LSB set for tagging.
This is the way it was originally implemented in Cinder :)
so far we have 3 different implementation approaches that provide similar perf benefits:
- assign task number lazily in
get_name.- Pros: no changes to the struct. very simple impl.
- Cons: changes the behavior of task numbers assignment
- use tagged pointer to make the task name field be a union of task name and counter.
- Pros: doesn't change behavior of task numbers assignment. doesn't add fields to the task struct.
- Cons: most complex impl.
- store the task number in a new field on the task struct to be used for deferred formatting in
get_name.- Pros: doesn't change behavior of task numbers assignment. relatively simple impl.
- Cons: adds a field to the task struct.
@kumaraditya303 iiuc you prefer option 2, for the benefit of not changing the order of task number allocation.
do you think it is worth the complexity, considering task numbers are mainly used for debugging and logging, and there are no guarantees provided by python about the semantics of the task name if not set by the user?- assign task number lazily in
Option 2 seems needlessly complicated for something as relatively unimportant as task names.
Is there an expectation that the numbers in task names increase over time?
If not, then option 1 is the best in terms of both performance and memory useOption 4 is to assign the number at task creation and compute the name whenever it is requested.
This costs no additional memory, as it replaces the name field with an index field.Reacted by Itamar Oren and Carol WillingOption 4 is to assign the number at task creation and compute the name whenever it is requested.
This costs no additional memory, as it replaces the name field with an index field.How does that work for user provided task names?
I would love to go with option 1 :)
I think option 4 can ~work if we do something like this:
- in construction - if user-provided string, do what we do today. if no user-provided - assign the number to the name field as a PyLong object
- in
get_name- if the name field is a PyLong, then use it to format the name. otherwise do what we do today.
if we go with this, we can go ahead and cache the formatted name back to the name field, which makes it similar to option 2 but using type checks instead of tagged pointers.
I like @markshannon's option 4 with the your suggestions @itamaro. I think Option 1's Con makes it less attractive to me.
Reacted by Itamar OrenLooking again, I now agree that the order in which task numbers are assigned may feel meaningful to the user, and only assigning the number when the task is being printed could cause some problems. E.g. the user has several tasks, and no matter in which order they print them, the first one printed is always named "Task-1" -- this could be very confusing. So this is another vote for 3 or 4. (I would be fine with 3. From the words Mark used when he proposed 4, I assume he missed that sometimes the user passes in a name which should override this mechanism.)
Reacted by Itamar OrenThanks for the feedback!
If my interpretation of option 4 is acceptable, I will go with that! Rather not add fields to the struct if not strictly necessary.- added a commit that references this issue
on Apr 29, 2023 Thanks for this improvement! (Would still like to see the perf numbers now that we allocate a PyLong during init.)
- added a commit that references this issue
on May 1, 2023
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Feature or enhancement
When an asyncio Task is created without passing in a
name(a common case), the init method uses a global counter to generate a task name of the form"Task-<counter>".It is very common in applications that the task name is never read or used, and string formatting has non-negligible runtime cost, making task creation slower. It would be beneficial to defer the string formatting operation and avoid incurring that overhead during task creation.
This can be done by storing the counter in the task struct, and using it to lazily populate the name in the
get_namemethod.Linked PRs