compiler-level if문 최적화

MariaDB 프로젝트에서는 likely, unlikely api를 사용하여 분기를 최적화한다.


__builtin_expect

long __builtin_expect (long expression, long value);
  • 컴파일러에게 표현식이 어떤 값일 확률이 높다는 정보를 주는 방법이다. 컴파일러는 이 힌트를 이용해 최적화를 진행한다
    • cpu는 instruction 실행의 병렬성(parallelism)을 높이기 위해 branch prediction을 진행한다
    • instruction의 병렬성은 instruction pipelining을 통해 구현된다
  • value에는 constant literal이 전달되어야 한다
  • expression의 결과가 value일 경우 성능이 향상되지만 그렇지 못할 경우에 대가를 치르기 때문에 신중하게 사용해야 한다
  • switch문에 대해서도 최적화가 가능하다

likely unlikely

/* Add checking if we are using likely/unlikely wrong */
#ifdef CHECK_UNLIKELY
C_MODE_START
extern void init_my_likely(), end_my_likely(FILE *);
extern int my_likely_ok(const char *file_name, uint line);
extern int my_likely_fail(const char *file_name, uint line);
C_MODE_END

#define likely(A) ((A) ? (my_likely_ok(__FILE__, __LINE__),1) : (my_likely_fail(__FILE__, __LINE__), 0))
#define unlikely(A) ((A) ? (my_likely_fail(__FILE__, __LINE__),1) : (my_likely_ok(__FILE__, __LINE__), 0))
/*
  These macros should be used when the check fails often when running benchmarks but
  we know for sure that the check is correct in a production environment
*/
#define checked_likely(A) (A)
#define checked_unlikely(A) (A)
#else
/**
  The semantics of builtin_expect() are that
  1) its two arguments are long
  2) it's likely that they are ==
  Those of our likely(x) are that x can be bool/int/longlong/pointer.
*/

#define likely(x)   __builtin_expect(((x) != 0),1)
#define unlikely(x) __builtin_expect(((x) != 0),0)
#define checked_likely(x) likely(x)
#define checked_unlikely(x) unlikely(x)
#endif /* CHECK_UNLIKELY */
  • MariaDB 프로젝트에서 최적화를 위해 사용하는 __builtin_expect 를 감싼 api
  • CHECK_UNLIKELY 매크로를 켜서 분기 예측의 hit, miss 여부에 관한 통계를 수집해서 unlikely, likely 사용 여부를 결정하는 방식이다

references