Я разрабатываю одну программу на C ++, которая вызывает процедуру на Fortran 77. Основная программа C ++ может работать многопоточно. Однако бывает, что подпрограмма Fortran 77 скрывает несколько общих блоков, которые модифицируются при каждом вызове в зависимости от его аргументов.
Я боюсь, что все общие блоки могут быть разделены между несколькими потоками, и что одновременный доступ к этим блокам, вероятно, испортит все.
Первый вопрос : Я прав? Будут ли общие блоки распределены между несколькими потоками?
Второй вопрос : Есть ли простой способ избежать этого? Переписать процедуры на Фортране кажется невозможным, я скорее ищу способ, чтобы у каждого потока была своя копия всех общих блоков (которые не большие, их следует быстро копировать). Я не знаю, поможет ли вариант компиляции или OpenMP может мне помочь.
Да, общие блоки являются общими.
В OpenMP можно указать общий блок как THREADPRIVATE. Каждый поток затем динамически создает новый экземпляр общего блока. Чтобы скопировать данные из оригинала, используйте спецификатор COPYIN. Смотрите также Разница между частным OpenMP и частным
Основной синтаксис
!$OMP THREADPRIVATE (/cb/, ...)
где cb — это имя общего блока. Увидеть https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE
Вы правы в том, что общие блоки не являются потокобезопасными. Это глобальные данные, которые позволяют вам объявлять переменные в любом блоке области видимости, которые имеют общую ассоциацию хранения. Эффект, по сути, тот же, если вы писали в глобальные переменные в C ++ со всеми проблемами синхронизации потоков, которые могли бы вызвать.
К сожалению, я не думаю, что есть простой способ избежать этого. Если вам нужно поддерживать многопоточный подход, одна идея, которую я видел в прошлом, заключается в том, чтобы переместить все переменные из общего блока в определенный пользователем тип и передать экземпляры этого типа в любую процедуру, требующую доступа. для них (один экземпляр на поток). Это может повлечь за собой потенциально дорогостоящие изменения в коде для реализации, хотя.
Вам также необходимо рассмотреть другие проблемы безопасности потоков в коде Фортрана (это не исчерпывающий список):
SAVE
Атрибут (неявный в переменных модуля и в переменных, инициализированных при объявлении) проблематичен (эти переменные постоянны между вызовами процедур). Неявность этого атрибута также зависит от компилятора / стандарта, что делает его еще более потенциальной проблемой.RECURSIVE
Атрибут — это означает, что функция является входной. Это также может быть выполнено путем компиляции с опцией компилятора openmp вместо изменения кода.Другой путь, который вы могли бы изучить, — это использовать многопроцессорную обработку или передачу сообщений для параллелизации кода, а не многопоточности. Это позволяет избежать проблем безопасности потока с вашим кодом на Фортране, но представляет другое потенциально дорогое изменение архитектуры кода.
Также см:
Да, вы не можете использовать общие области с многопоточностью. И нет, избежать этого невозможно. Все общие области фактически объединяются компоновщиком в один блок, и нет способа скопировать его между потоками. Это известная проблема везде, где существует устаревший код Fortran. Наиболее распространенным решением является использование многопроцессорной обработки вместо многопоточности.
Спасибо за ваши ответы, особенно намек на OpenMP, это действительно выполнимо. Я сделал небольшую программу, чтобы быть полностью уверенным. Он состоит из одной части на Фортране 77, которая вызывается в одной основной программе на C ++ (это мое дело):
Фортран 77 процедур func.f :
subroutine set(ii, jj)
implicit none
include "func.inc"integer ii, jj
integer OMP_GET_NUM_THREADS, OMP_GET_THREAD_NUM
i = ii + 1
j = jj
!$OMP CRITICAL
print *, OMP_GET_THREAD_NUM(), OMP_GET_NUM_THREADS(), i, j
!$OMP END CRITICAL
return
endsubroutine func(n, v)
implicit none
include "func.inc"
integer n, k
integer v(n)
do k = i, j
a = k + 1
b = a * a
c = k - 1
v(k) = b - c * c
enddo
return
end
с включаемым файлом func.inc
integer i, j
integer a, b, c
common /mycom1/ i, j
!$OMP THREADPRIVATE(/mycom1/)
common /mycom2/ a, b, c
!$OMP THREADPRIVATE(/mycom2/)
и, наконец, программа C ++ main.cpp :
#include<iostream>
#include<sstream>
#include<vector>
using namespace std;
#include<omp.h>
extern "C"{
void set_(int*, int*);
void func_(int*, int*);
};int main(int argc, char *argv[])
{
int nthread;
{
istringstream iss(argv[1]);
iss >> nthread;
}
int n;
{
istringstream iss(argv[2]);
iss >> n;
}
vector<int> a(n, -1);
#pragma omp parallel num_threads(nthread) shared(a)
{
const int this_thread = omp_get_thread_num();
const int num_threads = omp_get_num_threads();
const int m = n / num_threads;
int start = m * this_thread;
int end = start + m;
const int p = n % num_threads;
for (int i = 0; i < this_thread; ++i)
if (p > i) start++;
for (int i = 0; i <= this_thread; ++i)
if (p > i) end++;
#pragma omp critical
{
cout << "#t " << this_thread << " : [" << start
<< ", " << end << "[" << endl;
}
set_(&start, &end);
func_(&n, a.data());
}
cout << "[ " << a[0];
for (int i = 1; i < n; ++i)
cout << ", " << a[i];
cout << "]" << endl;
ostringstream oss;
for (int i = 1; i < n; ++i)
if ((a[i] - a[i - 1]) != int(4))
oss << i << " ";
if (! oss.str().empty())
cout << "<<!! Error occured at index " << oss.str()
<< " !!>>" << endl;
return 0;
}
Шаги компиляции (gcc версии 4.8.1):
gfortran -c func.f -fopenmp
g++ -c main.cpp -std=gnu++11 -fopenmp
g++ -o test main.o func.o -lgfortran -fopenmp
Вы можете запустить его следующим образом:
./test 10 1000
где
Цель этой программы — разделить этот вектор между потоками.
и позволить каждой нити заполнить одну ее часть.
Заполнение вектора производится внутри фортрана 77:
Обычно, если нет ошибок и если общие блоки fortran 77 не являются общими, конечный вектор должен быть заполнен значениями 4 * k, k от 1 до 1000.
Я не мог поймать программу. И наоборот, если я удаляю директивы Fortran 77 OMP в func.inc, тогда общие блоки больше не являются частными, и возникает множество ошибок.
Итак, в заключение, единственное, что мне нужно сделать, чтобы решить мою первоначальную проблему, это добавить директивы OMP сразу за любыми общими блоками, что, надеюсь, не сложно, поскольку все они собраны в один включаемый файл (например, мой тест).
Надеется, что это помогает.
С наилучшими пожеланиями.