带OMP的C中的链表:内存问题



我修改了一个非常好的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()清楚地将elementstack->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作为指向"东西"的指针堆栈。。。但无论如何,还有一点工作要做。

最新更新