我修改了一个非常好的omp并行代码来执行我在这里找到的数值积分。然而,一些质谱分析显示,存在一些严重的内存泄漏。。。我想这与如何处理从堆栈中弹出的元素有关,其中堆栈上的顶部元素被返回,但没有从堆栈中删除。
所以我想,我可以很容易地添加这个缺失的功能,就像我以前在串行代码中做的那样。但我很奇怪地做错了什么,因为在几次"释放"之后,我得到了一个"无效指针错误"。。。
既然我是OMP的新手,我想我可以向你们寻求帮助。我真的很抱歉,我没能再做我的"榜样"。但我认为,了解作者是如何实现链表以及哪些数据结构是很重要的。
我想下面的大部分代码可能会被跳过,对我来说很好。当我试图在函数"pop_stack"的最后"释放"与堆栈顶部元素相关的数据时,问题就出现了。
以串行方式弹出元素似乎很好——在并行部分中则不然。你能发现错误吗?
注意:这是不完整的代码。它不会编译。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>
#include <math.h>
int num_threads;
#define INITIAL_STACK_SIZE 128
/* the stack structure */
struct stack_s{
int el_count; /* count of elements on stack */
int el_size; /* size of an element */
int mem_reserve; /* allocated memory for stack */
void* elements; /* pointer to begin of stack */
};
struct myparams { double c1; double c1;};
typedef struct _work_t{
double a;
double b;
double rec; /* max recursion depth */
struct myparams * p;
} work_t;
typedef struct stack_s* stack_t;
void create_stack(stack_t* stack, int element_size);
int empty_stack(stack_t stack);
void push_stack(stack_t stack, void* element);
void pop_stack(stack_t stack, void* element);
double do_something(
double (*f)(double,struct myparams*), /* function to be called */
double a,
double b,
int rec, /*max. recursion depth*/
struct myparams* p);
static double myfun(double x,struct myparams* p){ return exp(-x*x)/p->a+log(x*pow(p->b,2.0));}
int main(int argc,char** argv)
{
num_threads=omp_get_num_threads();
double a=somevalue;
double b=somevalue;
struct myparams pinit;
pinit.c1=someval;
pinit.c2=someval;
double answer=0;
#pragma omp parallel
{
answer = do_something(myfun, xmin, xmax, 100, &pinit);
} /* omp parallel */
return 0;
}
double do_something(
double (*f)(double,struct myparams*), /* function to be called */
double a,
double b,
int rec,
struct myparams* p)
{
stack_t stack;
work_t work;
int ready, idle, busy;
/* prepare stack */
work.a = a;
work.b = b;
work.rec=rec;
work.p=p;
create_stack(&stack, sizeof(work_t));
push_stack(stack, &work);
double result = 0.0;
busy = 0;
#pragma omp parallel default(none)
shared(stack, result,f,busy)
private(work, idle, ready)
{
ready = 0;
idle = 1;
while(!ready )
{
#pragma omp critical (stack)
{
if (!empty_stack(stack)){
/* we have new work */
pop_stack(stack, &work);
if (idle){
/* say others i'm busy */
busy += 1;
idle = 0;
}
}else{
/* no new work on stack */
if (!idle){
busy -= 1;
idle = 1;
}
/* nobody has anything to do; let us leave the loop */
if (busy == 0)
ready = 1;
}
} /* end critical(stack) */
if (idle)
continue;
/* do some calculations using values saved in work and as well as the function f
along with the function parameters saved in myparams
-> estimate an error & save it to 'delta' */
if(rec <= 0 || fabs(delta) <= global_tolerance)
{
//error acceptable
#pragma omp critical (result)
result += somevalue_computed_above;
}
else // error not acceptable
{
//push 2 new work-elements to stack
//prepare 1st new elem.
work.a = some_new_a;
work.b = some_new_b;
work.rec=rec-1;
#pragma omp critical (stack)
{
push_stack(stack, &work);
//prepare 2nd new element
work.a = some_new_a2;
work.b = some_new_b2;
work.rec=rec-1;
push_stack(stack, &work);
}
}
} /* while */
} /* end omp parallel */
return result;
}
/******************************************
* create new stack
******************************************/
void
create_stack(
stack_t* stack, /* stack to create */
int element_size) /* size of a stack element */
{
int initial_size = INITIAL_STACK_SIZE;
/* allocate memory for new stack struct */
(*stack) = (stack_t) malloc(sizeof(struct stack_s));
if (!(*stack)){
fprintf(stderr, "error: could not allocate memory for stack.. Abort.n");
exit(1);
}
/* allocate memory for stack elements */
(*stack)->elements = (void*) malloc(element_size * initial_size);
(*stack)->mem_reserve = initial_size;
if (!(*stack)->elements){
fprintf(stderr, "error: could not allocate memory for stack.. Abort.n");
exit(1);
}
(*stack)->el_size = element_size;
(*stack)->el_count = 0;
}
/*****************************************
* check if the stack is empty
*****************************************/
int
empty_stack
(stack_t stack)
{
return stack->el_count <= 0;
}
/*****************************************
* push a element on stack
*****************************************/
void
push_stack(
stack_t stack, /* target stack */
void* element) /* element to push */
{
int i, new_reserve;
int log2_count;
/* check if we need more memory for stack */
if (stack->el_count >= stack->mem_reserve){
/* calculate new size for the stack
it should be a power of two */
for (i = stack->el_count, log2_count = 0;
i > 0;
i>>1, log2_count++);
new_reserve = 1 << log2_count;
/* reallocate memory for phase thread tables
and nullify new values */
stack->elements = (void *) realloc(stack->elements,
stack->el_size * new_reserve);
if (!stack->elements){
fprintf(stderr, "error: can't reallocate stack.. Abortingn");
exit(1);
}
stack->mem_reserve = new_reserve;
}
/* now push the element on top of the stack */
memcpy((char*)stack->elements + stack->el_count*stack->el_size,
element, stack->el_size);
stack->el_count++;
}
/*****************************************
* pop an element from stack
* THIS IS WHERE I SUSPECT A MISTAKE !
*****************************************/
void pop_stack(
stack_t stack, /* target stack */
void* element) /* where poped el. should be stored */
{
if (stack->el_count <= 0){
fprintf(stderr, "error: trying to pop from empty stack.n");
exit(2);
}
stack->el_count--;
memcpy(element,
(char*)stack->elements + stack->el_count*stack->el_size,
stack->el_size);
// try to remove last element from stack
// in original code there was no cleanup
struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
printf("ncount:%d, foo:%fn",stack->el_count+1,tmp->a);
free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
}
所以看起来pop_stack()
只在一些与stack
相关的关键区域内调用,所以我们可以不再担心数据竞争了。
您识别代码的这一部分:
/*****************************************
* pop an element from stack
* THIS IS WHERE I SUSPECT A MISTAKE !
*****************************************/
void pop_stack(
stack_t stack, /* target stack */
void* element) /* where poped el. should be stored */
{
if (stack->el_count <= 0){
fprintf(stderr, "error: trying to pop from empty stack.n");
exit(2);
}
stack->el_count--;
memcpy(element,
(char*)stack->elements + stack->el_count*stack->el_size,
stack->el_size);
// try to remove last element from stack
// in original code there was no cleanup
struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
printf("ncount:%d, foo:%fn",stack->el_count+1,tmp->a);
free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
}
作为问题的可能根源。因此,push_stack()
清楚地将element
的stack->el_size
字节复制到堆栈,pop_stack()
再次将它们复制回来。这一切看起来都很棒。然而,pop_stack()
的最后一部分有点令人费解。。。
struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
printf("ncount:%d, foo:%fn",stack->el_count+1,tmp->a);
free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
stack->elements
是指向第一个元素的第一个字节的void*
指针,因此将stack->el_count
添加到该指针不会,从而为您提供刚刚弹出的元素的地址,除非是stack->el_count == 0
!!因此tmp
被设置为无意义值,因此tmp->a
也是无意义的。至于free(tmp)
。。。只有当el_count == 0
(现在(时,free()
不会失败,但它会破坏stack
。
从stack
的工作方式来看,我并不认为pop_stack()
需要进行任何"清理"。如果你认为它确实存在,那么你需要重新考虑它到底需要什么。有可能你正在使用stack
作为指向"东西"的指针堆栈。。。但无论如何,还有一点工作要做。