C ++: Как это возможно, что чтение данных может повлиять на память? - PullRequest
5 голосов
/ 28 апреля 2009

В последнее время я углубляюсь в C ++, и мои ошибки кажутся сложными.

У меня есть вектор объектов, каждый объект содержит вектор с плавающей точкой. Я решил, что мне нужно создать еще один плоский массив, содержащий все значения с плавающей точкой всех объектов в одном. Это немного сложнее, но суть проблемы в том, что когда я перебираю свои объекты, извлекая значения с плавающей запятой, в какой-то момент мой вектор объектов изменяется или искажается каким-то странным образом. (Все мои операции чтения являются константными функциями)

Другой пример был с MPI. Я только начинал, поэтому я просто хотел запустить один и тот же код на двух разных узлах со своей собственной памятью без передачи данных, все очень просто. К моему удивлению, я получил ошибки сегментации и после нескольких часов отслеживания обнаружил, что одно присваивание одной переменной устанавливало совершенно другую переменную в NULL.

Так что мне любопытно, как это возможно, что операции чтения могут повлиять на мои структуры данных. Точно так же, как внешне связанная операция может повлиять на другую. Я не мог ожидать решения моих проблем с этими краткими описаниями, но любой совет будет с благодарностью.

Обновление: Вот фрагмент кода, который я изначально не публиковал, потому что я не уверен, сколько из него можно извлечь, не разбираясь во всей системе.

Одна вещь, которую я только что узнал, состояла в том, что, когда я прекратил присваивать значение моему плоскому массиву и просто вместо этого выполнил cout'ed, ошибки сегмента исчезли. Поэтому, возможно, я объявляю свой массив неверным, но даже если бы я был не уверен, как это повлияет на вектор объекта.

void xlMasterSlaveGpuEA::FillFlatGenes() {
    int stringLength = pop->GetGenome(0).GetLength();
    for (int i=0;i<pop->GetPopSize();i++)
        for (int j=0;j<stringLength;j++)
            flatGenes[(i*stringLength)+j]<< pop->GetGenome(i).GetFloatGene(j);
}

float xlVectorGenome::GetFloatGene(unsigned int i) const {
    return GetGene(i);
}

мой плоский массив является функцией-членом

float * flatFitness;

инициализируется в конструкторе следующим образом:

flatFitness = new float(popSize);

Обновление 2:

Я просто хочу отметить, что два приведенных выше примера не связаны между собой, первый не многопоточный. Второй пример MPI - это технически, но MPI - это распределенная память, и я намеренно попытался сделать самую простую реализацию, какую только мог себе представить, - обе машины выполняли код независимо друг от друга. Однако есть одна дополнительная деталь, которую я вставил в условное высказывание

if node 1 then do bottom half of loop

if node 1 then do top half

Опять же, память должна быть изолирована, они должны работать так, как будто они ничего не знают друг о друге ... но удаление этого условия и выполнение обоих циклов для всех кубов устраняет ошибку

Ответы [ 3 ]

14 голосов
/ 28 апреля 2009

Это не конструктор массива:

float * flatFitness;
flatFitness = new float(popSize);

Здесь вы создаете один float в куче, инициализированный значением popSize. Если вам нужен массив с плавающей точкой, вам нужно использовать скобки вместо скобок:

float *flatFitness = new float[popSize];

Это может легко вызвать проблемы, которые вы описываете. Кроме того, помните, что при создании массивов вам необходимо удалить, используя delete [] (в конце концов):

delete [] flatFitness;

Если вы просто используете delete, это может сработать, но поведение не определено.

Если вы хотите вообще не использовать синтаксис массива, почему бы не использовать std::vector? Вы можете создать вектор элементов popSize следующим образом:

#include <vector>

std::vector<float> flatFitness(popSize);

Это будет автоматически освобождено, когда выпадет из области видимости, поэтому вам не нужно беспокоиться о new или delete.

Обновление (re: comment): Если вы уже используете std::vectors в другом месте своего кода, взгляните на std::vector::swap(). Вы можете вообще избежать копирования и просто поменять пару векторов назад и вперед между буферизацией для CUDA и обработкой, которую вы здесь выполняете.

0 голосов
/ 28 апреля 2009

jeffamaphone может быть прав, что это проблема с потоками. Другая возможность состоит в том, что объекты, которые вы читаете, уже были удалены. Вы бы тогда читали с неверного адреса. Также возможно, что структуры данных, в которые вы пишете в это время, хранятся в том же месте, где ранее находились векторы. Это может привести к описанному вами поведению.

РЕДАКТИРОВАТЬ (в зависимости от вашего обновления):

Это может быть неисправно: stringLength инициализируется вне внешнего цикла, но похоже, что его нужно обновить во время этого внешнего цикла:

int stringLength = pop->GetGenome(0).GetLength();
for (int i=0;i<pop->GetPopSize();i++)
    for (int j=0;j<stringLength;j++)
        flatGenes[(i*stringLength)+j]<< pop->GetGenome(i).GetFloatGene(j);

Предлагаемое исправление:

for (int i=0;i<pop->GetPopSize();i++) {
    int stringLength = pop->GetGenome(i).GetLength();
    for (int j=0;j<stringLength;j++) {
        flatGenes[(i*stringLength)+j]<< pop->GetGenome(i).GetFloatGene(j);
    }
}
0 голосов
/ 28 апреля 2009

Я подозреваю, что у вас проблемы с многопоточностью или повреждением памяти, о которых вы не знали. Поведение, которое вы описываете, не является каким-либо стандартным, заведомо желательным поведением.

...