ОпенМП:Причины повреждения кучи, кто-нибудь?
-
16-09-2019 - |
Вопрос
РЕДАКТИРОВАТЬ: Я могу запустить одну и ту же программу дважды одновременно без каких-либо проблем. Как я могу продублировать это с помощью OpenMP или каким-либо другим методом?
Это основная структура проблемы.
//Defined elsewhere
class SomeClass
{
public:
void Function()
{
// Allocate some memory
float *Data;
Data = new float[1024];
// Declare a struct which will be used by functions defined in the DLL
SomeStruct Obj;
Obj = MemAllocFunctionInDLL(Obj);
// Call it
FunctionDefinedInDLL(Data,Obj);
// Clean up
MemDeallocFunctionInDLL(Obj);
delete [] Data;
}
}
void Bar()
{
#pragma omp parallel for
for(int j = 0;j<10;++j)
{
SomeClass X;
X.Function();
}
}
Я проверил, что при попытке освободить часть памяти через MemDeallocFunctionInDLL()
, _CrtIsValidHeapPointer()
утверждение терпит неудачу.
Это потому, что оба потока пишут в одну и ту же память?
Итак, чтобы это исправить, я решил сделать SomeClass
личное (это мне совершенно чуждо, поэтому любая помощь приветствуется).
void Bar()
{
SomeClass X;
#pragma omp parallel for default(shared) private(X)
for(int j = 0;j<10;++j)
{
X.Function();
}
}
И сейчас он терпит неудачу, когда пытается выделять память в начале для Data
.
Примечание: При необходимости могу внести изменения в DLL.
Примечание: Он прекрасно работает без #pragma omp parallel for
РЕДАКТИРОВАТЬ: Сейчас Bar
выглядит так:
void Bar()
{
int j
#pragma omp parallel for default(none) private(j)
for(j = 0;j<10;++j)
{
SomeClass X;
X.Function();
}
}
Все еще не повезло.
Решение
Ознакомьтесь с MemAllocFunctionInDLL, FunctionDefinedInDLL, MemDeallocFunctionInDLL. потокобезопасный, или вновь поступающий.Другими словами, являются ли эти функции статическими переменными или общими переменными?В таком случае вам необходимо убедиться, что эти переменные не повреждены другими потоками.
Тот факт, что без omp-for все в порядке, может означать, что вы неправильно написали некоторые функции для обеспечения потокобезопасности.
Я хотел бы посмотреть, какие функции выделения/освобождения памяти использовались в Mem(Alloc|Dealloc)FunctionInDLL.
Добавлен:Я почти уверен, что ваши функции в DLL не являются потокобезопасными.Вы можете без проблем запускать эту программу одновременно.Да, все должно быть в порядке, если только ваша программа не использует общесистемные общие ресурсы (такие как глобальная память или общая память между процессами), что случается очень редко.В этом случае в потоках нет общих переменных, поэтому ваша программа работает нормально.
Но вызов этих функций в многопоточность (то есть в одном процессе) приводит к сбою вашей программы.Это означает, что в потоках есть некоторые общие переменные, и они могли быть повреждены.
Это не проблема OpenMP, а просто ошибка многопоточности.Эту проблему можно было бы решить просто.Пожалуйста, проверьте функции DLL, можно ли их одновременно вызывать из многих потоков.
Как приватизировать статические переменные
Допустим, у нас есть такие глобальные переменные:
static int g_data;
static int* g_vector = new int[100];
Приватизация – это не что иное, как создание частный копия для каждой темы.
int g_data[num_threads];
int* g_vector[num_threads];
for (int i = 0; i < num_threads; ++i)
g_vector[i] = new int[100];
И тогда любые ссылки на такие переменные
// Thread: tid
g_data[tid] = ...
.. = g_vector[tid][..]
Да, это довольно просто.Однако этот вид кода может иметь ложный обмен проблема.Но ложное разделение — это вопрос производительности, а не правильности.
Во-первых, просто попробуйте приватизировать любые статические и глобальные переменные.Затем проверьте его правильность.Затем посмотрите, какое ускорение вы получите.Если ускорение масштабируемо (скажем, в 3,7 раза быстрее на четырехъядерном процессоре), то все в порядке.Но в случае низкого ускорения (например, двукратного ускорения на четырехъядерном процессоре) вы, вероятно, столкнетесь с проблемой ложного разделения.Чтобы решить проблему ложного совместного использования, все, что вам нужно сделать, это просто добавить некоторые дополнения в структуры данных.
Другие советы
Вместо
delete Data
ты должен написать
delete [] Data;
Где бы вы ни делали new [], обязательно используйте delete [].
Похоже, ваша проблема не специфична для openmp.Вы пытались запустить приложение без включения #pragma Parallel?
default(shared) означает, что все переменные совместно используются потоками, а это не то, что вам нужно.Измените это на значение по умолчанию (нет).
Private(X) создаст копию X для каждого потока, однако ни один из них не будет инициализирован, поэтому не обязательно выполнять какую-либо конструкцию.
Я думаю, вам лучше использовать свой первоначальный подход: поставить точку останова в вызове Dealloc и посмотреть, что такое указатель памяти и что он содержит.Вы можете увидеть защитные байты чтобы определить, была ли память перезаписана в конце одного вызова или после потока.
Кстати, я предполагаю, что это сработает, если вы запустите его один раз, без цикла omp?