【问题标题】:C++: How is it possible that reading data can affect memory?C++:读取数据怎么可能影响内存?
【发布时间】:2010-10-22 04:23:57
【问题描述】:

我最近一直在深入研究 C++,我的错误似乎变得复杂。

我有一个对象向量,每个对象都包含一个浮点向量。我决定我需要创建一个进一步的平面数组,其中包含所有对象的所有浮点值。它比这要复杂一些,但问题的要点是,当我遍历对象以提取浮点值时,在某些时候,我的对象向量会发生变化,或者以某种奇怪的方式损坏。 (我的读操作都是const函数)

另一个例子是 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

再次,内存应该被隔离,它们应该像彼此一无所知一样工作.. 但是删除这个条件并使两个循环都执行所有立方体,消除了错误

【问题讨论】:

  • 您可以发布您的代码的精简版吗?仅凭这一点很难判断会发生什么。
  • 你能发布循环代码来迭代向量中的对象......可能你在那里踩到内存?
  • 读取操作不会改变值。您的代码中必须有其他内容。您的第二个示例是变量指向错误位置的典型情况。

标签: c++ mpi corruption


【解决方案1】:

这不是数组构造函数:

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

您在此处的堆上创建一个浮点数,并使用值popSize 进行初始化。如果你想要一个浮点数组,你需要使用括号而不是括号:

float *flatFitness = new float[popSize];

这很容易导致您描述的问题。另外,请记住,当您创建数组时,您需要使用delete [] 删除(最终):

delete [] flatFitness;

如果您只使用delete,它可能会起作用,但行为未定义。

如果您想完全避免使用数组语法,为什么不使用std::vector?您可以像这样创建 popSize 元素的向量:

#include <vector>

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

这将在超出范围时自动释放,因此您不必担心newdelete

更新(回复:评论):如果您已经在代码的其他地方使用了std::vectors,请查看std::vector::swap()。您也许可以完全避免复制内容,只需在 CUDA 缓冲和您在此处进行的处理之间来回交换几个向量。

【讨论】:

  • 有趣的是,我正在从 std::vectors 中提取数据,因为我需要通过 CUDA 将其发送到 GPU。可能有更优雅的方式
  • 看看vector::swap()。您也许可以在此处实例化一些向量,然后将它们与您用于 CUDA 的向量交换,这样您就不必进行任何复制。只需使用 resize() 或构造函数(如上)确保它们的大小正确,否则您最终可能会写入您没有的内存。
【解决方案2】:

我怀疑您遇到了您不知道的多线程或内存损坏问题。你描述的行为不是任何一种标准的、设计的、理想的行为。

【讨论】:

  • 是的,这听起来像是缺少内存屏障和线程间同步不足的教科书式案例。在数据对象对另一个线程可见之后,只需要一个线程在某个时间点更新数据对象,并且缺乏同步迟早会咬你。
  • 也许,但他从未说过他在使用线程。 MPI 是进程级并行性,除非您将它与其他东西结合使用。
【解决方案3】:

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);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    相关资源
    最近更新 更多