Codechange: add annotation to selectively force inlining in debug build

pull/484/head
Rubidium 1 year ago committed by rubidium42
parent df89c34e03
commit b7a5d8e296

@ -29,7 +29,7 @@
* @return The selected bits, aligned to a LSB.
*/
template <typename T>
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 <typename T>
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;
}

@ -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<Tthis &>(*this); }
constexpr Tthis &operator =(StrongTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
debug_inline constexpr Tthis &operator =(const StrongTypedef &rhs) { this->value = rhs.value; return static_cast<Tthis &>(*this); }
debug_inline constexpr Tthis &operator =(StrongTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
debug_inline constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
explicit constexpr operator T() const { return this->value; }
@ -56,6 +56,16 @@ template <class T, class Tthis>
struct StrongIntegralTypedef : StrongTypedef<T, Tthis> {
using StrongTypedef<T, Tthis>::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<T, Tthis>(value) {}
debug_inline constexpr Tthis &operator =(const StrongIntegralTypedef &rhs) { this->value = rhs.value; return static_cast<Tthis &>(*this); }
debug_inline constexpr Tthis &operator =(StrongIntegralTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
debug_inline constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
constexpr Tthis &operator ++() { this->value++; return static_cast<Tthis &>(*this); }
constexpr Tthis &operator --() { this->value--; return static_cast<Tthis &>(*this); }
constexpr Tthis operator ++(int) { auto res = static_cast<Tthis &>(*this); this->value++; return res; }

@ -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();
}

@ -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; }

@ -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);
}

@ -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);
}

@ -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. */

@ -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;
}

@ -85,8 +85,18 @@ enum TropicZone {
struct TileIndex : StrongIntegralTypedef<uint32, TileIndex> {
using StrongIntegralTypedef<uint32, TileIndex>::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<uint32, TileIndex>(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 ==;

@ -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);
}

Loading…
Cancel
Save