【问题标题】:Vector push_back incredibly slow矢量 push_back 非常慢
【发布时间】:2015-01-08 03:36:35
【问题描述】:

因此,出于性能原因以及未来使用 CUDA 并行化某些内容的意图,我正在将我编写的 Java 代码转换为 C++ 代码。然而,我想做的第一件事是直接转换,让它在 C++ 中运行,代码与在 java 中的代码相同。

我遇到的问题是,下面的循环在 C++ 中实际上需要几分钟才能完成,而在 Java 中几乎不需要任何时间。唯一的区别是我在 C++ 中使用向量,而在 Java 中使用 ArrayList

当我最初创建单元向量时,我还为邻居向量保留了适当的大小。这段代码的目的是在 3d 立方体中创建一个统一的单元格网格,并将每个单元格的邻居存储在单元格本身内部,以便以后方便。

我使用 Visual Studio 2013 以防万一(对于 C++)和 Eclipse for java。

我觉得我在这里肯定遗漏了一些简单的东西,因为这样的减速似乎很疯狂,但是当我注释掉 push_back 时,代码基本上会立即执行。

whd 都是 20cellsCell 结构的向量(见下文)。

for (int i = 0; i < w; i++) {
    for (int j = 0; j < h; j++) {
        for (int k = 0; k < d; k++) {
            for (int x = -1; x < 2; x++) {
                for (int y = -1; y < 2; y++) {
                    for (int z = -1; z < 2; z++) {
                        if (i + x >= 0 && i + x < w && j + y >= 0 && j + y < h && k + z >= 0 && k + z < d) {
                            cells[i][j][k].addNeighbor(cells[i + x][j + y][k + z]);
                        }
                    }
                }
            }
        }
    }
}

在不同的文件中定义:

struct Cell {
    std::vector<Particle> particles;
    std::vector<Cell> neighbors;
    int b = 0;

    void addParticle(Particle &p) {
        particles.push_back(p);
    }

    void addNeighbor(Cell &c) {
        neighbors.push_back(c);
    }
};

【问题讨论】:

  • Cells 插入到向量中是复制,这与仅存储对单元格的引用的 Java 不同。通过反复执行此操作,您最终会得到向量的向量的向量...看看为什么现在这么慢?
  • 确保您正在运行优化/发布版本。不要使用调试版本来判断 STL 容器的速度,尤其是使用 Visual Studio。
  • 正如@PaulMcKenzie 提到的,还可以提供更多详细信息,例如构建设置,即发布/调试构建、通过调试器或命令提示符运行等。

标签: c++ performance vector


【解决方案1】:

在 C++ 中,vector 等标准容器按值存储元素,而不是按引用存储元素(就像在 Java 中那样)。这意味着您的循环正在创建的单元格不仅引用其他单元格,而且包含它们。您最终会创建一个巨大的嵌套向量森林,其中包含向量,它们本身也包含向量,等等(深度最多大约 20 个级别!)。

您可能想要做的是存储指向相邻单元格的指针:

struct Cell {
    ...

    std::vector<Cell*> neighbors;

    ...

    void addNeighbor(Cell &c) {
        neighbors.push_back(&c);
    }
};

这允许单元格存储彼此的弱引用。

请记住,C++ 没有垃圾收集器,也没有做太多的安全检查,因此您完全有责任确保在不再需要单元格时释放它们,并且确保指针在不再需要时不被取消引用细胞不见了。

【讨论】:

  • 如果单元格超出范围,邻居也会超出范围。无法访问它。
  • 当我 8 到 9 个月不使用一种语言并且忘记了我曾经知道的基本知识时,就会发生这种情况。谢谢!
  • 可以使用std::shared_ptrstd::weak_ptr来处理范围问题。
  • @Jagannath:如果所有的单元都在开始时分配并存储在某个“主”数组中,这不一定是个问题。
  • @Rufflewind:这正是正在发生的事情。单元格和程序共享相同的生命周期。
【解决方案2】:

对于 Cells,上面的答案就足够了。但对于粒子,会提出如下模型:

void addParticle(Particle &p) {
        particles.push_back(p);
    }

上面的代码在内存中创建了很多 Particle 对象并进行了大量的数据复制。以下模型将节省内存分配和复制:

struct Cell {
    std::vector<Particle> particles;
    Particle& getNextParticleRefForUpdate()
    {
        particles.push_back(Particle());
        return particles.at(particles.size() - 1);
    }
    ...
 };

粒子类会暴露集合函数:

struct Particle
{
    int a;
    int b;

    void set(int& aval, int& bval)
    {
        a = aval;
        b = bval;
    }
    ...
};

在设置 Cell 对象的粒子对象时,该函数将执行以下操作:

Cell cell_val;
int aval = 5
int bval = 10;
Particle& par_val = cell_val.getNextParticleRefForUpdate();
par_val.set(aval, bval);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多