1

我之前有一个关于堆栈溢出错误的问题,并为我的对象数组切换到向量。如果需要,可以在此处引用该问题:How to get rid of stack overflow error

但是,我当前的问题是,如何加快向量的初始化。我目前的方法目前需要约 15 秒。使用数组而不是向量需要一秒钟的时间,而数组的大小足够小,不会引发堆栈溢出错误。

这是我初始化它的方式:

在 main.cpp 我初始化我的地牢对象:

dungeon = Dungeon(0, &textureHandler, MIN_X, MAX_Y);

在我的地牢(...)构造函数中,我初始化我的 5x5 房间向量并调用 loadDungeon:

Dungeon::Dungeon(int dungeonID, TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
    currentRoomRow = 0;
    currentRoomCol = 0;
    for (int r = 0; r < MAX_RM_ROWS; ++r)
    {
        rooms.push_back(vector<Room>());
        for (int c = 0; c < MAX_RM_COLS; ++c)
        {
            rooms[r].push_back(Room());
        }
    }
    loadDungeon(dungeonID, textureHandler, topLeftX, topLeftY);
}

我的 Room 构造函数填充了我的 30x50 单元格向量(所以我可以在 loadDungeon 函数中设置它们):

Room::Room() 
{ 
    for (int r = 0; r < MAX_ROWS; ++r)
    {
        cells.push_back(vector<Cell>());
        for (int c = 0; c < MAX_COLS; ++c)
        {
            cells[r].push_back(Cell());
        }
    }
}

我的默认单元构造函数很简单,没有做太多,但我还是会发布它:

Cell::Cell() 
{ 
    x = 0;
    y = 0;
    width = 16;
    height = 16;
    solid = false;
    texCoords.push_back(0);
    texCoords.push_back(0);
    texCoords.push_back(1);
    texCoords.push_back(0);
    texCoords.push_back(1);
    texCoords.push_back(1);
    texCoords.push_back(0);
    texCoords.push_back(1);
}

最后,我的 loadDungeon() 函数将设置单元格。最终这将从文件中读取并加载单元格,但现在我想尽可能优化一下。

void Dungeon::loadDungeon(int dungeonID, TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
    int startX = topLeftX + (textureHandler->getSpriteWidth()/2);
    int startY = topLeftY - (textureHandler->getSpriteHeight()/2);
    int xOffset = 0;
    int yOffset = 0;
    for (int r = 0; r < MAX_RM_ROWS; ++r)
    {
        for (int c = 0; c < MAX_RM_COLS; ++c)
        {
            for (int cellRow = 0; cellRow < rooms[r][c].getMaxRows(); ++cellRow)
            {
                xOffset = 0;
                for (int cellCol = 0; cellCol < rooms[r][c].getMaxCols(); ++cellCol)
                {
                    rooms[r][c].setupCell(cellRow, cellCol, startX + xOffset, startY - yOffset, textureHandler->getSpriteWidth(), textureHandler->getSpriteHeight(), false, textureHandler->getSpriteTexCoords("grass"));
                    xOffset += textureHandler->getSpriteWidth();
                }
                yOffset += textureHandler->getSpriteHeight();
            }
        }
    }

    currentDungeon = dungeonID;
    currentRoomRow = 0;
    currentRoomCol = 0;
}

那么我怎样才能加快速度,这样每次加载都不需要大约 15 秒。我觉得加载一个简单的 2D 游戏不需要 15 秒。

解决方案 好吧,我的解决方案是在我的代码中使用 std::vector::reserve 调用(rooms.reserve,它最终运行良好。我将函数 Dungeon::loadDungeon 更改为 Dungeon::loadDefaultDungeon 因为它现在加载了一个保存文件.

无论如何,这里是代码(我在调试模式下从 ~15+ 秒缩短到大约 4-5 秒):

Dungeon::Dungeon() 
{ 
    rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS);
    currentDungeon = 0;
    currentRoomRow = 0;
    currentRoomCol = 0;
}
void Dungeon::loadDefaultDungeon(TextureHandler* textureHandler, int topLeftX, int topLeftY)
{
    int startX = topLeftX + (textureHandler->getSpriteWidth()/2);
    int startY = topLeftY - (textureHandler->getSpriteHeight()/2);
    int xOffset = 0;
    int yOffset = 0;
    cerr << "Loading default dungeon..." << endl;
    for (int roomRow = 0; roomRow < MAX_RM_ROWS; ++roomRow)
    {
        for (int roomCol = 0; roomCol < MAX_RM_COLS; ++roomCol)
        {
            rooms.push_back(Room());
            int curRoom = roomRow * MAX_RM_COLS + roomCol;
            for (int cellRow = 0; cellRow < rooms[curRoom].getMaxRows(); ++cellRow)
            {
                for (int cellCol = 0; cellCol < rooms[curRoom].getMaxCols(); ++cellCol)
                {
                    rooms[curRoom].setupCell(cellRow, cellCol, startX + xOffset, startY - yOffset, textureHandler->getSpriteWidth(), textureHandler->getSpriteHeight(), false, textureHandler->getSpriteTexCoords("default"), "default");
                    xOffset += textureHandler->getSpriteWidth();
                }
                yOffset += textureHandler->getSpriteHeight();
                xOffset = 0;
            }
            cerr << "     room " << curRoom << " complete" << endl;
        }
    }
    cerr << "default dungeon loaded" << endl;
}

Room::Room()
{ 
    cells.reserve(MAX_ROWS * MAX_COLS);
    for (int r = 0; r < MAX_ROWS; ++r)
    {
        for (int c = 0; c < MAX_COLS; ++c)
        {
            cells.push_back(Cell());
        }
    }
}
void Room::setupCell(int row, int col, float x, float y, float width, float height, bool solid, /*std::array<float, 8>*/ vector<float> texCoords, string texName)
{
    cells[row * MAX_COLS + col].setup(x, y, width, height, solid, texCoords, texName);
}

void Cell::setup(float x, float y, float width, float height, bool solid, /*std::array<float,8>*/ vector<float> t, string texName)
{
    this->x = x;
    this->y = y;
    this->width = width;
    this->height = height;
    this->solid = solid;
    for (int i = 0; i < t.size(); ++i)
        this->texCoords.push_back(t[i]);
    this->texName = texName;
}
4

2 回答 2

3

由于您的向量似乎在编译时定义了它们的大小,如果您可以使用 C++11,您可以考虑使用std::array而不是std::vector. std::array无法调整大小并且缺少 中的许多操作std::vector,但更轻量级,并且似乎非常适合您正在做的事情。

例如,您可以声明cells为:

#include <array>

/* ... */

std::array<std::array<Cell, MAX_COLS>, MAX_ROWS> cells;

更新:由于本地定义std::array的在堆栈上分配其内部数组,因此由于数组的大小相当大,OP 将遇到堆栈溢出。尽管如此,仍然可以通过在堆上分配数组来使用 an std::array(以及与 using 相比的好处)。std::vector这可以通过执行以下操作来完成:

typedef std::array<std::array<Cell, MAX_COLS>, MAX_ROWS> Map;
Map* cells;

/* ... */

cells = new Map();

更好的是,可以使用智能指针:

#include <memory>    

/* ... */

std::unique_ptr<Map> cells;
cells = std::unique_ptr(new Map());
于 2012-07-22T22:40:18.517 回答
3

有这么多动态分配似乎很浪费。您可以通过展平向量并大步访问它来摆脱一次分配:

std::vector<Room> rooms;

rooms.resize(MAX_RM_ROWS * MAX_RM_COLS);

for (unsigned int i = 0; i != MAX_RM_ROWS; ++i)
{ 
    for (unsigned int j = 0; j != MAX_RM_COLS; ++j)
    {
        Room & r = rooms[i * MAX_RM_COLS + j];
        // use `r`       ^^^^^^^^^^^^^^^^^^^-----<< strides!
    }
}

请注意如何resize只执行一次,只产生一次分配,以及默认构造每个元素。如果您希望专门构造每个元素,请rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS);改用并在循环中填充向量。

您可能还希望使用交换的行和列进行分析,看看哪个更快。

于 2012-07-22T23:06:20.137 回答