在尝试测试这个小程序时,我得到一个连续的堆栈溢出错误,以测试数组结构与结构数组之间的性能差异。我显然做错了什么,但我很难搞清楚。有人能帮忙吗?
#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。这应该是一个我。但它并没有影响堆栈溢出,所以我只是将其编辑出来。
答案 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+=()
崩溃而不是其他人发现)。