165 changes: 104 additions & 61 deletions openmp/runtime/src/kmp_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3662,104 +3662,139 @@ static inline void __kmp_omp_schedule_restore() {
__kmp_sched = kmp_sch_default;
}

// if parse_hier = true:
// Parse [HW,][modifier:]kind[,chunk]
// else:
// Parse [modifier:]kind[,chunk]
static const char *__kmp_parse_single_omp_schedule(const char *name,
const char *value,
bool parse_hier = false) {
/* get the specified scheduling style */
const char *ptr = value;
const char *comma = strchr(ptr, ',');
const char *delim;
int chunk = 0;
enum sched_type sched = kmp_sch_default;
if (*ptr == '\0')
return NULL;
delim = ptr;
while (*delim != ',' && *delim != ':' && *delim != '\0')
delim++;
#if KMP_USE_HIER_SCHED
kmp_hier_layer_e layer = kmp_hier_layer_e::LAYER_THREAD;
if (parse_hier) {
if (!__kmp_strcasecmp_with_sentinel("L1", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_L1;
} else if (!__kmp_strcasecmp_with_sentinel("L2", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_L2;
} else if (!__kmp_strcasecmp_with_sentinel("L3", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_L3;
} else if (!__kmp_strcasecmp_with_sentinel("NUMA", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_NUMA;
}
if (layer != kmp_hier_layer_e::LAYER_THREAD && !comma) {
if (*delim == ',') {
if (!__kmp_strcasecmp_with_sentinel("L1", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_L1;
} else if (!__kmp_strcasecmp_with_sentinel("L2", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_L2;
} else if (!__kmp_strcasecmp_with_sentinel("L3", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_L3;
} else if (!__kmp_strcasecmp_with_sentinel("NUMA", ptr, ',')) {
layer = kmp_hier_layer_e::LAYER_NUMA;
}
}
if (layer != kmp_hier_layer_e::LAYER_THREAD && *delim != ',') {
// If there is no comma after the layer, then this schedule is invalid
KMP_WARNING(StgInvalidValue, name, value);
__kmp_omp_schedule_restore();
return NULL;
} else if (layer != kmp_hier_layer_e::LAYER_THREAD) {
ptr = ++comma;
comma = strchr(ptr, ',');
ptr = ++delim;
while (*delim != ',' && *delim != ':' && *delim != '\0')
delim++;
}
}
delim = ptr;
while (*delim != ',' && *delim != ':' && *delim != '\0')
delim++;
#else // KMP_USE_HIER_SCHED
delim = ptr;
while (*delim != ',' && *delim != '\0')
delim++;
#endif // KMP_USE_HIER_SCHED
if (!__kmp_strcasecmp_with_sentinel("dynamic", ptr, *delim)) /* DYNAMIC */
#if OMP_45_ENABLED
// Read in schedule modifier if specified
enum sched_type sched_modifier = (enum sched_type)0;
if (*delim == ':') {
if (!__kmp_strcasecmp_with_sentinel("monotonic", ptr, *delim)) {
sched_modifier = sched_type::kmp_sch_modifier_monotonic;
ptr = ++delim;
while (*delim != ',' && *delim != ':' && *delim != '\0')
delim++;
} else if (!__kmp_strcasecmp_with_sentinel("nonmonotonic", ptr, *delim)) {
sched_modifier = sched_type::kmp_sch_modifier_nonmonotonic;
ptr = ++delim;
while (*delim != ',' && *delim != ':' && *delim != '\0')
delim++;
} else if (!parse_hier) {
// If there is no proper schedule modifier, then this schedule is invalid
KMP_WARNING(StgInvalidValue, name, value);
__kmp_omp_schedule_restore();
return NULL;
}
}
#endif
// Read in schedule kind (required)
if (!__kmp_strcasecmp_with_sentinel("dynamic", ptr, *delim))
sched = kmp_sch_dynamic_chunked;
else if (!__kmp_strcasecmp_with_sentinel("guided", ptr, *delim)) /* GUIDED */
else if (!__kmp_strcasecmp_with_sentinel("guided", ptr, *delim))
sched = kmp_sch_guided_chunked;
// AC: TODO: add AUTO schedule, and probably remove TRAPEZOIDAL (OMP 3.0 does
// not allow it)
else if (!__kmp_strcasecmp_with_sentinel("auto", ptr, *delim)) { /* AUTO */
// AC: TODO: probably remove TRAPEZOIDAL (OMP 3.0 does not allow it)
else if (!__kmp_strcasecmp_with_sentinel("auto", ptr, *delim))
sched = kmp_sch_auto;
if (comma) {
__kmp_msg(kmp_ms_warning, KMP_MSG(IgnoreChunk, name, comma),
__kmp_msg_null);
comma = NULL;
}
} else if (!__kmp_strcasecmp_with_sentinel("trapezoidal", ptr,
*delim)) /* TRAPEZOIDAL */
else if (!__kmp_strcasecmp_with_sentinel("trapezoidal", ptr, *delim))
sched = kmp_sch_trapezoidal;
else if (!__kmp_strcasecmp_with_sentinel("static", ptr, *delim)) /* STATIC */
else if (!__kmp_strcasecmp_with_sentinel("static", ptr, *delim))
sched = kmp_sch_static;
#if KMP_STATIC_STEAL_ENABLED
else if (!__kmp_strcasecmp_with_sentinel("static_steal", ptr, *delim))
sched = kmp_sch_static_steal;
#endif
else {
// If there is no proper schedule kind, then this schedule is invalid
KMP_WARNING(StgInvalidValue, name, value);
__kmp_omp_schedule_restore();
return NULL;
}
if (ptr && comma && *comma == *delim) {
ptr = comma + 1;
SKIP_DIGITS(ptr);

if (sched == kmp_sch_static)
sched = kmp_sch_static_chunked;
++comma;
chunk = __kmp_str_to_int(comma, *ptr);
if (chunk < 1) {
chunk = KMP_DEFAULT_CHUNK;
__kmp_msg(kmp_ms_warning, KMP_MSG(InvalidChunk, name, comma),
__kmp_msg_null);
KMP_INFORM(Using_int_Value, name, __kmp_chunk);
// AC: next block commented out until KMP_DEFAULT_CHUNK != KMP_MIN_CHUNK
// (to improve code coverage :)
// The default chunk size is 1 according to standard, thus making
// KMP_MIN_CHUNK not 1 we would introduce mess:
// wrong chunk becomes 1, but it will be impossible to explicitely set
// 1, because it becomes KMP_MIN_CHUNK...
// } else if ( chunk < KMP_MIN_CHUNK ) {
// chunk = KMP_MIN_CHUNK;
} else if (chunk > KMP_MAX_CHUNK) {
chunk = KMP_MAX_CHUNK;
__kmp_msg(kmp_ms_warning, KMP_MSG(LargeChunk, name, comma),
// Read in schedule chunk size if specified
if (*delim == ',') {
ptr = delim + 1;
SKIP_WS(ptr);
if (!isdigit(*ptr)) {
// If there is no chunk after comma, then this schedule is invalid
KMP_WARNING(StgInvalidValue, name, value);
__kmp_omp_schedule_restore();
return NULL;
}
SKIP_DIGITS(ptr);
// auto schedule should not specify chunk size
if (sched == kmp_sch_auto) {
__kmp_msg(kmp_ms_warning, KMP_MSG(IgnoreChunk, name, delim),
__kmp_msg_null);
KMP_INFORM(Using_int_Value, name, chunk);
} else {
if (sched == kmp_sch_static)
sched = kmp_sch_static_chunked;
chunk = __kmp_str_to_int(delim + 1, *ptr);
if (chunk < 1) {
chunk = KMP_DEFAULT_CHUNK;
__kmp_msg(kmp_ms_warning, KMP_MSG(InvalidChunk, name, delim),
__kmp_msg_null);
KMP_INFORM(Using_int_Value, name, __kmp_chunk);
// AC: next block commented out until KMP_DEFAULT_CHUNK != KMP_MIN_CHUNK
// (to improve code coverage :)
// The default chunk size is 1 according to standard, thus making
// KMP_MIN_CHUNK not 1 we would introduce mess:
// wrong chunk becomes 1, but it will be impossible to explicitly set
// to 1 because it becomes KMP_MIN_CHUNK...
// } else if ( chunk < KMP_MIN_CHUNK ) {
// chunk = KMP_MIN_CHUNK;
} else if (chunk > KMP_MAX_CHUNK) {
chunk = KMP_MAX_CHUNK;
__kmp_msg(kmp_ms_warning, KMP_MSG(LargeChunk, name, delim),
__kmp_msg_null);
KMP_INFORM(Using_int_Value, name, chunk);
}
}
} else if (ptr) {
SKIP_TOKEN(ptr);
} else {
ptr = delim;
}

SCHEDULE_SET_MODIFIERS(sched, sched_modifier);

#if KMP_USE_HIER_SCHED
if (layer != kmp_hier_layer_e::LAYER_THREAD) {
__kmp_hier_scheds.append(sched, chunk, layer);
Expand Down Expand Up @@ -3790,6 +3825,8 @@ static void __kmp_stg_parse_omp_schedule(char const *name, char const *value,
while ((ptr = __kmp_parse_single_omp_schedule(name, ptr, true))) {
while (*ptr == ' ' || *ptr == '\t' || *ptr == ':')
ptr++;
if (*ptr == '\0')
break;
}
} else
#endif
Expand All @@ -3813,8 +3850,14 @@ static void __kmp_stg_print_omp_schedule(kmp_str_buf_t *buffer,
} else {
__kmp_str_buf_print(buffer, " %s='", name);
}
enum sched_type sched = SCHEDULE_WITHOUT_MODIFIERS(__kmp_sched);
if (SCHEDULE_HAS_MONOTONIC(__kmp_sched)) {
__kmp_str_buf_print(buffer, "monotonic:");
} else if (SCHEDULE_HAS_NONMONOTONIC(__kmp_sched)) {
__kmp_str_buf_print(buffer, "nonmonotonic:");
}
if (__kmp_chunk) {
switch (__kmp_sched) {
switch (sched) {
case kmp_sch_dynamic_chunked:
__kmp_str_buf_print(buffer, "%s,%d'\n", "dynamic", __kmp_chunk);
break;
Expand All @@ -3839,7 +3882,7 @@ static void __kmp_stg_print_omp_schedule(kmp_str_buf_t *buffer,
break;
}
} else {
switch (__kmp_sched) {
switch (sched) {
case kmp_sch_dynamic_chunked:
__kmp_str_buf_print(buffer, "%s'\n", "dynamic");
break;
Expand Down
86 changes: 86 additions & 0 deletions openmp/runtime/test/worksharing/for/omp_monotonic_env.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// RUN: %libomp-compile
// RUN: env OMP_SCHEDULE=monotonic:dynamic,50 %libomp-run monotonic dynamic 50
// RUN: env OMP_SCHEDULE=monotonic:guided,51 %libomp-run monotonic guided 51
// RUN: env OMP_SCHEDULE=monotonic:static,52 %libomp-run monotonic static 52
// RUN: env OMP_SCHEDULE=nonmonotonic:dynamic,53 %libomp-run nonmonotonic dynamic 53
// RUN: env OMP_SCHEDULE=nonmonotonic:guided,54 %libomp-run nonmonotonic guided 54

// The test checks OMP 5.0 monotonic/nonmonotonic OMP_SCHEDULE parsing
// The nonmonotonic tests see if the parser accepts nonmonotonic, if the
// parser doesn't then a static schedule is assumed

#include <stdio.h>
#include <string.h>
#include <omp.h>

int err = 0;

omp_sched_t sched_without_modifiers(omp_sched_t sched) {
return (omp_sched_t)((int)sched & ~((int)omp_sched_monotonic));
}

int sched_has_modifiers(omp_sched_t sched, omp_sched_t modifiers) {
return (int)sched & (int)modifiers;
}

// check that sched = hope | modifiers
void check_schedule(const char *extra, const omp_sched_t sched, int chunk,
omp_sched_t hope_sched, int hope_chunk) {

if (sched != hope_sched || chunk != hope_chunk) {
++err;
printf("Error: %s: schedule: (%d, %d) is not equal to (%d, %d)\n", extra,
(int)hope_sched, hope_chunk, (int)sched, chunk);
}
}

omp_sched_t str2omp_sched(const char *str) {
if (!strcmp(str, "dynamic"))
return omp_sched_dynamic;
if (!strcmp(str, "static"))
return omp_sched_static;
if (!strcmp(str, "guided"))
return omp_sched_guided;
printf("Error: Unknown schedule type: %s\n", str);
exit(1);
}

int is_monotonic(const char *str) { return !strcmp(str, "monotonic"); }

int main(int argc, char **argv) {
int i, monotonic, chunk, ref_chunk;
omp_sched_t sched, ref_sched;

if (argc != 4) {
printf("Error: usage: <executable> monotonic|nonmonotonic <schedule> "
"<chunk-size>\n");
exit(1);
}

monotonic = is_monotonic(argv[1]);
ref_sched = str2omp_sched(argv[2]);
ref_chunk = atoi(argv[3]);

omp_get_schedule(&sched, &chunk);

if (monotonic && !sched_has_modifiers(sched, omp_sched_monotonic)) {
printf("Error: sched (0x%x) does not have monotonic modifier\n",
(int)sched);
++err;
}
sched = sched_without_modifiers(sched);
if (sched != ref_sched) {
printf("Error: sched (0x%x) is not 0x%x\n", (int)sched, (int)ref_sched);
++err;
}
if (chunk != ref_chunk) {
printf("Error: chunk is not %d\n", ref_chunk);
++err;
}
if (err > 0) {
printf("Failed\n");
return 1;
}
printf("Passed\n");
return 0;
}
134 changes: 134 additions & 0 deletions openmp/runtime/test/worksharing/for/omp_monotonic_schedule_set_get.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// RUN: %libomp-compile-and-run

// The test checks OMP 5.0 monotonic/nonmonotonic scheduling API
// 1. initial schedule should be (static,0)
// 2. omp_get_schedule() should return the schedule set by omp_set_schedule()
// 3. schedules set inside parallel should not impact outer tasks' schedules

#include <stdio.h>
#ifndef __INTEL_COMPILER
#define _OMPIMP
#endif

#define NO_MODIFIERS ((omp_sched_t)0)

#include "omp.h"

int global = 0;
int err = 0;

omp_sched_t sched_append_modifiers(omp_sched_t sched, omp_sched_t modifiers) {
return (omp_sched_t)((int)sched | (int)modifiers);
}

omp_sched_t sched_without_modifiers(omp_sched_t sched) {
return (omp_sched_t)((int)sched & ~((int)omp_sched_monotonic));
}

int sched_has_modifiers(omp_sched_t sched, omp_sched_t modifiers) {
return (((int)sched & ((int)omp_sched_monotonic)) > 0);
}

// check that sched = hope | modifiers
void check_schedule(const char *extra, const omp_sched_t sched, int chunk,
omp_sched_t hope_sched, int hope_chunk) {

if (sched != hope_sched || chunk != hope_chunk) {
#pragma omp atomic
++err;
printf("Error: %s: schedule: (%d, %d) is not equal to (%d, %d)\n", extra,
(int)hope_sched, hope_chunk, (int)sched, chunk);
}
}

int main() {
int i;
int chunk;
omp_sched_t sched0;

omp_set_dynamic(0);
omp_set_nested(1);

// check serial region
omp_get_schedule(&sched0, &chunk);
#ifdef DEBUG
printf("initial: (%d, %d)\n", sched0, chunk);
#endif
check_schedule("initial", omp_sched_static, 0, sched0, chunk);
// set schedule before the parallel, check it after the parallel
omp_set_schedule(
sched_append_modifiers(omp_sched_dynamic, omp_sched_monotonic), 3);

#pragma omp parallel num_threads(3) private(i)
{
omp_sched_t n_outer_set, n_outer_get;
int c_outer;
int tid = omp_get_thread_num();

n_outer_set = sched_append_modifiers((omp_sched_t)(tid + 1),
omp_sched_monotonic); // 1, 2, 3

// check outer parallel region
// master sets (static, unchunked), others - (dynamic, 1), (guided, 2)
// set schedule before inner parallel, check it after the parallel
omp_set_schedule(n_outer_set, tid);

// Make sure this schedule doesn't crash the runtime
#pragma omp for
for (i = 0; i < 100; ++i) {
#pragma omp atomic
global++;
}

#pragma omp parallel num_threads(3) private(i) shared(n_outer_set)
{
omp_sched_t n_inner_set, n_inner_get;
int c_inner_set, c_inner_get;
int tid = omp_get_thread_num();

n_inner_set = (omp_sched_t)(tid + 1); // 1, 2, 3
c_inner_set = (int)(n_outer_set)*10 +
(int)n_inner_set; // 11, 12, 13, 21, 22, 23, 31, 32, 33
n_inner_set = sched_append_modifiers(n_inner_set, omp_sched_monotonic);
// schedules set inside parallel should not impact outer schedules
omp_set_schedule(n_inner_set, c_inner_set);

// Make sure this schedule doesn't crash the runtime
#pragma omp for
for (i = 0; i < 100; ++i) {
#pragma omp atomic
global++;
}

#pragma omp barrier
omp_get_schedule(&n_inner_get, &c_inner_get);
#ifdef DEBUG
printf("inner parallel: o_th %d, i_th %d, (%d, %d)\n", n_outer_set - 1,
tid, n_inner_get, c_inner_get);
#endif
check_schedule("inner", n_inner_set, c_inner_set, n_inner_get,
c_inner_get);
}

omp_get_schedule(&n_outer_get, &c_outer);
#ifdef DEBUG
printf("outer parallel: thread %d, (%d, %d)\n", tid, n_outer_get, c_outer);
#endif
check_schedule("outer", n_outer_set, tid, n_outer_get, c_outer);
}

omp_get_schedule(&sched0, &chunk);
#ifdef DEBUG
printf("after parallels: (%d, %d)\n", sched0, chunk);
#endif
check_schedule("after parallels",
sched_append_modifiers(omp_sched_dynamic, omp_sched_monotonic),
3, sched0, chunk);

if (err > 0) {
printf("Failed\n");
return 1;
}
printf("Passed\n");
return 0;
}