У меня есть обычай ASSERT(...)
макрос, который я использую в приложении C ++.
#include <stdlib.h>
#include <iostream>
/// ASSERT(expr) checks if expr is true. If not, error details are logged
/// and the process is exited with a non-zero code.
#ifdef INCLUDE_ASSERTIONS
#define ASSERT(expr) \
if (!(expr)) { \
char buf[4096]; \
snprintf (buf, 4096, "Assertion failed in \"%s\", line %d\n%s\n", \
__FILE__, __LINE__, #expr); \
std::cerr << buf; \
::abort(); \
} \
else // This 'else' exists to catch the user's following semicolon
#else
#define ASSERT(expr)
#endif
Недавно я читал код модуля ядра Linux и наткнулся на существование likely(...)
а также unlikely(...)
макросы. Они дают подсказку процессору, что данная ветвь более вероятна, и что конвейер должен оптимизироваться для этого пути.
Утверждения, по определению, должны быть оценены как истинные (т.е. likely
).
Могу ли я предоставить аналогичную подсказку в моем ASSERT
макрос? Какой основной механизм здесь?
Очевидно, что я буду измерять разницу в производительности, но теоретически это должно иметь значение?
Я запускаю свой код только в Linux, но было бы интересно узнать, есть ли кросс-платформенный способ сделать это тоже. Я также использую gcc, но хотел бы также поддержать clang.
Увеличение производительности вряд ли будет значительным, но именно так определяются эти макросы ядра Linux:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
Таким образом, вы можете изменить свое состояние следующим образом (при условии, что expr
как ожидается, будет правдой и, следовательно, !(expr)
как ожидается, будет ложным):
if (__builtin_expect(!(expr), 0)) {
Или вы можете определить те же макросы, что и ядро, и использовать их для лучшей читаемости.
Это встроенный gcc, поэтому не переносимый, конечно.
это предполагает, что Clang также поддерживает встроенный. В противном случае вы можете использовать вышеупомянутые макросы и условно определять их как #define likely(x) (x)
на компиляторах, которые не поддерживают встроенные.
В вашем случае предсказание будет хорошим (либо это, либо вы прерываете работу), поэтому не должно быть риска пессимизации, но если вы все-таки подумаете об использовании встроенной функции более широко, вот несколько советов от gcc документация:
В общем, вы должны предпочесть использовать реальную обратную связь профиля (-fprofile-arcs), так как программисты, как известно, плохо предсказывают, как на самом деле работают их программы.
Для многих процессоров, likely
а также unlikely
(или что-нибудь еще в этом отношении) не предоставляют подсказку ветвления для ЦП (только для компилятора, который может использовать его для оптимизации по-разному, аналогично оптимизации по профилю) по той простой причине, что нет способа сделать это ,
Например, подсказки веток определены для x86 начиная с P4. До этого они не имели никакого эффекта, но, что еще хуже, они ни на что не влияли, кроме P4. Так что они бесполезны (но тратят пространство и пропускную способность), и, насколько я знаю, GCC их не излучает.
У ARM нет (еще?) Подсказок о ветвях. У PPC, IA64 и SPARC действительно есть подсказки, я не знаю, использует ли GCC likely
а также unlikely
хотя для них, но, по крайней мере, это возможно.
Нет необходимости в каких-либо дополнительных аннотациях. Компилятор уже знает о abort
будучи призванным очень редко (максимум один раз за выполнение программы), и поэтому компилятор будет рассматривать ветку, содержащую abort
как маловероятная ветвь в любом случае. Вы можете убедиться в этом, посмотрев декларацию abort
, В glibc он объявлен как
extern void abort (void) __THROW __attribute__ ((__noreturn__));
и в Visual Studio 2013:
_CRTIMP __declspec(noreturn) void __cdecl abort(void);