Skip to content

Commit 31f57fb

Browse files
config: Add "events" key in parse_task_phase_data()
Allow parsing an "events" key for phases with the following format: "events": [ {"run": 1000}, {"timer": {"ref": "helloworld", "period": 16000}}, {"run": 9000}, {"sleep": 1000} ] This allows: * Enforced ordering of events. Relying on the order of keys in an object is issue-prone, as interfaces to interact with mappings in most languages are not designed around preserving such order. On the contrary, array operations have always a well defined order semantic. * More critically, this allows repeating the same event an arbitrary number of times. Complex workload might otherwise need to be split among different phases to workaround that issue, which comes with some overhead and extra bookeeping in external code. Checked constraints: * only event objects are allowed in the array * objects can only have one key * when "events" is in use, no event key is allowed in the phase body. This means the user either uses the old format or the new one but mixing is not allowed. Signed-off-by: Douglas RAILLARD <[email protected]>
1 parent 37667fe commit 31f57fb

File tree

2 files changed

+102
-23
lines changed

2 files changed

+102
-23
lines changed

doc/tutorial.txt

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -285,16 +285,16 @@ scheduler point of view.
285285
*** NUMA memory binding
286286

287287
* nodes_membind: Array of Integer. Define the NUMA binding of the thread.
288-
Default value is all NUMA nodes of the system.
288+
Default value is all NUMA nodes of the system.
289289
An example : "nodes_membind" : [0, 2, 3]
290290

291-
"nodes_membind" Can be specified at task level or phase level.
291+
"nodes_membind" Can be specified at task level or phase level.
292292
As an example, below creates two threads.
293-
One thread will run with memory binding to nodes 2 and 3.
294-
The second task will run first phase with memory binding to nodes 0 and 1,
293+
One thread will run with memory binding to nodes 2 and 3.
294+
The second task will run first phase with memory binding to nodes 0 and 1,
295295
second phase with memory binding to node 2.
296-
Please note, that we follow an "event based policy", which means that
297-
rt-app changes memory binding when there is a "nodes_membind" event
296+
Please note, that we follow an "event based policy", which means that
297+
rt-app changes memory binding when there is a "nodes_membind" event
298298
and don't do anything otherwise.
299299

300300
"tasks" : {
@@ -339,8 +339,32 @@ unsigned int max_nbr_tgs = 32' at compile time.
339339

340340
*** events ***
341341

342-
events are simple action that will be performed by the thread or on the
343-
thread. They have to be listed by execution order.
342+
events are simple action that will be performed by the phase or on the
343+
thread.
344+
345+
Two formats are supported:
346+
* Either specified directly in the thread or phase body, in execution order:
347+
348+
"run": 1000,
349+
"timer": {"ref": "helloworld", "period": 16000}
350+
351+
* As objects inside an array stored under the "events" key:
352+
353+
"events": [
354+
{"run": 1000},
355+
{"timer": {"ref": "helloworld", "period": 16000}},
356+
{"run": 9000},
357+
{"sleep": 1000}
358+
]
359+
360+
Each object in the array must contain a single event key. This format allows
361+
specifying the same event more than once (like "run" in the example), and is
362+
friendlier to languages with map types that are not preserving insertion
363+
order.
364+
365+
Both formats cannot be mixed together.
366+
367+
Available events:
344368

345369
* run : Integer. Emulate the execution of a load. The duration is defined in
346370
usec but the run event will effectively run a number of time a loop that waste
@@ -650,7 +674,7 @@ The generated events are of these main categories:
650674

651675
- rtapp_loop: event=start thread_loop=0 phase=0 phase_loop=0
652676
- rtapp_loop: event=end thread_loop=0 phase=0 phase_loop=0
653-
677+
654678
Reporting the start and end of workload's phases and loops.
655679

656680
* Workload's events, for example:
@@ -756,5 +780,3 @@ below)
756780
20000 ++--+----+---+----+---+---+----+---+----+--++ 494000
757781
17560556057560556057560656065606756065606756075607
758782
Loop start time [msec]
759-
760-

src/rt-app_parse_config.c

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -983,37 +983,94 @@ parse_task_phase_data(struct json_object *obj,
983983
{
984984
/* used in the foreach macro */
985985
struct lh_entry *entry; char *key; struct json_object *val; int idx;
986-
int i;
986+
struct json_object *array_val;
987+
size_t i;
988+
struct json_object *events_obj = NULL;
989+
bool is_first_entry;
987990

988991
log_info(PFX "Parsing phase");
989992

993+
data->nbevents = 0;
994+
data->events = NULL;
995+
990996
/* loop */
991997
data->loop = get_int_value_from(obj, "loop", TRUE, 1);
992998

993-
/* Count number of events */
994-
data->nbevents = 0;
995999
foreach(obj, entry, key, val, idx) {
996-
if (obj_is_event(key))
1000+
if(!strncmp("events", key, 6)) {
1001+
if(!json_object_is_type(val, json_type_array)) {
1002+
log_critical(PIN "\"events\" key must be an array");
1003+
exit(EXIT_INV_CONFIG);
1004+
}
1005+
events_obj = val;
1006+
break;
1007+
}
1008+
}
1009+
1010+
foreach(obj, entry, key, val, idx) {
1011+
if (obj_is_event(key)) {
1012+
/* Check that we are not trying to mix old and new styles */
1013+
if (events_obj) {
1014+
log_critical(PIN "Event key %s cannot be used in conjunction with \"events\" key", key);
1015+
exit(EXIT_INV_CONFIG);
1016+
/* Count number of events */
1017+
} else {
9971018
data->nbevents++;
1019+
}
1020+
}
9981021
}
9991022

1023+
if (events_obj)
1024+
/* Assumes that all item in the array will be valid. If an item
1025+
* does not yield an entry in data->events, we must ensure it won't
1026+
* be used by exiting
1027+
*/
1028+
data->nbevents = json_object_array_length(events_obj);
1029+
10001030
if (data->nbevents == 0) {
10011031
log_critical(PIN "No events found. Task must have events or it's useless");
10021032
exit(EXIT_INV_CONFIG);
1003-
10041033
}
10051034

10061035
log_info(PIN "Found %d events", data->nbevents);
1007-
10081036
data->events = malloc(data->nbevents * sizeof(event_data_t));
10091037

10101038
/* Parse events */
1011-
i = 0;
1012-
foreach(obj, entry, key, val, idx) {
1013-
if (obj_is_event(key)) {
1014-
log_info(PIN "Parsing event %s", key);
1015-
parse_task_event_data(key, val, &data->events[i], tdata, opts);
1016-
i++;
1039+
if (events_obj) {
1040+
for (i=0; i < data->nbevents; i++) {
1041+
array_val = json_object_array_get_idx(events_obj, i);
1042+
is_first_entry = true;
1043+
foreach(array_val, entry, key, val, idx) {
1044+
if (!is_first_entry) {
1045+
log_critical(PIN "Encountered a second key %s in an \"events\" item", key);
1046+
exit(EXIT_INV_CONFIG);
1047+
} else {
1048+
is_first_entry = false;
1049+
}
1050+
1051+
if (obj_is_event(key)) {
1052+
log_info(PIN "Parsing event %s", key);
1053+
parse_task_event_data(key, val, &data->events[i], tdata, opts);
1054+
} else {
1055+
/* We must exit in this branch,
1056+
* otherwise we will have a non-initialized
1057+
* entry in data->events that would be an
1058+
* undefined behavior
1059+
*/
1060+
log_critical(PIN "Encountered non-event key %s in \"events\" array", key);
1061+
exit(EXIT_INV_CONFIG);
1062+
}
1063+
}
1064+
1065+
}
1066+
} else {
1067+
i = 0;
1068+
foreach(obj, entry, key, val, idx) {
1069+
if (obj_is_event(key)) {
1070+
log_info(PIN "Parsing event %s", key);
1071+
parse_task_event_data(key, val, &data->events[i], tdata, opts);
1072+
i++;
1073+
}
10171074
}
10181075
}
10191076
parse_cpuset_data(obj, &data->cpu_data);

0 commit comments

Comments
 (0)