From df26f23a9a39f262b17bc37e259c0678b076083a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Leclaire-Fournier?= Date: Sun, 21 Jan 2024 22:48:37 -0500 Subject: [PATCH] Simplifications --- GPA675Lab1GOL/GOLTeamH.cpp | 158 ++++++++++++++++++------------------ GPA675Lab1GOL/GOLTeamH.h | 5 +- GPA675Lab1GOL/GridTeamH.cpp | 87 +++++++++++--------- GPA675Lab1GOL/GridTeamH.h | 17 ---- GPA675Lab1GOL/main.cpp | 5 -- 5 files changed, 130 insertions(+), 142 deletions(-) diff --git a/GPA675Lab1GOL/GOLTeamH.cpp b/GPA675Lab1GOL/GOLTeamH.cpp index f2b8fc2..61e14c6 100644 --- a/GPA675Lab1GOL/GOLTeamH.cpp +++ b/GPA675Lab1GOL/GOLTeamH.cpp @@ -4,7 +4,6 @@ GOLTeamH::GOLTeamH() : mParsedRule{} { } - //! \brief Accesseurs retournant des informations générales sur la //! simulation en cours. //! @@ -209,6 +208,9 @@ bool GOLTeamH::setRule(std::string const& rule) //! //! BorderManagement::warping utilise les voisins opposés par rapport à la //! **grille**. + + +// TODO: Changer le tableau intermédiaire aussi. void GOLTeamH::setBorderManagement(BorderManagement borderManagement) { mBorderManagement = borderManagement; @@ -353,7 +355,6 @@ void GOLTeamH::setSolidColor(State state, Color const& color) mAliveColor = color; else mDeadColor = color; - } @@ -373,75 +374,73 @@ void GOLTeamH::setSolidColor(State state, Color const& color) void GOLTeamH::processOneStep() { - // Pour des raisons de performance, on accède à la grille interne en faisant - // des calculs pour le border manuellement. - auto& grid{ mData.data() }; - auto& intGrid{ mData.intData() }; + // Pour des raisons de performance, on accède à la grille interne. + // + // Les variables suivantes sont utilisées afin d'éviter des appels de fonctions + // qui peuvent prendre beaucoup de temps. + auto const widthNoBorder{ mData.width() - 2 }, heightNoBorder{ mData.height() - 2 }; + auto const offset{ mData.width() }; - if (mBorderManagement == GOL::BorderManagement::foreverDead) { - size_t aliveCount{}; - size_t offset{ width() + 2 }; - auto width{ mData.width() }, height{ mData.height() }; + size_t aliveCount{}; - // Index commence à la première case qui n'est pas dans le border - auto* ptrGridInt{ &intGrid[width + 3] }; // Pointeur du tableau intermédiaire. - auto* ptrGrid{ &grid[width + 3] }; // Pointeur qui se promène en mémoire. + // On commence à la première case qui n'est pas dans le border + auto* ptrGridInt{ (&(mData.intData()[0])) + (offset + 1) }; // Pointeur du tableau intermédiaire. + auto* ptrGrid{ &(mData.data()[0]) }; // Pointeur qui se promène en mémoire. - for (size_t j{}; j < height; ++j) { - for (size_t i{}; i < width; ++i) { + for (size_t j{}; j < heightNoBorder; ++j) { + for (size_t i{}; i < widthNoBorder; ++i) { - aliveCount = 0; + aliveCount = 0; - // Top - ptrGrid -= offset + 1; - aliveCount += static_cast (*ptrGrid); - ptrGrid++; - aliveCount += static_cast (*ptrGrid); - ptrGrid++; - aliveCount += static_cast (*ptrGrid); + // Top + aliveCount += static_cast (*ptrGrid); + ptrGrid++; + aliveCount += static_cast (*ptrGrid); + ptrGrid++; + aliveCount += static_cast (*ptrGrid); - // Milieu - ptrGrid += offset - 2; - aliveCount += static_cast (*ptrGrid); - ptrGrid += 2; - aliveCount += static_cast (*ptrGrid); - - - // Dessous - ptrGrid += offset - 2; - aliveCount += static_cast (*ptrGrid); - ptrGrid++; - aliveCount += static_cast (*ptrGrid); - ptrGrid++; - aliveCount += static_cast (*ptrGrid); - - // On retourne à une place plus loin qu'à l'origine. - ptrGrid -= offset; - ptrGridInt++; - - // On prend avantage du fait que GOL::State::alive = 1. - // - // On évite aussi d'utiliser l'opérateur []. En profilant, nous avons vu un - // impact de performance de ~5%. - // - // On accède à la bonne partie des bits et on compare si le bit de survie/réanimation est - // présent. Voir GOLTeamH.cpp pour plus de détails. - *(ptrGridInt - 1) = static_cast( - static_cast( - (mParsedRule >> static_cast(*(ptrGrid - 1)) * 16) & (1u << aliveCount) - ) - ); - } - - // On saute le border + // Milieu + ptrGrid += offset - 2; + aliveCount += static_cast (*ptrGrid); ptrGrid += 2; - ptrGridInt += 2; - } - ptrGrid = nullptr; + aliveCount += static_cast (*ptrGrid); - mData.switchToIntermediate(); - mIteration.value()++; + + // Dessous + ptrGrid += offset - 2; + aliveCount += static_cast (*ptrGrid); + ptrGrid++; + aliveCount += static_cast (*ptrGrid); + ptrGrid++; + aliveCount += static_cast (*ptrGrid); + + // On retourne à une place plus loin qu'à l'origine. + ptrGrid -= (2 * offset) + 1; + ptrGridInt++; + + // On prend avantage du fait que GOL::State::alive = 1. + // + // On évite aussi d'utiliser l'opérateur []. En profilant, nous avons vu un + // impact de performance de ~5%. + // + // On accède à la bonne partie des bits et on compare si le bit de survie/réanimation est + // présent. Voir GOLTeamH.cpp pour plus de détails. + *(ptrGridInt - 1) = static_cast( + static_cast( + (mParsedRule >> static_cast(*(ptrGrid + offset)) * 16) & (1u << aliveCount) + ) + ); + } + + // On saute le border + ptrGrid += 2; + ptrGridInt += 2; } + ptrGrid = nullptr; + ptrGridInt = nullptr; + + mData.switchToIntermediate(); + mIteration.value()++; } @@ -484,33 +483,32 @@ void GOLTeamH::updateImage(uint32_t* buffer, size_t buffer_size) const if (buffer == nullptr) return; - auto s_ptr = buffer; + auto* s_ptr = buffer; - auto& grid = mData.data(); auto width{ mData.width() }, height{ mData.height() }; - auto* ptrGrid{ &grid[width + 3] }; // Pointeur qui se promène en mémoire. + auto* ptrGrid{ &mData.data()[0] }; // Pointeur qui se promène en mémoire. + auto var = static_cast(*ptrGrid); // Variable qui va décoder l'état d'une cellule. // On itère sur chaque éléments du tableau et on associe la couleur. - for (size_t j{}; j < height; ++j) { - for (size_t i{}; i < width; ++i) { - auto var = static_cast(*ptrGrid); + for (size_t j{}; j < height * width; ++j) { + var = static_cast(*ptrGrid); - *s_ptr &= 0; // Clear - *s_ptr |= MAX_ALPHA << 24; // Alpha = 255 + *s_ptr &= 0; // Clear + *s_ptr |= MAX_ALPHA << 24; // Alpha = 255 - *s_ptr |= mAliveColor.red * var << 16; - *s_ptr |= mAliveColor.green * var << 8; - *s_ptr |= mAliveColor.blue * var; + *s_ptr |= mAliveColor.red * var << 16; + *s_ptr |= mAliveColor.green * var << 8; + *s_ptr |= mAliveColor.blue * var; - *s_ptr |= mDeadColor.red * (1 - var) << 16; - *s_ptr |= mDeadColor.green * (1 - var) << 8; - *s_ptr |= mDeadColor.blue * (1 - var); + *s_ptr |= mDeadColor.red * (1 - var) << 16; + *s_ptr |= mDeadColor.green * (1 - var) << 8; + *s_ptr |= mDeadColor.blue * (1 - var); - s_ptr++; - ptrGrid++; - } - ptrGrid += 2; + s_ptr++; + ptrGrid++; } + s_ptr = nullptr; + ptrGrid = nullptr; } std::optional GOLTeamH::convertCharToNumber(const char c) diff --git a/GPA675Lab1GOL/GOLTeamH.h b/GPA675Lab1GOL/GOLTeamH.h index f41ee24..8c5aa89 100644 --- a/GPA675Lab1GOL/GOLTeamH.h +++ b/GPA675Lab1GOL/GOLTeamH.h @@ -19,9 +19,10 @@ public: size_t height() const override { return mData.height(); } size_t size() const override { return mData.size(); } State state(int x, int y) const override { return mData.value(x, y); } - std::string rule() const override { return mRule.value_or(std::move(std::string())); } + std::string rule() const override { return mRule.value_or(std::string()); } BorderManagement borderManagement() const override { return mBorderManagement.value_or(GOL::BorderManagement::immutableAsIs); } Color color(State state) const override { return state == GOL::State::alive ? mAliveColor : mDeadColor; } + Statistics statistics() const override; ImplementationInformation information() const override; @@ -46,7 +47,7 @@ private: // On utilise un bitset qui contient les règles de chaque nombre. // On n'utilise pas std::bitset pour des raisons de performance. // - // Le premier 8 bits (à gauche) est celui de la règle de survie alors + // Le premier 16 bits (à gauche) est celui de la règle de survie alors // qu'à droite nous avons la règle de réanimation. // 0000000111000111 0000000110011001 // ^^^^^^^ ^^^^^^^ diff --git a/GPA675Lab1GOL/GridTeamH.cpp b/GPA675Lab1GOL/GridTeamH.cpp index e64137a..4b9b3f6 100644 --- a/GPA675Lab1GOL/GridTeamH.cpp +++ b/GPA675Lab1GOL/GridTeamH.cpp @@ -23,10 +23,10 @@ GridTeamH::~GridTeamH() // Mutateur modifiant la taille de la grille et initialise le contenu par la valeur spécifiée. void GridTeamH::resize(size_t width, size_t height, CellType initValue) { - mData.resize((width + 2) * (height + 2)); - mIntermediateData.resize((width + 2) * (width + 2)); mWidth = width; mHeight = height; + mData.resize((width * height) + width); + mIntermediateData.resize((width * height) + width); fill(initValue, false); } @@ -34,15 +34,13 @@ void GridTeamH::resize(size_t width, size_t height, CellType initValue) // Accesseur retournant la valeur d'une cellule à une certaine coordonnée. GridTeamH::CellType GridTeamH::value(int column, int row) const { - size_t offset{ mWidth + 2 }; - return mData[offset * row + (column - 1)]; + return mData[(row - 1) * mWidth + (column - 1)]; } // Mutateur modifiant la valeur d'une cellule à une certaine coordonnée. void GridTeamH::setValue(int column, int row, CellType value) { - size_t offset{ mWidth + 2 }; - mData[offset * row + (column - 1)] = value; + mData[(row - 1) * mWidth + (column - 1)] = value; } // Accesseur retournant la valeur d'une cellule à une certaine coordonnée. @@ -51,8 +49,7 @@ std::optional GridTeamH::at(int column, int row) const if (column >= mWidth || row >= mHeight) return std::nullopt; - size_t offset{ mWidth + 2 }; - return mData[offset * row + (column - 1)]; + return mData[(row - 1) * mWidth + (column - 1)]; } @@ -62,8 +59,7 @@ void GridTeamH::setAt(int column, int row, CellType value) if (column > mWidth || row > mHeight) return; - size_t offset{ mWidth + 2 }; - mData[offset * row + (column - 1)] = value; + mData[(row - 1) * mWidth + (column - 1)] = value; } @@ -89,7 +85,7 @@ GridTeamH::DataType& GridTeamH::intData() return mIntermediateData; } -// TODO: FIX +// TODO: FIX performance // https://en.cppreference.com/w/cpp/algorithm/count size_t GridTeamH::totalDead() const { @@ -119,11 +115,10 @@ void GridTeamH::fill(CellType value, bool fillBorder) i = value; } else { - for (size_t index{}; index < mData.size(); index++) { - if (isInBorder(index)) - continue; - - mData[index] = value; + for (size_t i{ 1 }; i < mWidth - 1; i++) { + for (size_t j{ 1 }; j < mHeight - 1; ++j) { + mData[i + (j * mWidth)] = value; + } } } } @@ -139,11 +134,10 @@ void GridTeamH::fillAternately(CellType initValue, bool fillBorder) mData[i] = !(i % 2) ? initValue : otherValue; } else { - for (size_t index{}; index < mData.size(); index++) { - if (isInBorder(index)) - continue; - - mData[index] = !(index % 2) ? initValue : otherValue; + for (size_t i{ 1 }; i < mWidth - 1; i++) { + for (size_t j{ 1 }; j < mHeight - 1; ++j) { + mData[i + (j * mWidth)] = !(i % 2) ? initValue : otherValue; + } } } } @@ -152,32 +146,48 @@ void GridTeamH::randomize(double percentAlive, bool fillBorder) { if (fillBorder) { for (auto& i : mData) { - if (mDistribution(mEngine) < percentAlive) - i = CellType::alive; - else - i = CellType::dead; + i = static_cast(mDistribution(mEngine) < percentAlive); } } else { - for (size_t index{}; index < mData.size() - 1; index++) { - if (isInBorder(index)) - continue; - - if (mDistribution(mEngine) < percentAlive) - mData[index] = CellType::alive; - else - mData[index] = CellType::dead; + for (size_t i{ 1 }; i < mWidth - 1; i++) { + for (size_t j{ 1 }; j < mHeight - 1; ++j) { + mData[i + (j * mWidth)] = static_cast(mDistribution(mEngine) < percentAlive); + } } } } - -// Performance non nécessaire. void GridTeamH::fillBorder(CellType value) { - for (size_t index{}; index < mData.size(); index++) { - if (isInBorder(index)) - mData[index] = value; + auto* ptr = &mData.front(); + auto* e_ptr = &mData.front() + mWidth; + + // TOP + while (ptr < e_ptr) { + *ptr = value; + ptr++; + } + + // DROITE + e_ptr += mWidth * mHeight; + while (ptr < e_ptr) { + *ptr = value; + ptr += mWidth; + } + + // DESSOUS + e_ptr -= mWidth; + while (ptr > e_ptr) { + *ptr = value; + ptr--; + } + + // GAUCHE + e_ptr -= mWidth * mHeight; + while (ptr > e_ptr) { + *ptr = value; + ptr -= mWidth; } } @@ -199,3 +209,4 @@ void GridTeamH::switchToIntermediate() mData.swap(mIntermediateData); } + diff --git a/GPA675Lab1GOL/GridTeamH.h b/GPA675Lab1GOL/GridTeamH.h index 0c5050b..724242b 100644 --- a/GPA675Lab1GOL/GridTeamH.h +++ b/GPA675Lab1GOL/GridTeamH.h @@ -9,11 +9,6 @@ Cette classe fonctionne en ayant deux std::vector qui contiennent les statuts des cellules. Un vecteur intermédiaire est échangé avec .swap() avec le vecteur réel lors des calculs. - - On utilise une grandeur de width + 2 par height + 2. Cela nous permet d'avoir un - contour qui sera modifié de façon dynamique par la règle du border. Cela est - complètement transparent aux fonctions publiques autres que certaines fonctions - spécialisées. */ class GridTeamH @@ -37,7 +32,6 @@ public: size_t width() const { return mWidth; } size_t height() const { return mHeight; } size_t size() const { return mHeight * mWidth; } - size_t realSize() const { return (mHeight + 2) * (mWidth + 2); } void resize(size_t width, size_t height, CellType initValue = CellType{}); @@ -70,7 +64,6 @@ public: void fillBorderMirror(); void switchToIntermediate(); - bool isInBorder(size_t index) const; private: DataType mData, mIntermediateData; @@ -81,13 +74,3 @@ private: std::mt19937 mEngine; std::uniform_real_distribution<> mDistribution; }; - -// Attention: performance terrible si utilisation. Seulement lorsque vitesse -// n'est pas demandée, donc pas dans la boucle principale. -inline bool GridTeamH::isInBorder(size_t index) const -{ - return(index % (mWidth + 2) < 1 - || index % (mWidth + 2) > mWidth - || index < mWidth + 2 - || index >(mWidth + 2) * (mHeight + 1)); -} \ No newline at end of file diff --git a/GPA675Lab1GOL/main.cpp b/GPA675Lab1GOL/main.cpp index c951eb3..709327c 100644 --- a/GPA675Lab1GOL/main.cpp +++ b/GPA675Lab1GOL/main.cpp @@ -4,8 +4,6 @@ #include "GOLTeamH.h" -//TEST DE COMMIT - int main(int argc, char* argv[]) { QApplication application(argc, argv); @@ -13,9 +11,6 @@ int main(int argc, char* argv[]) GOLApp window; window.addEngine(new GOLTeamH()); - - - window.show(); return application.exec(); }