PHP扩展开发——EXPECTED与UNEXPECTED

在PHP扩展开发中,经常会看到EXPECTEDUNEXPECTED两个宏的使用,但是却不清楚它们的用途,这次就来拨开它们的面纱。

首先举个在PHP底层中使用到EXPECTEDUNEXPECTED的例子:

if (UNEXPECTED(_num_args < _min_num_args) || \
    (UNEXPECTED(_num_args > _max_num_args) && \
        EXPECTED(_max_num_args >= 0))) { \
    if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
        if (_flags & ZEND_PARSE_PARAMS_THROW) { \
            zend_wrong_parameters_count_exception(_min_num_args, _max_num_args); \
        } else { \
            zend_wrong_parameters_count_error(_min_num_args, _max_num_args); \
        } \
    } \
    error_code = ZPP_ERROR_FAILURE; \
    break; \
} \

这是Zend提供用于解析函数参数的一个宏的部分代码,大致代码逻辑就是判断参数数量是否不正确。在zend_API.h中,EXPECTEDUNEXPECTED是这样定义的:

#if PHP_HAVE_BUILTIN_EXPECT
# define EXPECTED(condition)   __builtin_expect(!!(condition), 1)
# define UNEXPECTED(condition) __builtin_expect(!!(condition), 0)
#else
# define EXPECTED(condition)   (condition)
# define UNEXPECTED(condition) (condition)
#endif

可以发现它们其实是对__builtin_expect函数的一个封装,所以最终就是要搞清楚__builtin_expect到底是干嘛的。

__builtin_expect()是GCC v2.96版本引入的内建函数,提供给程序员做分支预测。

简单来说,就是程序员告诉编译器,后续的条件判断中可能性最大的条件分支是什么,方便编译器针对代码进行优化,将可能性最大的分支代码放在前面,降低指令跳转的可能性,以优化性能。例如:

// 假如有a变量
if (EXPECTED(a == 0)) {
    // ...
} else {
    // ...
}

以上代码表示,a == 0的可能性更大,即更有可能执行if分支代码,此时,编译器就会将if分支代码放在前面。反之,如果使用UNEXPECTED,就会将else分支代码放在前面。