1

以前、スタック オーバーフロー エラーに関する質問があり、オブジェクトの配列をベクトルに切り替えました。その質問は、必要に応じてここで参照できます:スタック オーバーフロー エラーを取り除く方法

ただし、私の現在の質問は、ベクトルの初期化をどのように高速化するかです。私の現在の方法は現在、約15秒かかります。ベクトルの代わりに配列を使用すると、スタックオーバーフローエラーをスローしないほど十分に小さい配列のサイズで1秒ほどかかりました。

これが私がそれを初期化する方法です:

main.cpp で、ダンジョン オブジェクトを初期化します。

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

dungeon(...) コンストラクターで、部屋の 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 でスタック オーバーフローが発生します。それでも、配列をヒープに割り当てることで、を使用することができますstd::array(および を使用する場合と比較した場合の利点)。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が 1 回だけ実行され、単一の割り当てが 1 回だけ発生し、各要素がデフォルトで構築されることに注意してください。各要素を具体的に構築したい場合は、rooms.reserve(MAX_RM_ROWS * MAX_RM_COLS);代わりに使用して、ループにベクトルを設定します。

行と列を入れ替えてプロファイリングし、どちらが高速かを確認することもできます。

于 2012-07-22T23:06:20.137 に答える