1

I'm using OpenMP to parallelize a for loop. This program uses the Gurobi C API.

GRBupdatemodel(model);

#pragma omp parallel for
for (int batch = 0; batch < n_batch; ++batch)
{

    int cind[1 + n_max];
    for (int j = 0; j < n_max; ++j)
    {
        cind[1 + j] = n_sum + j;
    }

    double cval[1 + n_max];
    cval[0] = 1;

    GRBmodel* model_copy = GRBcopymodel(model);
    for (int i = 0; i < n_sum; ++i)
    {
        cind[0] = i;
        for (int k = 0; k < n_min; ++k)
        {
            for (int j = 0; j < n_max; ++j) 
            {
                cval[1 + j] = -*((double*) PyArray_GetPtr(array, (long []) {batch, i, j, k}));
            }
            GRBaddconstr(model_copy, 1 + n_max, cind, cval, GRB_LESS_EQUAL, 0, NULL);
        }
    }
    GRBoptimize(model_copy);
    GRBgetdblattrarray(model_copy, GRB_DBL_ATTR_X, 0, n_sum, values[batch]);
    GRBgetdblattrarray(model_copy, GRB_DBL_ATTR_X, n_sum, n_max, max_strategy[batch]);
    GRBgetdblattrarray(model_copy, GRB_DBL_ATTR_PI, 1, n_sum * n_min, (double *) min_strategies[batch]);
    GRBfreemodel(model_copy);
}

Each iteration of this loop writes to a different section of the arrays values, max_strategy, and min_strategies.

Removing #pragma omp parallel for yields correct results. Adding it yields incorrect results, including nan values. Thus I suspect there is a race condition in my loop, but I haven't been able to find it. Does anyone know what might be going wrong? I only see two types of writes in my loop body:

  • To cind, cval, and model_copy (by GRBgetdblattrarray), which are local variables declared inside the loop body and therefore private to each thread.
  • To non-overlapping sections of values, max_strategy, and min_strategies.
2
  • It could be an issue with the library. I recommend you contact support, as we cannot examine the source (afaik) to verify a race condition or not Commented Dec 28, 2019 at 0:10
  • What happens if you pre allocate all your temporary copies of the model? It’s hard to say what the library functions do but the copy method always acts on the same object from all threads. I know a copy should be Thread safe in the ideal case. I wouldn’t bet on it though. Commented Dec 28, 2019 at 16:13

1 Answer 1

2
+50

https://www.gurobi.com/documentation/8.1/refman/py_env2.html

According to the docs the models in each loop iteration are not independent copies. They share the same environment variable. This means they potentially read/write from the same memory. If you ensure each copy has its own environment it might succeed.

I see you are calling this code from python. If you only use C is to use multithreaded computation I would recommend using pure python code with joblib or dask to have the same result. The race condition would also be apparent in python as the different models cannot be serialized. See this support question

Sign up to request clarification or add additional context in comments.

1 Comment

I ended up creating a separate model from scratch with GRBnewmodel inside the loop body instead of copying from a template model with GRBcopymodel. It seems to work properly and is faster than the non-parallel solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.