Возможная утечка памяти в программе C ++ MPI? - PullRequest
0 голосов
/ 24 октября 2011

Я пишу код C ++ MPI для класса параллельных вычислений.Мой код работает, и я изменил назначение, но код использует намного больше памяти, чем я ожидал.По мере увеличения числа процессоров требования к памяти на узел быстро растут.Это первая настоящая программа на C / C ++ или MPI, которую мне когда-либо приходилось писать, поэтому я думаю, что где-то произошла утечка памяти.Может кто-нибудь взглянуть на этот код и сказать мне, где?Всякий раз, когда я создаю переменную, используя new, я удаляю ее, поэтому я не уверен, что еще мне нужно искать.Я полагаю, что некоторые проблемы могут быть вызваны объектами, которые я создаю, но следует ли вызывать деструкторы для этих объектов в конце их области действия, чтобы освободить память, выделенную в куче?Я родом из Java, и большая часть моего C / C ++ самоучка, поэтому мне сложно справиться с управлением собственной памятью.

Проблема очень проста.У меня есть матрица (хранится как одномерный вектор) размером MSIZE * MSIZE.Каждый процессор отвечает за некоторый непрерывный блок данных.Затем я запускаю 500 итераций, где каждый некраевой элемент A[r][c] установлен на максимум A[r][c], A[r+1][c], A[r-1][c], A[r][c+1], A[r-1][c-1].Новое значение A[r][c] не сохраняется до тех пор, пока не завершится весь процесс обновления для этих итераций.Процессоры должны передавать значения, находящиеся на границах, другим процессорам.

Вот мой код (я думаю, что проблема возникает где-то здесь, но если вы хотите увидеть остальную часть кода (в основном вспомогательные функции и функции инициализации), дайте мне знать, и я опубликую его):

#include <math.h> 
#include "mpi.h" 
#include <iostream>
#include <float.h>
#include <math.h>
#include <assert.h>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
using namespace std;

#define MSIZE 4000
#define TOTAL_SIZE (MSIZE * MSIZE)
#define NUM_ITERATIONS 500

int myRank;
int numProcs;
int start, end;
int numIncomingMessages;

double startTime;

vector<double> a;

map<int, set<int> > neighborsToNotify;


/*
 * Send the indices that have other processors depending on them to those processors.
 * Once the messages have been sent, receive messages until we've received all the messages
 * we are expecting to receive.
 */
void doCommunication(){
    int messagesReceived = 0;
    map<int, set<int> >::iterator iter;
    for(iter = neighborsToNotify.begin(); iter != neighborsToNotify.end(); iter++){
        int destination = iter->first;
        set<int> indices = iter->second;

        set<int>::iterator setIter;
        for(setIter = indices.begin(); setIter != indices.end(); setIter++){
            double val = a.at(*setIter);
            MPI_Bsend(&val, 1, MPI_DOUBLE, destination, *setIter, MPI_COMM_WORLD);
        }

        MPI_Status s;
        int flag;
        MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &s);
        while(flag){
            double message;
            MPI_Recv(&message, 1, MPI_DOUBLE, s.MPI_SOURCE, s.MPI_TAG, MPI_COMM_WORLD, &s);
            a.at(s.MPI_TAG) = message;
            messagesReceived++;
            MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &s);
        }

    }

    while(messagesReceived < numIncomingMessages){
        MPI_Status s;
        MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &s);
        double message;
        MPI_Recv(&message, 1, MPI_DOUBLE, s.MPI_SOURCE, s.MPI_TAG, MPI_COMM_WORLD, &s);
        a.at(s.MPI_TAG) = message;
        messagesReceived++;
    }
}

/*
 * Perform one timestep of iteration.
 */
void doIteration(){
    int pos;
    vector<double> temp;
    temp.assign(end - start + 1, 0);
    for(pos = start; pos <= end; pos++){
        int i;
        double max;

        if(isEdgeNode(pos))
            continue;

        int dependents[4];
        getDependentsOfPosition(pos, dependents);

        max = a.at(pos);

        for(i = 0; i < 4; i++){
            if(isInvalidPos(dependents[i]))
                continue;

            max = std::max(max, a.at(dependents[i]));
        }

        temp.at(pos - start) = max;
    }

    for(pos = start; pos <= end; pos++){
        if(! isEdgeNode(pos)){
            a.at(pos) = temp.at(pos - start);
        }
    }
}

/*
 * Compute the checksum for this processor
 */
double computeCheck(){
    int pos;
    double sum = 0;
    for(pos = start; pos <= end; pos++){
        sum += a.at(pos) * a.at(pos);
    }
    return sum;
}

int main(int argc, char *argv[]) {
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
    MPI_Comm_size(MPI_COMM_WORLD, &numProcs);

    findStartAndEndPositions();

    initializeArray();

    findDependentElements();

    MPI_Barrier(MPI_COMM_WORLD);

    if(myRank == 0){
        startTime = MPI_Wtime();
    }

    int i;
    for(i = 0; i < NUM_ITERATIONS; i++){
        if(myRank == 0)
            cout << ".";
        doCommunication();
        MPI_Barrier(MPI_COMM_WORLD);
        doIteration();
    }

    double check = computeCheck();
    double receive = 0;

    MPI_Reduce(&check, &receive, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

    if(myRank == 0){
        cout << "n = " << MSIZE << " and p = " << numProcs << "\n";
        cout << "The total time was: " << MPI_Wtime() - startTime << " seconds \n";
        cout << "The checksum was: " << receive << " \n";
    }

    MPI_Finalize();
    return 0;
}

1 Ответ

0 голосов
/ 26 октября 2011

Не думаю, что у вас утечка памяти. Но вы можете проверить это с помощью Valgrind. Имейте в виду, что результат выглядит ужасно.

 mpirun -n8 valgrind ./yourProgram

Я думаю, что причина в MPI. Вы используете буферизованную отправку, поэтому каждый узел будет генерировать собственный буфер, чем больше у вас узлов, тем больше будет сгенерировано буфера. Чтобы убедиться, что ваш алгоритм масштабируется относительно памяти, используйте небуферизованную отправку (только для целей отладки, поскольку это убьет ваше ускорение). Или попробуйте увеличить матрицу, в данный момент вы используете только 112 МБ, что не является проблемой для распараллеливания. Попробуйте найти некоторый размер, чтобы использовать почти всю память одного узла.

...