c++ - 为什么这个 noexcept 对性能如此重要,而类似的其他 noexcept 却没有?

标签 c++ performance c++11 noexcept

我有一段通用代码,其性能对我来说很重要,因为我面临着与用 C 编写的著名手工代码的运行时间相匹配的挑战。在开始使用 noexcept 之前,我的代码运行时间为 4.8 秒。通过将 noexcept 放在我能想到的每个地方(我知道这不是一个好主意,但我这样做是为了学习),代码加速了 3.3 秒。然后我开始恢复更改,直到获得更好的性能(3.1 秒)并保持单个 noexcept!

问题是:为什么这个特殊的 noexcept 有这么大的帮助?在这里:

static const AllActions& allActions_() noexcept {
    static const AllActions instance = computeAllActions();
    return instance;
}

有趣的是,我还有另一个类似的功能,没有 noexcept 也很好(即放置 noexcept 不会提高性能):

static const AllMDDeltas& mdDeltas_() {
    static const AllMDDeltas instance = computeAllMDDeltas();
    return instance;
}

我的代码(执行递归深度优先搜索)多次调用这两个函数,因此第二个函数对于整体性能与第一个函数一样重要。

P.S. 下面是更多上下文的相关代码(引用的函数和它们调用的函数在 list 的末尾):

/// The sliding-tile puzzle domain.
/// \tparam nRows Number of rows on the board.
/// \tparam nRows Number of columns on the board.
template <int nRows, int nColumns>
struct SlidingTile : core::sb::DomainBase {
    /// The type representing the cost of actions in the domain. Every
    /// domain must provide this name.
    using CostType = int;

    using SNeighbor =
        core::sb::StateNeighbor<SlidingTile>; ///< State neighbor type.
    using ANeighbor =
        core::sb::ActionNeighbor<SlidingTile>; ///< Action neighbor type.

    /// The type for representing an action. The position of the tile being moved.
    using Action = int;

    /// Number of positions.
    static constexpr int size_ = nRows * nColumns;

    /// The type for the vector of actions for a given position of the blank.
    using BlankActions = std::vector<ANeighbor>;

    /// The type for all the actions in the domain.
    using AllActions = std::array<BlankActions, size_>;

    /// The type for two-dimension array of Manhattan distance heuristic deltas
    /// for a given tile. The indexes are from and to of an action.
    using TileMDDeltas = std::array<std::array<int, size_>, size_>;

    /// The type for all Manhattan distance heuristic deltas.
    using AllMDDeltas = std::array<TileMDDeltas, size_>;

    /// The type for raw state representation.
    using Board = std::array<int, size_>;

    /// Initializes the ordered state.
    SlidingTile() {
        int i = -1;
        for (auto &el : tiles_) el = ++i;
    }

    /// Initializes the state from a string, e.g. "[1, 4, 2, 3, 0, 5]" or "1 4 2
    /// 4 0 5" for 3x2 board.
    /// \param s The string.
    SlidingTile(const std::string &s) {
        int i = -1;
        for (auto el : core::util::split(s, {' ', ',', '[', ']'})) {
            tiles_[++i] = std::stoi(el);
            if (tiles_[i] == 0) blank_ = i;
        }
    }

    /// The default copy constructor.
    SlidingTile(const SlidingTile &) = default;

    /// The default assignment operator.
    /// \return Reference to the assigned state.
    SlidingTile &operator=(const SlidingTile &) = default;

    /// Returns the array of tiles at each position.
    /// \return The raw representation of the state, which is the array of tiles
    /// at each position..
    const Board &getTiles() const { return tiles_; }

    /// Applies an action to the state.
    /// \param a The action to be applied, i.e. the next position of the blank
    /// on the board.
    /// \return The state after the action.
    SlidingTile &apply(Action a) {
        tiles_[blank_] = tiles_[a];
        blank_ = a;
        return *this;
    }

    /// Returns the reverse of the given action in this state.
    /// \param a The action whose reverse is to be returned.
    /// \return The reverse of the given action.
    Action reverseAction(Action a) const {
        (void)a;
        return blank_;
    }

    /// Computes the state neighbors of the state.
    /// \return Vector of state neighbors of the state.
    std::vector<SNeighbor> stateSuccessors() const {
        std::vector<SNeighbor> res;
        for (auto a : actionSuccessors()) {
            auto n = SlidingTile{*this}.apply(a.action());
            res.push_back(std::move(n));
        }
        return res;
    }

    /// Computes the action neighbors of the state.
    /// \return Vector of action neighbors of the state.
    const std::vector<ANeighbor> &actionSuccessors() const {
        return allActions_()[blank_];
    }

    /// The change in the Manhattan distance heuristic to the goal state with
    /// ordered tiles and the blank at position 0 due to applying the given action.
    /// \param a The given action.
    /// \return The change in the Manhattan distance heuristic to the goal state
    /// with ordered pancake due to applying the given action.
    int mdDelta(Action a) const {
        return mdDeltas_()[tiles_[a]][a][blank_];
    }

    /// Computes the Manhattan distance heuristic to the goal state with
    /// ordered tiles and the blank at position 0.
    /// \return The Manhattan distance heuristic to the goal state with
    /// ordered tiles and the blank at position 0.
    int mdHeuristic() const {
        int res = 0;
        for (int pos = 0; pos < size_; ++pos)
            if (pos != blank_)
                res += rowDist(pos, tiles_[pos]) + colDist(pos, tiles_[pos]);
        return res;
    }

    /// Computes the hash-code of the state.
    /// \return The hash-code of the state.
    std::size_t hash() const {
        boost::hash<Board> v_hash;
        return v_hash(tiles_);
    }

    /// Dumps the state to the given stream.
    /// \tparam The stream type.
    /// \param o The stream.
    /// \return The modified stream.
    template <class Stream> Stream &dump(Stream &o) const {
        return o << tiles_;
    }

    /// Randomly shuffles the tiles.
    void shuffle() {
        auto old = tiles_;
        while (old == tiles_)
            std::random_shuffle(tiles_.begin(), tiles_.end());
    }

    /// The equality operator.
    /// \param rhs The right-hand side of the operator.
    /// \return \c true if the two states compare equal and \c false
    /// otherwise.
    bool operator==(const SlidingTile &rhs) const {
        if (blank_ != rhs.blank_) return false;
        for (int i = 0; i < size_; ++i)
            if (i != blank_ && tiles_[i] != rhs.tiles_[i]) return false;
        return true;
    }

    /// Returns a random state.
    /// \return A random state.
    static SlidingTile random() {
        SlidingTile res{};
        res.shuffle();
        return res;
    }

private:
    /// Tile at each position. This does not include the position of the blank,
    /// which is stored separately.
    std::array<int, size_> tiles_;

    /// Blank position.
    int blank_{};

    /// Computes the row number corresponding to the given position.
    /// \return The row number corresponding to the given position.
    static int row(int pos) { return pos / nColumns; }

    /// The difference between the row numbers corresponding to the two given
    /// positions.
    /// \return The difference between the row numbers corresponding to the two
    /// given positions.
    static int rowDiff(int pos1, int pos2) { return row(pos1) - row(pos2); }

    /// The distance between the row numbers corresponding to the two given
    /// positions.
    /// \return The distance between the row numbers corresponding to the two
    /// given positions.
    static int rowDist(int pos1, int pos2) {
        return std::abs(rowDiff(pos1, pos2));
    }

    /// Computes the column number corresponding to the given position.
    /// \return The column number corresponding to the given position.
    static int col(int pos) { return pos % nColumns; }

    /// The difference between the column numbers corresponding to the two given
    /// positions.
    /// \return The difference between the column numbers corresponding to the
    /// two given positions.
    static int colDiff(int pos1, int pos2) { return col(pos1) - col(pos2); }

    /// The distance between the column numbers corresponding to the two given
    /// positions.
    /// \return The distance between the column numbers corresponding to the
    /// two given positions.
    static int colDist(int pos1, int pos2) {
        return std::abs(colDiff(pos1, pos2));
    }

    /// Computes the actions available for each position of the blank.
    static AllActions computeAllActions() {
        AllActions res;
        for (int blank = 0; blank < size_; ++blank) {
            // the order is compatible with the code of Richard Korf.
            if (blank > nColumns - 1)
                res[blank].push_back(Action{blank - nColumns});
            if (blank % nColumns > 0)
                res[blank].push_back(Action{blank - 1});
            if (blank % nColumns < nColumns - 1)
                res[blank].push_back(Action{blank + 1});
            if (blank < size_ - nColumns)
                res[blank].push_back(Action{blank + nColumns});
        }
        return res;
    }

    /// Computes the heuristic updates for all the possible moves.
    /// \return The heuristic updates for all the possible moves.
    static AllMDDeltas computeAllMDDeltas() {
        AllMDDeltas res;
        for (int tile = 1; tile < size_; ++tile) {
            for (int blank = 0; blank < size_; ++blank) {
                for (const ANeighbor &a: allActions_()[blank]) {
                    int from = a.action(), to = blank;
                    res[tile][from][to] =
                        (rowDist(tile, to) - rowDist(tile, from)) +
                        (colDist(tile, to) - colDist(tile, from));
                }
            }
        }
        return res;
    }

    /// Returns all the actions.
    /// \note See http://stackoverflow.com/a/42208278/2725810
    static const AllActions& allActions_() noexcept {
        static const AllActions instance = computeAllActions();
        return instance;
    }

    /// Returns all the updates of the MD heuristic.
    static const AllMDDeltas& mdDeltas_() {
        static const AllMDDeltas instance = computeAllMDDeltas();
        return instance;
    }
};

最佳答案

computeAllMDDeltas 不同,函数 computeAllActions 包含对 push_back 的一些调用,这可能会执行一些内存分配。如果底层分配器出现异常,例如内存不足,这可能会抛出。这是编译器无法优化的东西,因为它取决于运行时参数。

添加noexcept告诉编译器不能出现这些错误,这样他就可以省略异常处理的代码。

关于c++ - 为什么这个 noexcept 对性能如此重要,而类似的其他 noexcept 却没有?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42809905/

相关文章:

c++ - 为什么 ‘Char’标识符在VS2013中无法识别?

c++ - 信号未发出

c++ - 根据第一个表的信息选择第二个表时如何短语sql查询

database - 在 $facet 中使用 $sort 和 $limit 时慢 Mongo 聚合

c++ - 我应该如何正确播种 C++11 std::default_random_engine?

c++ - C++11 是否支持 C11 的新特性?

c# - SQLBulkCopy 或批量插入

performance - 为什么批处理模式比 parfor 快这么多?

c++ - 为 codechef 中的每个代码获取 SIGEMT 错误

C++ std::async 调用类成员方法看不到变量变化