-
Notifications
You must be signed in to change notification settings - Fork 18
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
In-graph synchronization with Epochs #86
Conversation
583104d
to
3a53747
Compare
59307c8
to
24f4c12
Compare
From live discussion with @PeterTh: We need to re-visit an earlier concern about race conditions on tasks returned form |
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.
I'm only about a third through the changes so far; I've added some minor notes. However I did a quick test and it seems like there is a ~10% performance drop when running wave_sim -N 4096 -T 200
with 4 workers on gpuc1: Current master takes 522ms on average (over 5 runs), where this PR takes 585ms on average.
36454f2
to
c19e7f9
Compare
I have collected longer-running benchmark results on the The following numbers were collected by running The outliers skew the mean, but by eliminating them (or using the median), we see that the "normal" run-time behavior has not changed noticeably. |
ORDER_DEP was handled like TRUE_DEP everywhere, and it does not add any value except for context information in printed graphs. This adds a dependency_origin enum to distinguish data flow dependencies from epoch/horizon/collective temporal dependencies.
… members in task_manager and graph_generator
…not need to specialize the first task
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.
Thanks again, I've added a few more notes.
The only thing that I still find somewhat confusing is that epochs are both an abstract concept and a concrete type of task/command; in that horizons can also act as epochs, and there are some fields that are named *epoch*
, but can also point to a horizon task/command. Can you outline to me again briefly why you decided against treating horizons as a type of epoch?
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.
Looks good to me. The only thing I found was a typo that wasn't even introduced by this PR.
Thanks to all of you for the continued endurance on this! |
So far,
queue::slow_full_sync()
and the shutdown of workerexecutor
s are implemented by sending SYNC and SHUTDOWN commands throughbroadcast_control_command()
and then busy-waiting on the main thread. Since we already introduced synchronization logic to task and command graphs before as part of horizons, we now have the opportunity to integrate SYNC and SHUTDOWN into the graphs as well and reduce specialized code paths. This will also be a stepping stone towards using explicit synchronization to exchange data in celerity buffers with the main thread.This PR represents synchronization points in the graph with epochs, which themselves are a generalization of INIT/NOP tasks and commands. Each graph node (except the first epoch) has exactly one preceding epoch, and no node can ever depend on a node before its epoch. In that sense they are similar to horizons, except that they fully serialize execution and dependency tracking:
To ensure correct temporal ordering, a new epoch node receives a true-dependency on the entire execution front (orange edges); and all nodes without other true-dependencies (pure producers, tasks
a
b
d
e
) receive a dependency on their epoch (pink edges). A new epoch will immediately become the last-writer and host-buffer initializer for all successor tasks. Like any other node, epochs can be subsumed by horizons, after which the subsuming horizon becomes the epoch for all following nodes.Inserting a new epoch task with
task_manager::finish_epoch()
will create one epoch command per node, with dependencies on the current execution front. Eachepoch_command
will issue oneepoch_job
, which awaits the completion of said dependencies, optionally issues anMPI_Barrier
and then notifies the task manager vianotify_epoch_completed()
. Other threads can await this notification to block the main thread inqueue::slow_full_sync()
and~runtime()
.The nodes preceding a task epoch are pruned once the first task is created after
notify_epoch_completed()
. In the command graph generator, nodes can be deleted as soon as they lie behind the last epoch, as they can't be depended on by any successor task. In that regard, epochs act like immediately-applied horizons.This PR also recognizes that the task manager can prune all nodes preceding a horizon
A
as soon as it has been notified that horizonB
withA → B
has been reached. This notification signifies that all commands of tasks precedingA
have finished executing, so even though they might still be referenced from already-executed commands in the command graph, the task data never has to be accessed again.To aid debugging, Celerity will now also track the origin of a dependency in addition to its kind. This is currently visualized in generated dot graphs.
The old synchronization infrastructure (
broadcast_control_command()
,sync_id
, ...) has been removed.