diff --git a/src/core/bitmath_func.hpp b/src/core/bitmath_func.hpp index 979d9b73b7..745a32afc4 100644 --- a/src/core/bitmath_func.hpp +++ b/src/core/bitmath_func.hpp @@ -29,7 +29,7 @@ * @return The selected bits, aligned to a LSB. */ template -static inline uint GB(const T x, const uint8 s, const uint8 n) +debug_inline static uint GB(const T x, const uint8 s, const uint8 n) { return (x >> s) & (((T)1U << n) - 1); } @@ -100,7 +100,7 @@ static inline T AB(T &x, const uint8 s, const uint8 n, const U i) * @return True if the bit is set, false else. */ template -static inline bool HasBit(const T x, const uint8 y) +debug_inline static bool HasBit(const T x, const uint8 y) { return (x & ((T)1U << y)) != 0; } diff --git a/src/core/strong_typedef_type.hpp b/src/core/strong_typedef_type.hpp index b5df28b2fb..179d2b3a0d 100644 --- a/src/core/strong_typedef_type.hpp +++ b/src/core/strong_typedef_type.hpp @@ -29,15 +29,15 @@ struct StrongTypedef : StrongTypedefBase { T value{}; ///< Backing storage field. - constexpr StrongTypedef() = default; - constexpr StrongTypedef(const StrongTypedef &o) = default; - constexpr StrongTypedef(StrongTypedef &&o) = default; + debug_inline constexpr StrongTypedef() = default; + debug_inline constexpr StrongTypedef(const StrongTypedef &o) = default; + debug_inline constexpr StrongTypedef(StrongTypedef &&o) = default; - constexpr StrongTypedef(const T &value) : value(value) {} + debug_inline constexpr StrongTypedef(const T &value) : value(value) {} - constexpr Tthis &operator =(const StrongTypedef &rhs) { this->value = rhs.value; return static_cast(*this); } - constexpr Tthis &operator =(StrongTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast(*this); } - constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast(*this); } + debug_inline constexpr Tthis &operator =(const StrongTypedef &rhs) { this->value = rhs.value; return static_cast(*this); } + debug_inline constexpr Tthis &operator =(StrongTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast(*this); } + debug_inline constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast(*this); } explicit constexpr operator T() const { return this->value; } @@ -56,6 +56,16 @@ template struct StrongIntegralTypedef : StrongTypedef { using StrongTypedef::StrongTypedef; + debug_inline constexpr StrongIntegralTypedef() = default; + debug_inline constexpr StrongIntegralTypedef(const StrongIntegralTypedef &o) = default; + debug_inline constexpr StrongIntegralTypedef(StrongIntegralTypedef &&o) = default; + + debug_inline constexpr StrongIntegralTypedef(const T &value) : StrongTypedef(value) {} + + debug_inline constexpr Tthis &operator =(const StrongIntegralTypedef &rhs) { this->value = rhs.value; return static_cast(*this); } + debug_inline constexpr Tthis &operator =(StrongIntegralTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast(*this); } + debug_inline constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast(*this); } + constexpr Tthis &operator ++() { this->value++; return static_cast(*this); } constexpr Tthis &operator --() { this->value--; return static_cast(*this); } constexpr Tthis operator ++(int) { auto res = static_cast(*this); this->value++; return res; } diff --git a/src/map_func.h b/src/map_func.h index f5279fe5b7..3ebfb349c6 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -51,7 +51,7 @@ public: * @note try to avoid using this one * @return 2^"return value" == Map::SizeX() */ - static inline uint LogX() + debug_inline static uint LogX() { return Map::log_x; } @@ -70,7 +70,7 @@ public: * Get the size of the map along the X * @return the number of tiles along the X of the map */ - static inline uint SizeX() + debug_inline static uint SizeX() { return Map::size_x; } @@ -88,7 +88,7 @@ public: * Get the size of the map * @return the number of tiles of the map */ - static inline uint Size() + debug_inline static uint Size() { return Map::size; } @@ -97,7 +97,7 @@ public: * Gets the maximum X coordinate within the map, including MP_VOID * @return the maximum X coordinate */ - static inline uint MaxX() + debug_inline static uint MaxX() { return Map::SizeX() - 1; } @@ -179,7 +179,7 @@ typedef int32 TileIndexDiff; * @param y The y coordinate of the tile * @return The TileIndex calculated by the coordinate */ -static inline TileIndex TileXY(uint x, uint y) +debug_inline static TileIndex TileXY(uint x, uint y) { return (y << Map::LogX()) + x; } @@ -210,7 +210,7 @@ static inline TileIndexDiff TileDiffXY(int x, int y) * @param y The virtual y coordinate of the tile. * @return The TileIndex calculated by the coordinate. */ -static inline TileIndex TileVirtXY(uint x, uint y) +debug_inline static TileIndex TileVirtXY(uint x, uint y) { return (y >> 4 << Map::LogX()) + (x >> 4); } @@ -221,7 +221,7 @@ static inline TileIndex TileVirtXY(uint x, uint y) * @param tile the tile to get the X component of * @return the X component */ -static inline uint TileX(TileIndex tile) +debug_inline static uint TileX(TileIndex tile) { return tile.value & Map::MaxX(); } @@ -231,7 +231,7 @@ static inline uint TileX(TileIndex tile) * @param tile the tile to get the Y component of * @return the Y component */ -static inline uint TileY(TileIndex tile) +debug_inline static uint TileY(TileIndex tile) { return tile.value >> Map::LogX(); } diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index ed5f181437..d50ae13d26 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -86,11 +86,11 @@ struct CFollowTrackT m_railtypes = railtype_override; } - inline static TransportType TT() { return Ttr_type_; } - inline static bool IsWaterTT() { return TT() == TRANSPORT_WATER; } - inline static bool IsRailTT() { return TT() == TRANSPORT_RAIL; } + debug_inline static TransportType TT() { return Ttr_type_; } + debug_inline static bool IsWaterTT() { return TT() == TRANSPORT_WATER; } + debug_inline static bool IsRailTT() { return TT() == TRANSPORT_RAIL; } inline bool IsTram() { return IsRoadTT() && RoadTypeIsTram(RoadVehicle::From(m_veh)->roadtype); } - inline static bool IsRoadTT() { return TT() == TRANSPORT_ROAD; } + debug_inline static bool IsRoadTT() { return TT() == TRANSPORT_ROAD; } inline static bool Allow90degTurns() { return T90deg_turns_allowed_; } inline static bool DoTrackMasking() { return Tmask_reserved_tracks; } diff --git a/src/rail_map.h b/src/rail_map.h index bd1d3c7492..5717da3613 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -33,7 +33,7 @@ enum RailTileType { * @pre IsTileType(t, MP_RAILWAY) * @return the RailTileType */ -static inline RailTileType GetRailTileType(TileIndex t) +debug_inline static RailTileType GetRailTileType(TileIndex t) { assert(IsTileType(t, MP_RAILWAY)); return (RailTileType)GB(_m[t].m5, 6, 2); @@ -46,7 +46,7 @@ static inline RailTileType GetRailTileType(TileIndex t) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile is normal rail (with or without signals) */ -static inline bool IsPlainRail(TileIndex t) +debug_inline static bool IsPlainRail(TileIndex t) { RailTileType rtt = GetRailTileType(t); return rtt == RAIL_TILE_NORMAL || rtt == RAIL_TILE_SIGNALS; @@ -57,7 +57,7 @@ static inline bool IsPlainRail(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is normal rail (with or without signals) */ -static inline bool IsPlainRailTile(TileIndex t) +debug_inline static bool IsPlainRailTile(TileIndex t) { return IsTileType(t, MP_RAILWAY) && IsPlainRail(t); } @@ -92,7 +92,7 @@ static inline void SetHasSignals(TileIndex tile, bool signals) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile is a rail depot */ -static inline bool IsRailDepot(TileIndex t) +debug_inline static bool IsRailDepot(TileIndex t) { return GetRailTileType(t) == RAIL_TILE_DEPOT; } @@ -102,7 +102,7 @@ static inline bool IsRailDepot(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is a rail depot */ -static inline bool IsRailDepotTile(TileIndex t) +debug_inline static bool IsRailDepotTile(TileIndex t) { return IsTileType(t, MP_RAILWAY) && IsRailDepot(t); } diff --git a/src/road_map.h b/src/road_map.h index 6be02e42f6..3114bcc18a 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -49,7 +49,7 @@ static inline bool MayHaveRoad(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return The road tile type. */ -static inline RoadTileType GetRoadTileType(TileIndex t) +debug_inline static RoadTileType GetRoadTileType(TileIndex t) { assert(IsTileType(t, MP_ROAD)); return (RoadTileType)GB(_m[t].m5, 6, 2); @@ -61,7 +61,7 @@ static inline RoadTileType GetRoadTileType(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if normal road. */ -static inline bool IsNormalRoad(TileIndex t) +debug_inline static bool IsNormalRoad(TileIndex t) { return GetRoadTileType(t) == ROAD_TILE_NORMAL; } @@ -71,7 +71,7 @@ static inline bool IsNormalRoad(TileIndex t) * @param t Tile to query. * @return True if normal road tile. */ -static inline bool IsNormalRoadTile(TileIndex t) +debug_inline static bool IsNormalRoadTile(TileIndex t) { return IsTileType(t, MP_ROAD) && IsNormalRoad(t); } @@ -103,7 +103,7 @@ static inline bool IsLevelCrossingTile(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if road depot. */ -static inline bool IsRoadDepot(TileIndex t) +debug_inline static bool IsRoadDepot(TileIndex t) { return GetRoadTileType(t) == ROAD_TILE_DEPOT; } @@ -113,7 +113,7 @@ static inline bool IsRoadDepot(TileIndex t) * @param t Tile to query. * @return True if road depot tile. */ -static inline bool IsRoadDepotTile(TileIndex t) +debug_inline static bool IsRoadDepotTile(TileIndex t) { return IsTileType(t, MP_ROAD) && IsRoadDepot(t); } diff --git a/src/stdafx.h b/src/stdafx.h index 395a3bf6e4..f62a83e83c 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -323,6 +323,51 @@ # define PRINTF_SIZEX "%zX" #endif +/* + * When making a (pure) debug build, the compiler will by default disable + * inlining of functions. This has a detremental effect on the performance of + * debug builds, especially when more and more trivial (wrapper) functions get + * added to the code base. + * Take for example the savegame called "Wentbourne", when running this game + * for 100 ticks with the null video driver a number of fairly trivial + * functions show up on top. The most common one is the implicit conversion + * operator of TileIndex to unsigned int, which takes up over 5% of the total + * run time and functionally does absolutely nothing. The remaining functions + * for the top 5 are GB, GetTileType, Map::Size and IsTileType to a total of + * about 12.5% of the game's total run time. + * It is possible to still force inlining in the most commonly used compilers, + * but that is at the cost of some problems with debugging due to the forced + * inlining. However, the performance benefit can be enormous; when forcing + * inlining for the previously mentioned top 5, the debug build ran about 15% + * quicker. + * The following debug_inline annotation may be added to functions comply + * with the following preconditions: + * 1: the function takes more than 0.5% of a profiled debug runtime + * 2: the function does not modify the game state + * 3: the function does not contain selection or iteration statements, + * i.e. no if, switch, for, do, while, etcetera. + * 4: the function is one line of code, excluding assertions. + * 5: the function is defined in a header file. + * The debug_inline annotation must be placed in front of the function, i.e. + * before the optional static or constexpr modifier. + */ +#if !defined(_DEBUG) || defined(NO_DEBUG_INLINE) +/* + * Do not force inlining when not in debug. This way we do not work against + * any carefully designed compiler optimizations. + */ +#define debug_inline inline +#elif defined(__clang__) || defined(__GNUC__) +#define debug_inline [[gnu::always_inline]] inline +#else +/* + * MSVC explicitly disables inlining, even forced inlining, in debug builds + * so __forceinline makes no difference compared to inline. Other unknown + * compilers can also just fallback to a normal inline. + */ +#define debug_inline inline +#endif + typedef unsigned char byte; /* This is already defined in unix, but not in QNX Neutrino (6.x) or Cygwin. */ diff --git a/src/tile_map.h b/src/tile_map.h index b02b957fa2..fb27154ab6 100644 --- a/src/tile_map.h +++ b/src/tile_map.h @@ -26,7 +26,7 @@ * @return the height of the tile * @pre tile < Map::Size() */ -static inline uint TileHeight(TileIndex tile) +debug_inline static uint TileHeight(TileIndex tile) { assert(tile < Map::Size()); return _m[tile].height; @@ -93,7 +93,7 @@ static inline uint TilePixelHeightOutsideMap(int x, int y) * @return The tiletype of the tile * @pre tile < Map::Size() */ -static inline TileType GetTileType(TileIndex tile) +debug_inline static TileType GetTileType(TileIndex tile) { assert(tile < Map::Size()); return (TileType)GB(_m[tile].type, 4, 4); @@ -147,7 +147,7 @@ static inline void SetTileType(TileIndex tile, TileType type) * @param type The type to check against * @return true If the type matches against the type of the tile */ -static inline bool IsTileType(TileIndex tile, TileType type) +debug_inline static bool IsTileType(TileIndex tile, TileType type) { return GetTileType(tile) == type; } diff --git a/src/tile_type.h b/src/tile_type.h index 6b5514d90e..db7ece562c 100644 --- a/src/tile_type.h +++ b/src/tile_type.h @@ -85,8 +85,18 @@ enum TropicZone { struct TileIndex : StrongIntegralTypedef { using StrongIntegralTypedef::StrongIntegralTypedef; + debug_inline constexpr TileIndex() = default; + debug_inline constexpr TileIndex(const TileIndex &o) = default; + debug_inline constexpr TileIndex(TileIndex &&o) = default; + + debug_inline constexpr TileIndex(const uint32 &value) : StrongIntegralTypedef(value) {} + + debug_inline constexpr TileIndex &operator =(const TileIndex &rhs) { this->value = rhs.value; return *this; } + debug_inline constexpr TileIndex &operator =(TileIndex &&rhs) { this->value = std::move(rhs.value); return *this; } + debug_inline constexpr TileIndex &operator =(const uint32 &rhs) { this->value = rhs; return *this; } + /** Implicit conversion to the base type for e.g. array indexing. */ - constexpr operator uint32() const { return this->value; } + debug_inline constexpr operator uint32() const { return this->value; } /* Import operators from the base class into our overload set. */ using StrongIntegralTypedef::operator ==; diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 60c36761bf..5d2ebba7fa 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -512,7 +512,7 @@ public: * Check if the vehicle is a ground vehicle. * @return True iff the vehicle is a train or a road vehicle. */ - inline bool IsGroundVehicle() const + debug_inline bool IsGroundVehicle() const { return this->type == VEH_TRAIN || this->type == VEH_ROAD; } @@ -924,7 +924,7 @@ public: * Check if the vehicle is a front engine. * @return Returns true if the vehicle is a front engine. */ - inline bool IsFrontEngine() const + debug_inline bool IsFrontEngine() const { return this->IsGroundVehicle() && HasBit(this->subtype, GVSF_FRONT); }