Simplifications

This commit is contained in:
Timothée Leclaire-Fournier 2024-01-21 22:48:37 -05:00
parent 53a8a048d9
commit df26f23a9a
5 changed files with 130 additions and 142 deletions

View File

@ -4,7 +4,6 @@ GOLTeamH::GOLTeamH()
: mParsedRule{} : mParsedRule{}
{ {
} }
//! \brief Accesseurs retournant des informations générales sur la //! \brief Accesseurs retournant des informations générales sur la
//! simulation en cours. //! simulation en cours.
//! //!
@ -209,6 +208,9 @@ bool GOLTeamH::setRule(std::string const& rule)
//! //!
//! BorderManagement::warping utilise les voisins opposés par rapport à la //! BorderManagement::warping utilise les voisins opposés par rapport à la
//! **grille**. //! **grille**.
// TODO: Changer le tableau intermédiaire aussi.
void GOLTeamH::setBorderManagement(BorderManagement borderManagement) void GOLTeamH::setBorderManagement(BorderManagement borderManagement)
{ {
mBorderManagement = borderManagement; mBorderManagement = borderManagement;
@ -353,7 +355,6 @@ void GOLTeamH::setSolidColor(State state, Color const& color)
mAliveColor = color; mAliveColor = color;
else else
mDeadColor = color; mDeadColor = color;
} }
@ -373,75 +374,73 @@ void GOLTeamH::setSolidColor(State state, Color const& color)
void GOLTeamH::processOneStep() void GOLTeamH::processOneStep()
{ {
// Pour des raisons de performance, on accède à la grille interne en faisant // Pour des raisons de performance, on accède à la grille interne.
// des calculs pour le border manuellement. //
auto& grid{ mData.data() }; // Les variables suivantes sont utilisées afin d'éviter des appels de fonctions
auto& intGrid{ mData.intData() }; // 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 aliveCount{};
size_t offset{ width() + 2 };
auto width{ mData.width() }, height{ mData.height() };
// Index commence à la première case qui n'est pas dans le border // On commence à la première case qui n'est pas dans le border
auto* ptrGridInt{ &intGrid[width + 3] }; // Pointeur du tableau intermédiaire. auto* ptrGridInt{ (&(mData.intData()[0])) + (offset + 1) }; // Pointeur du tableau intermédiaire.
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.
for (size_t j{}; j < height; ++j) { for (size_t j{}; j < heightNoBorder; ++j) {
for (size_t i{}; i < width; ++i) { for (size_t i{}; i < widthNoBorder; ++i) {
aliveCount = 0; aliveCount = 0;
// Top // Top
ptrGrid -= offset + 1; aliveCount += static_cast<uint8_t> (*ptrGrid);
aliveCount += static_cast<uint8_t> (*ptrGrid); ptrGrid++;
ptrGrid++; aliveCount += static_cast<uint8_t> (*ptrGrid);
aliveCount += static_cast<uint8_t> (*ptrGrid); ptrGrid++;
ptrGrid++; aliveCount += static_cast<uint8_t> (*ptrGrid);
aliveCount += static_cast<uint8_t> (*ptrGrid);
// Milieu // Milieu
ptrGrid += offset - 2; ptrGrid += offset - 2;
aliveCount += static_cast<uint8_t> (*ptrGrid); aliveCount += static_cast<uint8_t> (*ptrGrid);
ptrGrid += 2;
aliveCount += static_cast<uint8_t> (*ptrGrid);
// Dessous
ptrGrid += offset - 2;
aliveCount += static_cast<uint8_t> (*ptrGrid);
ptrGrid++;
aliveCount += static_cast<uint8_t> (*ptrGrid);
ptrGrid++;
aliveCount += static_cast<uint8_t> (*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<GOL::State>(
static_cast<bool>(
(mParsedRule >> static_cast<bool>(*(ptrGrid - 1)) * 16) & (1u << aliveCount)
)
);
}
// On saute le border
ptrGrid += 2; ptrGrid += 2;
ptrGridInt += 2; aliveCount += static_cast<uint8_t> (*ptrGrid);
}
ptrGrid = nullptr;
mData.switchToIntermediate();
mIteration.value()++; // Dessous
ptrGrid += offset - 2;
aliveCount += static_cast<uint8_t> (*ptrGrid);
ptrGrid++;
aliveCount += static_cast<uint8_t> (*ptrGrid);
ptrGrid++;
aliveCount += static_cast<uint8_t> (*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<GOL::State>(
static_cast<bool>(
(mParsedRule >> static_cast<bool>(*(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) if (buffer == nullptr)
return; return;
auto s_ptr = buffer; auto* s_ptr = buffer;
auto& grid = mData.data();
auto width{ mData.width() }, height{ mData.height() }; 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<uint8_t>(*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. // On itère sur chaque éléments du tableau et on associe la couleur.
for (size_t j{}; j < height; ++j) { for (size_t j{}; j < height * width; ++j) {
for (size_t i{}; i < width; ++i) { var = static_cast<uint8_t>(*ptrGrid);
auto var = static_cast<uint8_t>(*ptrGrid);
*s_ptr &= 0; // Clear *s_ptr &= 0; // Clear
*s_ptr |= MAX_ALPHA << 24; // Alpha = 255 *s_ptr |= MAX_ALPHA << 24; // Alpha = 255
*s_ptr |= mAliveColor.red * var << 16; *s_ptr |= mAliveColor.red * var << 16;
*s_ptr |= mAliveColor.green * var << 8; *s_ptr |= mAliveColor.green * var << 8;
*s_ptr |= mAliveColor.blue * var; *s_ptr |= mAliveColor.blue * var;
*s_ptr |= mDeadColor.red * (1 - var) << 16; *s_ptr |= mDeadColor.red * (1 - var) << 16;
*s_ptr |= mDeadColor.green * (1 - var) << 8; *s_ptr |= mDeadColor.green * (1 - var) << 8;
*s_ptr |= mDeadColor.blue * (1 - var); *s_ptr |= mDeadColor.blue * (1 - var);
s_ptr++; s_ptr++;
ptrGrid++; ptrGrid++;
}
ptrGrid += 2;
} }
s_ptr = nullptr;
ptrGrid = nullptr;
} }
std::optional<unsigned char> GOLTeamH::convertCharToNumber(const char c) std::optional<unsigned char> GOLTeamH::convertCharToNumber(const char c)

View File

@ -19,9 +19,10 @@ public:
size_t height() const override { return mData.height(); } size_t height() const override { return mData.height(); }
size_t size() const override { return mData.size(); } size_t size() const override { return mData.size(); }
State state(int x, int y) const override { return mData.value(x, y); } 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); } BorderManagement borderManagement() const override { return mBorderManagement.value_or(GOL::BorderManagement::immutableAsIs); }
Color color(State state) const override { return state == GOL::State::alive ? mAliveColor : mDeadColor; } Color color(State state) const override { return state == GOL::State::alive ? mAliveColor : mDeadColor; }
Statistics statistics() const override; Statistics statistics() const override;
ImplementationInformation information() const override; ImplementationInformation information() const override;
@ -46,7 +47,7 @@ private:
// On utilise un bitset qui contient les règles de chaque nombre. // On utilise un bitset qui contient les règles de chaque nombre.
// On n'utilise pas std::bitset pour des raisons de performance. // 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. // qu'à droite nous avons la règle de réanimation.
// 0000000111000111 0000000110011001 // 0000000111000111 0000000110011001
// ^^^^^^^ ^^^^^^^ // ^^^^^^^ ^^^^^^^

View File

@ -23,10 +23,10 @@ GridTeamH::~GridTeamH()
// Mutateur modifiant la taille de la grille et initialise le contenu par la valeur spécifiée. // 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) 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; mWidth = width;
mHeight = height; mHeight = height;
mData.resize((width * height) + width);
mIntermediateData.resize((width * height) + width);
fill(initValue, false); 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. // Accesseur retournant la valeur d'une cellule à une certaine coordonnée.
GridTeamH::CellType GridTeamH::value(int column, int row) const GridTeamH::CellType GridTeamH::value(int column, int row) const
{ {
size_t offset{ mWidth + 2 }; return mData[(row - 1) * mWidth + (column - 1)];
return mData[offset * row + (column - 1)];
} }
// Mutateur modifiant la valeur d'une cellule à une certaine coordonnée. // Mutateur modifiant la valeur d'une cellule à une certaine coordonnée.
void GridTeamH::setValue(int column, int row, CellType value) void GridTeamH::setValue(int column, int row, CellType value)
{ {
size_t offset{ mWidth + 2 }; mData[(row - 1) * mWidth + (column - 1)] = value;
mData[offset * row + (column - 1)] = value;
} }
// Accesseur retournant la valeur d'une cellule à une certaine coordonnée. // Accesseur retournant la valeur d'une cellule à une certaine coordonnée.
@ -51,8 +49,7 @@ std::optional<GridTeamH::CellType> GridTeamH::at(int column, int row) const
if (column >= mWidth || row >= mHeight) if (column >= mWidth || row >= mHeight)
return std::nullopt; return std::nullopt;
size_t offset{ mWidth + 2 }; return mData[(row - 1) * mWidth + (column - 1)];
return mData[offset * row + (column - 1)];
} }
@ -62,8 +59,7 @@ void GridTeamH::setAt(int column, int row, CellType value)
if (column > mWidth || row > mHeight) if (column > mWidth || row > mHeight)
return; return;
size_t offset{ mWidth + 2 }; mData[(row - 1) * mWidth + (column - 1)] = value;
mData[offset * row + (column - 1)] = value;
} }
@ -89,7 +85,7 @@ GridTeamH::DataType& GridTeamH::intData()
return mIntermediateData; return mIntermediateData;
} }
// TODO: FIX // TODO: FIX performance
// https://en.cppreference.com/w/cpp/algorithm/count // https://en.cppreference.com/w/cpp/algorithm/count
size_t GridTeamH::totalDead() const size_t GridTeamH::totalDead() const
{ {
@ -119,11 +115,10 @@ void GridTeamH::fill(CellType value, bool fillBorder)
i = value; i = value;
} }
else { else {
for (size_t index{}; index < mData.size(); index++) { for (size_t i{ 1 }; i < mWidth - 1; i++) {
if (isInBorder(index)) for (size_t j{ 1 }; j < mHeight - 1; ++j) {
continue; mData[i + (j * mWidth)] = value;
}
mData[index] = value;
} }
} }
} }
@ -139,11 +134,10 @@ void GridTeamH::fillAternately(CellType initValue, bool fillBorder)
mData[i] = !(i % 2) ? initValue : otherValue; mData[i] = !(i % 2) ? initValue : otherValue;
} }
else { else {
for (size_t index{}; index < mData.size(); index++) { for (size_t i{ 1 }; i < mWidth - 1; i++) {
if (isInBorder(index)) for (size_t j{ 1 }; j < mHeight - 1; ++j) {
continue; mData[i + (j * mWidth)] = !(i % 2) ? initValue : otherValue;
}
mData[index] = !(index % 2) ? initValue : otherValue;
} }
} }
} }
@ -152,32 +146,48 @@ void GridTeamH::randomize(double percentAlive, bool fillBorder)
{ {
if (fillBorder) { if (fillBorder) {
for (auto& i : mData) { for (auto& i : mData) {
if (mDistribution(mEngine) < percentAlive) i = static_cast<GridTeamH::CellType>(mDistribution(mEngine) < percentAlive);
i = CellType::alive;
else
i = CellType::dead;
} }
} }
else { else {
for (size_t index{}; index < mData.size() - 1; index++) { for (size_t i{ 1 }; i < mWidth - 1; i++) {
if (isInBorder(index)) for (size_t j{ 1 }; j < mHeight - 1; ++j) {
continue; mData[i + (j * mWidth)] = static_cast<GridTeamH::CellType>(mDistribution(mEngine) < percentAlive);
}
if (mDistribution(mEngine) < percentAlive)
mData[index] = CellType::alive;
else
mData[index] = CellType::dead;
} }
} }
} }
// Performance non nécessaire.
void GridTeamH::fillBorder(CellType value) void GridTeamH::fillBorder(CellType value)
{ {
for (size_t index{}; index < mData.size(); index++) { auto* ptr = &mData.front();
if (isInBorder(index)) auto* e_ptr = &mData.front() + mWidth;
mData[index] = value;
// 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); mData.swap(mIntermediateData);
} }

View File

@ -9,11 +9,6 @@
Cette classe fonctionne en ayant deux std::vector qui contiennent les statuts 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 des cellules. Un vecteur intermédiaire est échangé avec .swap() avec le vecteur
réel lors des calculs. 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 class GridTeamH
@ -37,7 +32,6 @@ public:
size_t width() const { return mWidth; } size_t width() const { return mWidth; }
size_t height() const { return mHeight; } size_t height() const { return mHeight; }
size_t size() const { return mHeight * mWidth; } 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{}); void resize(size_t width, size_t height, CellType initValue = CellType{});
@ -70,7 +64,6 @@ public:
void fillBorderMirror(); void fillBorderMirror();
void switchToIntermediate(); void switchToIntermediate();
bool isInBorder(size_t index) const;
private: private:
DataType mData, mIntermediateData; DataType mData, mIntermediateData;
@ -81,13 +74,3 @@ private:
std::mt19937 mEngine; std::mt19937 mEngine;
std::uniform_real_distribution<> mDistribution; 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));
}

View File

@ -4,8 +4,6 @@
#include "GOLTeamH.h" #include "GOLTeamH.h"
//TEST DE COMMIT
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
QApplication application(argc, argv); QApplication application(argc, argv);
@ -13,9 +11,6 @@ int main(int argc, char* argv[])
GOLApp window; GOLApp window;
window.addEngine(new GOLTeamH()); window.addEngine(new GOLTeamH());
window.show(); window.show();
return application.exec(); return application.exec();
} }