Make add and clear use indices.

This is the last commit of the workstream and we
have a ~10% speed improvement.
This commit is contained in:
2025-09-04 11:27:49 +02:00
parent f1445b7544
commit ff43ba5287

58
main.cc
View File

@@ -26,7 +26,7 @@ namespace {
* \param pos Position of closest corner to origin * \param pos Position of closest corner to origin
* \param length Side length. * \param length Side length.
*/ */
Square(Pos const &pos, size_t const length) noexcept : pos_(pos), length_(length) { Square(Pos pos, size_t const length) noexcept : pos_(pos), length_(length) {
} }
Square(Square const &other) noexcept = default; Square(Square const &other) noexcept = default;
@@ -57,9 +57,13 @@ namespace {
} }
Results(Results const &other) noexcept = delete; Results(Results const &other) noexcept = delete;
Results &operator=(Results const &other) noexcept = delete; Results &operator=(Results const &other) noexcept = delete;
Results &operator=(Results &&other) noexcept = default; Results &operator=(Results &&other) noexcept = default;
Results(Results &&other) noexcept = default; Results(Results &&other) noexcept = default;
~Results() noexcept = default; ~Results() noexcept = default;
auto length() const noexcept -> size_t { return length_; } auto length() const noexcept -> size_t { return length_; }
@@ -67,7 +71,7 @@ namespace {
/** Output the grid. */ /** Output the grid. */
auto output() const -> void { auto output() const -> void {
std::string out(length_ * length_, '.'); std::string out(length_ * length_, '.');
for (auto const& sq : squares_) { for (auto const &sq: squares_) {
prettify_sq(out, sq); prettify_sq(out, sq);
} }
for (size_t idx = 0; idx < length_ * length_; idx += length_) { for (size_t idx = 0; idx < length_ * length_; idx += length_) {
@@ -76,17 +80,17 @@ namespace {
} }
private: private:
auto set(std::string& s, size_t x, size_t y, char c) const noexcept -> void { auto set(std::string &s, size_t x, size_t y, char c) const noexcept -> void {
assert(x < length_); assert(x < length_);
assert(y < length_); assert(y < length_);
assert(grid_[x + y * length_] != c); assert(grid_[x + y * length_] != c);
s[x + y * length_] = c; s[x + y * length_] = c;
} }
auto sq_x(Square const& sq) const noexcept -> size_t { return sq.pos() % length_; } auto sq_x(Square const &sq) const noexcept -> size_t { return sq.pos() % length_; }
auto sq_y(Square const& sq) const noexcept -> size_t { return sq.pos() / length_; } auto sq_y(Square const &sq) const noexcept -> size_t { return sq.pos() / length_; }
auto prettify_sq(std::string& s, Square const &sq) const noexcept -> void { auto prettify_sq(std::string &s, Square const &sq) const noexcept -> void {
switch (sq.length()) { switch (sq.length()) {
case 1: set(s, sq_x(sq), sq_y(sq), '*'); case 1: set(s, sq_x(sq), sq_y(sq), '*');
break; break;
@@ -102,7 +106,7 @@ namespace {
set(s, sq_x(sq), sq_y(sq) + n - 1, '+'); set(s, sq_x(sq), sq_y(sq) + n - 1, '+');
set(s, sq_x(sq) + n - 1, sq_y(sq) + n - 1, '+'); set(s, sq_x(sq) + n - 1, sq_y(sq) + n - 1, '+');
for (size_t i = 1; i < n - 1; ++i) { for (size_t i = 1; i < n - 1; ++i) {
set(s, sq_x(sq) + i,sq_y(sq), '-'); set(s, sq_x(sq) + i, sq_y(sq), '-');
set(s, sq_x(sq) + i, sq_y(sq) + n - 1, '-'); set(s, sq_x(sq) + i, sq_y(sq) + n - 1, '-');
set(s, sq_x(sq), sq_y(sq) + i, '|'); set(s, sq_x(sq), sq_y(sq) + i, '|');
for (size_t j = 1; j < n - 1; ++j) { for (size_t j = 1; j < n - 1; ++j) {
@@ -144,35 +148,35 @@ namespace {
~Grid() noexcept = default; ~Grid() noexcept = default;
/** Get grid length */ /** Get grid length */
auto length() const noexcept -> size_t { return length_; }
auto end() const noexcept -> size_t { return grid_.size(); } auto end() const noexcept -> size_t { return grid_.size(); }
auto pos_x(Pos const& pos) const noexcept -> size_t { return pos % length_; }
auto pos_y(Pos const& pos) const noexcept -> size_t { return pos / length_; }
/** Add a square to the grid. */ /** Add a square to the grid. */
auto add(Square const &sq) noexcept -> void { auto add(Square const &sq) noexcept -> void {
for (auto i = pos_x(sq.pos()); i < pos_x(sq.pos()) + sq.length(); ++i) { /* One would expect the fastest way to do this would be to have x be the
for (auto j = pos_y(sq.pos()); j < pos_y(sq.pos()) + sq.length(); ++j) { * fastest increasing index so we stores [pos, pos + 1,..., pos+length, ...]
set(i, j, filled); * But experimentation tells us this isn't so, and storing
* [pos, pos + length, ..., pos + 1, ...] is faster!
*/
for (auto x = 0; x < sq.length(); ++x) {
for (auto y = sq.pos(); y < sq.pos() + sq.length() * length_; y += length_) {
grid_[x + y] = filled;
} }
} }
} }
/** Clear a square from the grid. */ /** Clear a square from the grid. */
auto clear(Square const &sq) noexcept -> void { auto clear(Square const &sq) noexcept -> void {
for (auto i = pos_x(sq.pos()); i < pos_x(sq.pos()) + sq.length(); ++i) { for (auto x = 0; x < sq.length(); ++x) {
for (auto j = pos_y(sq.pos()); j < pos_y(sq.pos()) + sq.length(); ++j) { for (auto y = sq.pos(); y < sq.pos() + sq.length() * length_; y += length_) {
set(i, j, empty); grid_[x + y] = empty;
} }
} }
} }
/** \brief Get length of the largest square that fits at \a pos in the grid. /** \brief Get length of the largest square that fits at \a pos in the grid.
*/ */
auto largest_square(Pos const &pos, size_t n) const noexcept -> size_t { auto largest_square(Pos pos, size_t n) const noexcept -> size_t {
assert(x(pos) < length_); assert(pos < end());
assert(y(pos) < length_);
/* Because of how we walk through the grid (starting at 0,0 then increasing /* Because of how we walk through the grid (starting at 0,0 then increasing
* x followed by y) we can assume that if the position (b, y) is clear * x followed by y) we can assume that if the position (b, y) is clear
@@ -181,7 +185,7 @@ namespace {
* This means we only need to look for the first non-clear position along the * This means we only need to look for the first non-clear position along the
* current row. * current row.
*/ */
auto const pos_x = pos % length_; auto const pos_x = pos % length_;
auto const pos_y0 = pos - pos_x; auto const pos_y0 = pos - pos_x;
auto b = pos; auto b = pos;
// Make sure we don't go looking in the next row. // Make sure we don't go looking in the next row.
@@ -192,15 +196,16 @@ namespace {
} }
// Check that this length fits vertically as well. // Check that this length fits vertically as well.
auto const len = b - pos; auto const len = b - pos;
auto const ye = std::min((pos / length_) + len, length_); auto const pos_y = pos / length_;
return ye - pos_y(pos); auto const ye = std::min(pos_y + len, length_);
return ye - pos_y;
} }
/** Get the next position to check starting at pos. /** Get the next position to check starting at pos.
* *
* Returns grid_.length() if no more positions avaialble. * Returns grid_.length() if no more positions avaialble.
*/ */
auto next_pos(Pos const &pos) const noexcept -> Pos { auto next_pos(Pos pos) const noexcept -> Pos {
auto const b = grid_.begin() + pos; auto const b = grid_.begin() + pos;
auto const p = std::find(b, grid_.end(), empty); auto const p = std::find(b, grid_.end(), empty);
return p - grid_.begin(); return p - grid_.begin();
@@ -287,7 +292,10 @@ namespace {
} }
// If there are no squares available of the current size try the next one. // If there are no squares available of the current size try the next one.
if (avail_sqs[idx] == 0) { --idx; continue; } if (avail_sqs[idx] == 0) {
--idx;
continue;
}
/* Place a square of side length idx at pos, push this onto the stack and /* Place a square of side length idx at pos, push this onto the stack and
* set up to look at the next position. * set up to look at the next position.