矢量化后C ++中的堆栈溢出错误

时间:2017-08-30 21:05:40

标签: visual-studio c++11

在尝试测试这个小程序时,我得到一个连续的堆栈溢出错误,以测试数组结构与结构数组之间的性能差异。我显然做错了什么,但我很难搞清楚。有人能帮忙吗?

#include "stdafx.h"

#include <vector>

#define NUM_ITEMS 10000000

struct Vector3 {

    Vector3() : x(0.0f), y(0.0f), z(0.0f) {}

    float x, y, z;

    Vector3 operator+=(const Vector3& v) {
        Vector3 v_new;
        v_new.x = this->x + v.x;
        v_new.y = this->y + v.y;
        v_new.z = this->z + v.z;

        return v_new;
    }
};

struct SoA {
    SoA() : pos(NUM_ITEMS), vel(NUM_ITEMS) {}

    std::vector<Vector3> pos, vel;
};

struct AoS {
    Vector3 pos, vel;
};

int main()
{
    std::vector<AoS> entities[NUM_ITEMS];
    for (int i = 0; i < entities->size(); i++) {
        entities->at(i).pos += entities->at(i).vel;
    }

    SoA entityManager;
    for (int i = 0; i < NUM_ITEMS; i++) {
        entityManager.pos[i] += entityManager.vel[i];
    }

    return 0;
}

编辑:我看到我不小心在第二个循环中放了1。这应该是一个我。但它并没有影响堆栈溢出,所以我只是将其编辑出来。

3 个答案:

答案 0 :(得分:2)

更改第二个for循环
for (int i = 0; 1 < NUM_ITEMS; i++) {

for (int i = 0; i < NUM_ITEMS; i++) {

基本上你是无限地追加向量。

答案 1 :(得分:1)

首先,

Vector3 operator+=(const Vector3& v) {
    Vector3 v_new;
    v_new.x = this->x + v.x;
    v_new.y = this->y + v.y;
    v_new.z = this->z + v.z;

    return v_new;
}

应该是

Vector3& operator+=(const Vector3& v) {
    this->x += v.x;
    this->y += v.y;
    this->z += v.z;

    return *this;
}
Vector3 operator+(const Vector3& v) const {
  auto v_new = *this;
  v_new += v;
  return v_new;
}

其次,你的循环有错误(1 < NUM_ITEMS表示“永远”)。通过...手动索引修复此问题。

你也有很多entities的向量。然后你似乎认为空向量包含项目。

std::vector<AoS> entities(NUM_ITEMS);
for (auto& e : entities) {
    e.pos += e.vel;
}

在第二个循环中,它可以消除手动边界检查,但可以完成。

template<class Base>
struct index_iterator {
  Base b;
  Base operator*() const { return b; }
  index_iterator& operator++() { ++b; return *this; }
  index_iterator operator++(int) { auto r = *this; ++b; return r; }
  bool operator==(index_iterator const& o) const {
    return b == o.b;
  }
  bool operator!=(index_iterator const& o) const {
    return b != o.b;
  }
  index_iterator(index_iterator const&)=default;
  index_iterator(index_iterator &&)=default;
  index_iterator& operator=(index_iterator const&)=default;
  index_iterator& operator=(index_iterator &&)=default;
  index_iterator(Base bin):b(std::move(bin)) {}
};
template<class It>
struct range_t {
  It b, e;
  It begin() const{ return b; }
  It end() const{ return e; }
};
template<class It>
range_t<It> range( It b, It e ) { return {b,e}; }

using index = index_iterator<std::size_t>;
template<class T, std::size_t N>
range_t<index> indexes_into( T(&)[N] ) {
  return range<index>(0, N);
}
range_t<index> indexes_into( std::array<T,N> const& ) {
  return range<index>(0, N);
}
template<class C>
range_t<index> indexes_into( C const& c ) {
  return range<index>(0, c.size());
}

现在我们可以:

SoA entityManager;
for (auto i : indexes_into(entityManager.pos)) {
  entityManager.pos[i] += entityManager.vel[i];
}

永远不要再次使用fencepost错误搞砸for()循环。

但那只是我。我宁愿写一堆元编程代码而不是处理1分之一的错误。因为元编程很容易且可以测试,但是为了避免这种错误很难

答案 2 :(得分:0)

问题完全正如您所描述的那样:堆栈溢出,并且由于以下原因而发生:

std::vector<AoS> entities[NUM_ITEMS];

这是一个存储在堆栈中的100m std::vector包含类型AoS的数组。这里(MacOSX 10.12.6; Apple LLVM版本7.0.0(clang-700.0.72) - 这导致每个元素有24个字节的存储空间,总共〜240MB

这是有问题的,因为堆栈空间在所有现代操作系统上都是有限的 - 通常在每个线程堆栈几MB的范围内。当函数序言尝试默认构造数组中的第一个元素时,会发生堆栈溢出(请记住:项目是从堆栈中的高低内存存储的,并且它远离底部)。

在此处进行调试时,崩溃发生在main()开始执行之前。减少NUM_ITEMS的值会导致程序进一步变大(它在Vector3::operator+=()崩溃而不是其他人发现)。