From ff43ba5287708618e5578dc6e27fb44af24927f7 Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Thu, 4 Sep 2025 11:27:49 +0200 Subject: [PATCH] Make add and clear use indices. This is the last commit of the workstream and we have a ~10% speed improvement. --- main.cc | 58 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/main.cc b/main.cc index f92ab2a..9de046f 100644 --- a/main.cc +++ b/main.cc @@ -26,7 +26,7 @@ namespace { * \param pos Position of closest corner to origin * \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; @@ -57,9 +57,13 @@ namespace { } Results(Results const &other) noexcept = delete; + Results &operator=(Results const &other) noexcept = delete; + Results &operator=(Results &&other) noexcept = default; + Results(Results &&other) noexcept = default; + ~Results() noexcept = default; auto length() const noexcept -> size_t { return length_; } @@ -67,7 +71,7 @@ namespace { /** Output the grid. */ auto output() const -> void { std::string out(length_ * length_, '.'); - for (auto const& sq : squares_) { + for (auto const &sq: squares_) { prettify_sq(out, sq); } for (size_t idx = 0; idx < length_ * length_; idx += length_) { @@ -76,17 +80,17 @@ namespace { } 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(y < length_); assert(grid_[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_y(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 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()) { case 1: set(s, sq_x(sq), sq_y(sq), '*'); break; @@ -102,7 +106,7 @@ namespace { set(s, sq_x(sq), 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) { - 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), sq_y(sq) + i, '|'); for (size_t j = 1; j < n - 1; ++j) { @@ -144,35 +148,35 @@ namespace { ~Grid() noexcept = default; /** Get grid length */ - auto length() const noexcept -> size_t { return length_; } 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. */ auto add(Square const &sq) noexcept -> void { - for (auto i = pos_x(sq.pos()); i < pos_x(sq.pos()) + sq.length(); ++i) { - for (auto j = pos_y(sq.pos()); j < pos_y(sq.pos()) + sq.length(); ++j) { - set(i, j, filled); + /* One would expect the fastest way to do this would be to have x be the + * fastest increasing index so we stores [pos, pos + 1,..., pos+length, ...] + * 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. */ auto clear(Square const &sq) noexcept -> void { - for (auto i = pos_x(sq.pos()); i < pos_x(sq.pos()) + sq.length(); ++i) { - for (auto j = pos_y(sq.pos()); j < pos_y(sq.pos()) + sq.length(); ++j) { - set(i, j, empty); + for (auto x = 0; x < sq.length(); ++x) { + for (auto y = sq.pos(); y < sq.pos() + sq.length() * length_; y += length_) { + grid_[x + y] = empty; } } } /** \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 { - assert(x(pos) < length_); - assert(y(pos) < length_); + auto largest_square(Pos pos, size_t n) const noexcept -> size_t { + assert(pos < end()); /* 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 @@ -181,7 +185,7 @@ namespace { * This means we only need to look for the first non-clear position along the * current row. */ - auto const pos_x = pos % length_; + auto const pos_x = pos % length_; auto const pos_y0 = pos - pos_x; auto b = pos; // Make sure we don't go looking in the next row. @@ -192,15 +196,16 @@ namespace { } // Check that this length fits vertically as well. auto const len = b - pos; - auto const ye = std::min((pos / length_) + len, length_); - return ye - pos_y(pos); + auto const pos_y = pos / length_; + auto const ye = std::min(pos_y + len, length_); + return ye - pos_y; } /** Get the next position to check starting at pos. * * 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 p = std::find(b, grid_.end(), empty); return p - grid_.begin(); @@ -287,7 +292,10 @@ namespace { } // 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 * set up to look at the next position.