libevent的evbuffer之减少内存拷贝
evbuffer是块状数组的数据结构,既是一块块连续的内存链接起来的,evbuffer有许多有用的接口,这里只想对可以减少内存拷贝的接口做详细记录。
1.int evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, struct evbuffer_iovec *vec, int n_vecs);
int evbuffer_commit_space(struct evbuffer *buf, struct evbuffer_iovec *vec, int n_vecs);
evbuffer的其他接口大部分都是把要添加的数据从用户给的buffer拷到自己的buffer,但其实是可以避免这一次拷贝的。evbuffer_reserve_space从evbuffer中保留出一片内存,用户在这片内存上可以直接写数据,待到写完,调用evbuffer_commit_space此时这片内存才真正被“占用”,例如对于bufferevent的output evbuffer,commit后就是告诉要发送数据了。用法可以简化为如下:
/* some codes like OutputBuf = bufferevent_get_output(bufev); */
struct evbuffer_iovec v;
/* Reserve 2048 bytes */
n = evbuffer_reserve_space(OutputBuf, 2048, &v, 1);
if (n <= 0)
/* reserve error */
return;
assert(v.iov_len == 2048);
/* Process data in reserved buf */
char *DatBuf = v.iov_base;
for (i = 0; i < 2048; i ++)
DatBuf[i] = gen_data(i);
/* Send datas */
v.iov_len = data_len_to_send;
n = evbuffer_commit_space(OutputBuf, &v, 1);
if (n < 0)
return; /* commit error */
2.typedef void (*evbuffer_ref_cleanup_cb)(const void *data, size_t datalen, void *extra);
int evbuffer_add_reference(struct evbuffer *outbuf, const void *data, size_t datlen, evbuffer_ref_cleanup_cb cleanupfn, void *extra);
这个接口是官方文档推荐在避免内存拷贝时使用的接口,在evbuffer中直接挂接用户提供的buffer,该buffer由用户分配,当然也该由用户释放,evbuffer_ref_cleanup_cb,在evbuffer_drain时会被回调,告诉用户你的这块内存我不用了,你释放吧。这个接口可以配合用户的自定义内存池来使用,简单用法示例如下:
/* some codes like OutputBuf = bufferevent_get_output(bufev); */
...
char *DatBuf = mem_pool_alloc(2048);
/* Process data in alloced buf */
char *DatBuf = v.iov_base;
for (i = 0; i < 2048; i ++)
DatBuf[i] = gen_data(i);
evbuffer_add_reference(OutputBuf, DatBuf, 2048, cleanupfn, NULL);
...
/* cleanupfn like this */
void cleanupfn(const void *data, size_t datalen, void *extra)
{
mem_pool_free(data, datalen);
}