mirror of https://github.com/OpenTTD/OpenTTD.git
Codechange: add annotation to selectively force inlining in debug build
This commit is contained in:
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);
|
||||
}
|
||||
|
|
45
src/stdafx.h
45
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. */
|
||||
|
|
|
@ -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…
Reference in New Issue