nginx细节
nginx中有很多细节的地方做了性能优化,举几个例子。
例子1:
自定义字符串类型,在/src/core/config.h中,对字符串进行了封装,包含长度,和具体字符
typedef struct { size_t len; u_char *data; } ngx_str_t; typedef struct { ngx_str_t key; ngx_str_t value; } ngx_keyval_t;
这样做的好处是,后续的字符操作,长度不需要再重复计算,而是直接引用,而且很多字符串string.h里面的操作差不多都重新实现了一遍。
例子2:
错误码的预定义,在main里面,刚开始会调用ngx_strerror_init,这个函数做的事情是把系统的错误码和错误信息,存在ngx_sys_errlist中,省去了出错时候再调用strerror()查找错误原因的开销,能减少系统调用就要避免。
ngx_int_t ngx_strerror_init(void) { char *msg; u_char *p; size_t len; ngx_err_t err; /* * ngx_strerror() is not ready to work at this stage, therefore, * malloc() is used and possible errors are logged using strerror(). */ //预分配NGX_SYS_NERR个字符对象,NGX_SYS_NERR在linux中是132 len = NGX_SYS_NERR * sizeof(ngx_str_t); //分配全局错误列表空间 ngx_sys_errlist = malloc(len); if (ngx_sys_errlist == NULL) { goto failed; } for (err = 0; err < NGX_SYS_NERR; err++) { //获取系统的每一个错误编号原因和长度 msg = strerror(err); len = ngx_strlen(msg); p = malloc(len); if (p == NULL) { goto failed; } //赋值错误id和错误原因到全局列表中 ngx_memcpy(p, msg, len); ngx_sys_errlist[err].len = len; ngx_sys_errlist[err].data = p; } return NGX_OK; failed: err = errno; ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err)); return NGX_ERROR; }
在这里nginx并没有使用其内存池,而是使用系统默认的malloc进行内存分配,因为在这里程序还没有创建内存池,而后续的初始化工作,可能出现未知的错误,那么,该处初始化的错误数组就可以派上用场。
例子3:
nignx的内存管理也用到了自己的内存池,但是注意到在申请内存时,有两个函数:ngx_palloc和ngx_pnalloc,两者的区别是一个进行了对齐,一个没有对齐,取整可以降低CPU读取内存的次数,提高性能。
对齐使用了ngx_align_ptr(p,a),把指针p按照a的size进行对齐。
#define ngx_align_ptr(p, a) \ (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
总之nginx里面很多小的地方都做的很极致,希望有时间进一步发现,nginx的文章比较多,读起来比haproxy方便很多。
发表评论