diff --git a/CHANGELOG.md b/CHANGELOG.md index 456528b..e25dad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add new body model to support parallel tasks in nOS-V (used in taskfor). +- Add the ability to restrict transitions in the task model states. +- Add nOS-V support for parallel tasks reading the body id from the + event payload. + ### Changed - The task model now requires the previous task body to be paused before nesting diff --git a/cfg/cpu/nosv/body-id.cfg b/cfg/cpu/nosv/body-id.cfg new file mode 100644 index 0000000..1d04787 --- /dev/null +++ b/cfg/cpu/nosv/body-id.cfg @@ -0,0 +1,42 @@ +#ParaverCFG +ConfigFile.Version: 3.4 +ConfigFile.NumWindows: 1 + + +################################################################################ +< NEW DISPLAYING WINDOW CPU: nOS-V task body ID of the RUNNING thread > +################################################################################ +window_name CPU: nOS-V task body ID of the RUNNING thread +window_type single +window_id 1 +window_position_x 0 +window_position_y 0 +window_width 600 +window_height 150 +window_comm_lines_enabled true +window_flags_enabled false +window_noncolor_mode true +window_color_mode window_in_null_gradient_mode +window_logical_filtered true +window_physical_filtered false +window_comm_fromto true +window_comm_tagsize true +window_comm_typeval true +window_units Microseconds +window_maximum_y 1000.0 +window_minimum_y 1.0 +window_compute_y_max true +window_level thread +window_scale_relative 1.000000000000 +window_end_time_relative 1.000000000000 +window_object appl { 1, { All } } +window_begin_time_relative 0.000000000000 +window_open true +window_drawmode draw_randnotzero +window_drawmode_rows draw_randnotzero +window_pixel_size 1 +window_labels_to_draw 1 +window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } +window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } +window_filter_module evt_type 1 15 +window_filter_module evt_type_label 1 "CPU: nOS-V task body ID of the RUNNING thread" diff --git a/cfg/thread/nosv/body-id.cfg b/cfg/thread/nosv/body-id.cfg new file mode 100644 index 0000000..4bb9915 --- /dev/null +++ b/cfg/thread/nosv/body-id.cfg @@ -0,0 +1,42 @@ +#ParaverCFG +ConfigFile.Version: 3.4 +ConfigFile.NumWindows: 1 + + +################################################################################ +< NEW DISPLAYING WINDOW Thread: nOS-V task body ID of the RUNNING thread > +################################################################################ +window_name Thread: nOS-V task body ID of the RUNNING thread +window_type single +window_id 1 +window_position_x 0 +window_position_y 0 +window_width 600 +window_height 150 +window_comm_lines_enabled true +window_flags_enabled false +window_noncolor_mode true +window_color_mode window_in_null_gradient_mode +window_logical_filtered true +window_physical_filtered false +window_comm_fromto true +window_comm_tagsize true +window_comm_typeval true +window_units Microseconds +window_maximum_y 1000.0 +window_minimum_y 1.0 +window_compute_y_max true +window_level thread +window_scale_relative 1.000000000000 +window_end_time_relative 1.000000000000 +window_object appl { 1, { All } } +window_begin_time_relative 0.000000000000 +window_open true +window_drawmode draw_randnotzero +window_drawmode_rows draw_randnotzero +window_pixel_size 1 +window_labels_to_draw 1 +window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } +window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } +window_filter_module evt_type 1 15 +window_filter_module evt_type_label 1 "Thread: nOS-V task body ID of the RUNNING thread" diff --git a/doc/user/emulation/events.md b/doc/user/emulation/events.md index 02f0456..c8c2aa5 100644 --- a/doc/user/emulation/events.md +++ b/doc/user/emulation/events.md @@ -1,7 +1,7 @@ # Emulator events This is a exhaustive list of the events recognized by the emulator. -Built on Feb 15 2024. +Built on Mar 13 2024. ## Model nanos6 @@ -615,18 +615,20 @@ List of events for the model *tampi* with identifier **`T`** at version `1.0.0`: ## Model nosv -List of events for the model *nosv* with identifier **`V`** at version `1.1.0`: +List of events for the model *nosv* with identifier **`V`** at version `2.0.0`:
VTc(u32 taskid, u32 typeid)
creates task %{taskid} with type %{typeid}
-
VTx(u32 taskid)
-
executes the task %{taskid}
-
VTe(u32 taskid)
-
ends the task %{taskid}
-
VTp(u32 taskid)
-
pauses the task %{taskid}
-
VTr(u32 taskid)
-
resumes the task %{taskid}
+
VTC(u32 taskid, u32 typeid)
+
creates parallel task %{taskid} with type %{typeid}
+
VTx(u32 taskid, u32 bodyid)
+
executes the task %{taskid} with bodyid %{bodyid}
+
VTe(u32 taskid, u32 bodyid)
+
ends the task %{taskid} with bodyid %{bodyid}
+
VTp(u32 taskid, u32 bodyid)
+
pauses the task %{taskid} with bodyid %{bodyid}
+
VTr(u32 taskid, u32 bodyid)
+
resumes the task %{taskid} with bodyid %{bodyid}
VYc+(u32 typeid, str label)
creates task type %{typeid} with label "%{label}"
VSr
diff --git a/doc/user/emulation/fig/body-model.dot b/doc/user/emulation/fig/body-model.dot new file mode 100644 index 0000000..d179340 --- /dev/null +++ b/doc/user/emulation/fig/body-model.dot @@ -0,0 +1,9 @@ +digraph { + graph [size="6!", ranksep="0.7", nodesep="1"]; + + Created -> Running; + Running -> Paused [label=BODY_FLAG_PAUSE]; + Paused -> Running; + Running -> Dead; + Dead -> Running [label="BODY_FLAG_RESURRECT\niteration++"]; +} diff --git a/doc/user/emulation/fig/body-model.svg b/doc/user/emulation/fig/body-model.svg new file mode 100644 index 0000000..0f575a4 --- /dev/null +++ b/doc/user/emulation/fig/body-model.svg @@ -0,0 +1,69 @@ + + + + + + + + + +Created + +Created + + + +Running + +Running + + + +Created->Running + + + + + +Paused + +Paused + + + +Running->Paused + + +BODY_FLAG_PAUSE + + + +Dead + +Dead + + + +Running->Dead + + + + + +Paused->Running + + + + + +Dead->Running + + +BODY_FLAG_RESURRECT +iteration++ + + + diff --git a/doc/user/emulation/fig/parallel-tasks.svg b/doc/user/emulation/fig/parallel-tasks.svg new file mode 100644 index 0000000..3a85b3c --- /dev/null +++ b/doc/user/emulation/fig/parallel-tasks.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + Normal task + + Body 1 + + Parallel task + + Body 1 + + Body 2 + + ... + + diff --git a/doc/user/emulation/nosv.md b/doc/user/emulation/nosv.md index a9ebfc4..054d5b2 100644 --- a/doc/user/emulation/nosv.md +++ b/doc/user/emulation/nosv.md @@ -1,9 +1,42 @@ # nOS-V model +The [nOS-V library][nosv] implements a user space runtime that can schedule +tasks to run in multiple CPUs. The nOS-V library is instrumented to +track the internal state of the runtime as well as emit information +about the tasks that are running. + +[nosv]: https://github.com/bsc-pm/nos-v + +## Task model + The nOS-V runtime is composed of tasks that can be scheduled to run in threads. Tasks can be paused and resumed, leaving the CPUs free to -execute other tasks. The nOS-V model tracks the state of each task as -well as the state of the runtime internal state. +execute other tasks. + +In nOS-V, parallel tasks can also be scheduled multiple times and the +same task may run concurrently in several CPUs. To model this scenario, +we introduce the concept of *body*, which maps to each execution of the +same task, with a unique body id. + +![Parallel tasks](fig/parallel-tasks.svg) + +A normal task only has one body, while a parallel task (created with +`TASK_FLAG_PARALLEL`) can have more than one body. Each body holds the +execution state, and can transition to different execution states +following this state diagram: + +![Body model](fig/body-model.svg) + +Bodies begin in the Created state and transition to Running when they +begin the execution. Bodies that can be paused (created with the flag +`BODY_FLAG_PAUSE` can transition to the Paused state. + +Additionally, bodies can run multiple times if they are created with the +`BODY_FLAG_RESURRECT`, and transition from Dead to Running. This +transition is required to model the tasks that implement the taskiter in +NODES, which will be submitted multiple times for execution reusing the +same task id and body id. Every time a body runs again, the iteration +number is increased. ## Task type colors diff --git a/src/emu/emu_prv.h b/src/emu/emu_prv.h index 664ac0b..48b6010 100644 --- a/src/emu/emu_prv.h +++ b/src/emu/emu_prv.h @@ -18,6 +18,7 @@ enum emu_prv_types { PRV_NOSV_APPID = 12, PRV_NOSV_SUBSYSTEM = 13, PRV_NOSV_RANK = 14, + PRV_NOSV_BODYID = 15, PRV_TAMPI_SUBSYSTEM = 20, PRV_MPI_FUNCTION = 25, PRV_NODES_SUBSYSTEM = 30, diff --git a/src/emu/nosv/event.c b/src/emu/nosv/event.c index bfe5051..cca6fe6 100644 --- a/src/emu/nosv/event.c +++ b/src/emu/nosv/event.c @@ -97,12 +97,16 @@ simple(struct emu *emu) } static int -chan_task_stopped(struct emu *emu) +chan_body_stopped(struct emu *emu) { struct nosv_thread *th = EXT(emu->thread, 'V'); struct chan *ch = th->m.ch; struct value null = value_null(); + if (chan_set(&ch[CH_BODYID], null) != 0) { + err("chan_set taskid failed"); + return -1; + } if (chan_set(&ch[CH_TASKID], null) != 0) { err("chan_set taskid failed"); return -1; @@ -128,8 +132,9 @@ chan_task_stopped(struct emu *emu) } static int -chan_task_running(struct emu *emu, struct task *task) +chan_body_running(struct emu *emu, struct body *body) { + struct task *task = body_get_task(body); struct nosv_thread *th = EXT(emu->thread, 'V'); struct proc *proc = emu->proc; struct chan *ch = th->m.ch; @@ -147,6 +152,10 @@ chan_task_running(struct emu *emu, struct task *task) return -1; } + if (chan_set(&ch[CH_BODYID], value_int64(body_get_id(body))) != 0) { + err("chan_set bodyid failed"); + return -1; + } if (chan_set(&ch[CH_TASKID], value_int64(task->id)) != 0) { err("chan_set taskid failed"); return -1; @@ -171,22 +180,24 @@ chan_task_running(struct emu *emu, struct task *task) } static int -chan_task_switch(struct emu *emu, struct task *prev, struct task *next) +chan_body_switch(struct emu *emu, struct body *bprev, struct body *bnext) { struct nosv_thread *th = EXT(emu->thread, 'V'); struct chan *ch = th->m.ch; struct proc *proc = emu->proc; - if (!prev || !next) { - err("cannot switch to or from a NULL task"); + if (!bprev || !bnext) { + err("cannot switch to or from a NULL body"); return -1; } - if (prev == next) { - err("cannot switch to the same task"); + if (bprev == bnext) { + err("cannot switch to the same body"); return -1; } + struct task *next = body_get_task(bnext); + if (next->id == 0) { err("next task id cannot be 0"); return -1; @@ -197,11 +208,10 @@ chan_task_switch(struct emu *emu, struct task *prev, struct task *next) return -1; } - if (prev->thread != next->thread) { - err("cannot switch to a task of another thread"); + if (chan_set(&ch[CH_BODYID], value_int64(body_get_id(bnext))) != 0) { + err("chan_set taskid failed"); return -1; } - if (chan_set(&ch[CH_TASKID], value_int64(next->id)) != 0) { err("chan_set taskid failed"); return -1; @@ -235,12 +245,15 @@ update_task_state(struct emu *emu) uint32_t task_id = emu->ev->payload->u32[0]; + if (emu->ev->payload_size < 8) { + err("missing body id in payload"); + return -1; + } + struct nosv_thread *th = EXT(emu->thread, 'V'); struct nosv_proc *proc = EXT(emu->proc, 'V'); - struct task_info *info = &proc->task_info; struct task_stack *stack = &th->task_stack; - struct task *task = task_find(info->tasks, task_id); if (task == NULL) { @@ -248,19 +261,37 @@ update_task_state(struct emu *emu) return -1; } + uint32_t body_id = emu->ev->payload->u32[1]; + + if (task_is_parallel(task)) { + if (body_id == 0) { + err("the body_id must be > 0 for parallel task %u", + task_get_id(task)); + return -1; + } + } else { + if (body_id != 0) { + err("the body_id must be 0 for non-parallel task %u", + task_get_id(task)); + return -1; + } + /* We use body_id = 1 internally, as they cannot be zero. */ + body_id = 1; + } + int ret = 0; switch (emu->ev->v) { case 'x': - ret = task_execute(stack, task); + ret = task_execute(stack, task, body_id); break; case 'e': - ret = task_end(stack, task); + ret = task_end(stack, task, body_id); break; case 'p': - ret = task_pause(stack, task); + ret = task_pause(stack, task, body_id); break; case 'r': - ret = task_resume(stack, task); + ret = task_resume(stack, task, body_id); break; default: err("unexpected nOS-V task event"); @@ -297,22 +328,22 @@ expand_transition_value(struct emu *emu, int was_running, int runs_now, char *tr } static int -update_task_channels(struct emu *emu, char tr, struct task *prev, struct task *next) +update_task_channels(struct emu *emu, char tr, struct body *bprev, struct body *bnext) { int ret = 0; switch (tr) { case 'x': case 'r': - ret = chan_task_running(emu, next); + ret = chan_body_running(emu, bnext); break; case 'e': case 'p': - ret = chan_task_stopped(emu); + ret = chan_body_stopped(emu); break; /* Additional nested transitions */ case 'X': case 'E': - ret = chan_task_switch(emu, prev, next); + ret = chan_body_switch(emu, bprev, bnext); break; default: err("unexpected transition value %c", tr); @@ -328,7 +359,7 @@ update_task_channels(struct emu *emu, char tr, struct task *prev, struct task *n } static int -enforce_task_rules(struct emu *emu, char tr, struct task *next) +enforce_task_rules(struct emu *emu, char tr, struct body *next) { if (tr != 'x' && tr != 'X') return 0; @@ -336,8 +367,8 @@ enforce_task_rules(struct emu *emu, char tr, struct task *next) /* If a task has just entered the running state, it must show * the running task body subsystem */ - if (next->state != TASK_ST_RUNNING) { - err("task not in running state on begin"); + if (body_get_state(next) != BODY_ST_RUNNING) { + err("body not in running state on begin"); return -1; } @@ -384,7 +415,7 @@ update_task(struct emu *emu) struct nosv_thread *th = EXT(emu->thread, 'V'); struct task_stack *stack = &th->task_stack; - struct task *prev = task_get_running(stack); + struct body *prev = task_get_running(stack); /* Update the emulator state, but don't modify the channels */ if (update_task_state(emu) != 0) { @@ -392,7 +423,7 @@ update_task(struct emu *emu) return -1; } - struct task *next = task_get_running(stack); + struct body *next = task_get_running(stack); /* Update the subsystem channel */ if (update_task_ss_channel(emu, emu->ev->v) != 0) { @@ -423,9 +454,9 @@ update_task(struct emu *emu) } static int -create_task(struct emu *emu) +create_task(struct emu *emu, char value) { - if (emu->ev->payload_size != 8) { + if (emu->ev->payload_size < 8) { err("unexpected payload size"); return -1; } @@ -433,10 +464,20 @@ create_task(struct emu *emu) uint32_t task_id = emu->ev->payload->u32[0]; uint32_t type_id = emu->ev->payload->u32[1]; + int is_parallel = (value == 'C'); + + uint32_t flags; + + /* Parallel tasks cannot pause or resurrect */ + if (is_parallel) + flags = TASK_FLAG_PARALLEL; + else + flags = TASK_FLAG_RESURRECT | TASK_FLAG_PAUSE; + struct nosv_proc *proc = EXT(emu->proc, 'V'); struct task_info *info = &proc->task_info; - if (task_create(info, type_id, task_id) != 0) { + if (task_create(info, type_id, task_id, flags) != 0) { err("task_create failed"); return -1; } @@ -451,8 +492,9 @@ pre_task(struct emu *emu) { int ret = 0; switch (emu->ev->v) { + case 'C': case 'c': - ret = create_task(emu); + ret = create_task(emu, emu->ev->v); break; case 'x': case 'e': diff --git a/src/emu/nosv/nosv_priv.h b/src/emu/nosv/nosv_priv.h index cef68e7..01f523a 100644 --- a/src/emu/nosv/nosv_priv.h +++ b/src/emu/nosv/nosv_priv.h @@ -12,7 +12,8 @@ /* Private enums */ enum nosv_chan { - CH_TASKID = 0, + CH_BODYID = 0, + CH_TASKID, CH_TYPE, CH_APPID, CH_SUBSYSTEM, diff --git a/src/emu/nosv/setup.c b/src/emu/nosv/setup.c index fd4bfeb..36464da 100644 --- a/src/emu/nosv/setup.c +++ b/src/emu/nosv/setup.c @@ -32,10 +32,11 @@ enum { model_id = 'V' }; static struct ev_decl model_evlist[] = { { "VTc(u32 taskid, u32 typeid)", "creates task %{taskid} with type %{typeid}" }, - { "VTx(u32 taskid)", "executes the task %{taskid}" }, - { "VTe(u32 taskid)", "ends the task %{taskid}" }, - { "VTp(u32 taskid)", "pauses the task %{taskid}" }, - { "VTr(u32 taskid)", "resumes the task %{taskid}" }, + { "VTC(u32 taskid, u32 typeid)", "creates parallel task %{taskid} with type %{typeid}" }, + { "VTx(u32 taskid, u32 bodyid)", "executes the task %{taskid} with bodyid %{bodyid}" }, + { "VTe(u32 taskid, u32 bodyid)", "ends the task %{taskid} with bodyid %{bodyid}" }, + { "VTp(u32 taskid, u32 bodyid)", "pauses the task %{taskid} with bodyid %{bodyid}" }, + { "VTr(u32 taskid, u32 bodyid)", "resumes the task %{taskid} with bodyid %{bodyid}" }, { "VYc+(u32 typeid, str label)", "creates task type %{typeid} with label \"%{label}\"" }, @@ -72,7 +73,7 @@ static struct ev_decl model_evlist[] = { struct model_spec model_nosv = { .name = model_name, - .version = "1.1.0", + .version = "2.0.0", .evlist = model_evlist, .model = model_id, .create = model_nosv_create, @@ -85,6 +86,7 @@ struct model_spec model_nosv = { /* ----------------- channels ------------------ */ static const char *chan_name[CH_MAX] = { + [CH_BODYID] = "bodyid", [CH_TASKID] = "taskid", [CH_TYPE] = "task_type", [CH_APPID] = "appid", @@ -97,6 +99,7 @@ static const int chan_stack[CH_MAX] = { }; static const int chan_dup[CH_MAX] = { + [CH_BODYID] = 1, /* Switch to another task with body 1 */ [CH_APPID] = 1, [CH_TYPE] = 1, [CH_RANK] = 1, @@ -108,6 +111,7 @@ static const int chan_dup[CH_MAX] = { /* ----------------- pvt ------------------ */ static const int pvt_type[CH_MAX] = { + [CH_BODYID] = PRV_NOSV_BODYID, [CH_TASKID] = PRV_NOSV_TASKID, [CH_TYPE] = PRV_NOSV_TYPE, [CH_APPID] = PRV_NOSV_APPID, @@ -116,6 +120,7 @@ static const int pvt_type[CH_MAX] = { }; static const char *pcf_prefix[CH_MAX] = { + [CH_BODYID] = "nOS-V task body ID", [CH_TASKID] = "nOS-V task ID", [CH_TYPE] = "nOS-V task type", [CH_APPID] = "nOS-V task AppID", @@ -152,11 +157,12 @@ static const struct pcf_value_label *pcf_labels[CH_MAX] = { }; static const long prv_flags[CH_MAX] = { - [CH_TASKID] = PRV_SKIPDUP, - [CH_TYPE] = PRV_EMITDUP, /* Switch to task of same type */ - [CH_APPID] = PRV_EMITDUP, /* Switch to task of same appid */ - [CH_SUBSYSTEM] = PRV_SKIPDUP, - [CH_RANK] = PRV_EMITDUP, /* Switch to task of same rank */ + [CH_BODYID] = PRV_SKIPDUPNULL, /* Switch to different task, same bodyid */ + [CH_TASKID] = PRV_SKIPDUPNULL, /* Switch to another body of the same task */ + [CH_TYPE] = PRV_SKIPDUPNULL, /* Switch to task of same type */ + [CH_APPID] = PRV_SKIPDUPNULL, /* Switch to task of same appid */ + [CH_SUBSYSTEM] = PRV_SKIPDUPNULL, + [CH_RANK] = PRV_SKIPDUPNULL, /* Switch to task of same rank */ }; static const struct model_pvt_spec pvt_spec = { @@ -169,6 +175,7 @@ static const struct model_pvt_spec pvt_spec = { /* ----------------- tracking ------------------ */ static const int th_track[CH_MAX] = { + [CH_BODYID] = TRACK_TH_RUN, [CH_TASKID] = TRACK_TH_RUN, [CH_TYPE] = TRACK_TH_RUN, [CH_APPID] = TRACK_TH_RUN, @@ -177,6 +184,7 @@ static const int th_track[CH_MAX] = { }; static const int cpu_track[CH_MAX] = { + [CH_BODYID] = TRACK_TH_RUN, [CH_TASKID] = TRACK_TH_RUN, [CH_TYPE] = TRACK_TH_RUN, [CH_APPID] = TRACK_TH_RUN, @@ -256,12 +264,6 @@ model_nosv_create(struct emu *emu) return -1; } - /* Init task stack thread pointer */ - for (struct thread *t = sys->threads; t; t = t->gnext) { - struct nosv_thread *th = EXT(t, model_id); - th->task_stack.thread = t; - } - for (struct proc *p = sys->procs; p; p = p->gnext) { if (init_proc(p) != 0) { err("init_proc failed"); diff --git a/test/emu/nosv/CMakeLists.txt b/test/emu/nosv/CMakeLists.txt index 9e11a54..ee32e30 100644 --- a/test/emu/nosv/CMakeLists.txt +++ b/test/emu/nosv/CMakeLists.txt @@ -5,7 +5,7 @@ test_emu(attach.c) test_emu(attach-old.c) test_emu(nested-tasks.c) test_emu(nested-tasks-bad.c SHOULD_FAIL - REGEX "cannot execute task 1: state is not created") + REGEX "body_execute: body 1 state must be Created but is Running") test_emu(task-types.c MP) test_emu(pause.c MP) #test_emu(mp-rank.c MP)