Связанный список в C с OMP: проблемы с памятью - PullRequest
0 голосов
/ 18 февраля 2020

Я адаптировал очень хороший 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.. Aborting\n");
        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:%f\n",stack->el_count+1,tmp->a);
  free(tmp); //Works as long as el_count == 1 but fails if it becomes 2

}

1 Ответ

1 голос
/ 18 февраля 2020

Похоже, что 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:%f\n",stack->el_count+1,tmp->a);
  free(tmp); //Works as long as el_count == 1 but fails if it becomes 2
}

как возможное место проблемы. Таким образом, push_stack() четко копирует stack->el_size байтов element в стек, а pop_stack() копирует их обратно. Это все выглядит замечательно. Однако последняя часть pop_stack() представляет собой небольшую загадку ...

  struct _work_t *tmp = (struct _work_t*) stack->elements+stack->el_count;
  printf("ncount:%d, foo:%f\n",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() not , но это уничтожит stack.

Взгляд на путь stack работает не означает, что pop_stack() необходимо выполнить «очистку». Если вы думаете, что это так, то вам нужно пересмотреть, что это действительно должно быть. Возможно, вы используете stack в качестве стека указателей на «вещи» ... но в любом случае, предстоит еще немного поработать.

...