TAILQ
隊列是FreeBSD
內核中的一種隊列數據結構,在一些著名的開源庫中(如DPDK
,libevent
)有普遍的應用。
TAILQ
隊列的定義TAILQ
隊列有HEAD
和ENTRY
兩種基本的數據結構node
#define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev;/* addr of previous next element*/ \ }
注意:數據結構中的filed
都是type
類型的指針(或者是二級指針),這裏的type
是用戶的隊列元素類型,,將ENTRY
結構內嵌到用戶的QUEUE_ITEM
結構中:linux
struct QUEUE_ITEM{ int value; TAILQ_ENTRY(QUEUE_ITEM) entries; }; TAILQ_HEAD(headname,QUEUE_ITEM) queue_head;
這和Linux
中list
的組織方式不同,後者是單純地將struct list_head
做爲鏈表的一個掛接點,並無用戶的信息,具體差異能夠看下圖:git
TAILQ
隊列的操做TAILQ
提供了多種操做隊列的API
,好比:github
TAILQ_HEAD(name, type) TAILQ_ENTRY(type) TAILQ_EMPTY(head) TAILQ_FIRST(head) TAILQ_FOREACH(var, head, field) TAILQ_INIT(head) TAILQ_INSERT_AFTER(head, listelm, elm, field) TAILQ_INSERT_BEFORE(listelm, elm, field) TAILQ_INSERT_TAIL(head, elm, field) .....
這些接口的實現和更多的操做接口能夠參考 FreeBSD queue數據結構
TAILQ
隊列中爲何tqh_prev
和tqh_last
要使用二級指針要搞清楚這個問題,咱們能夠考慮若是不使用二級指針會怎麼樣? 就像定義成下面這樣。性能
#define FAKE_TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type *tqh_last; /* last element */ \ } #define FAKE_TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type *tqe_prev; /* previous element*/ \ }
對比一下TAILQ_HEAD
和FAKE_TAILQ_HEAD
(注意其中的紅線和綠線的區別)測試
若是咱們想要刪除隊列的任意一個元素,對FAKE_TAILQ
,咱們須要特殊處理該元素是第一個元素的狀況(第一個元素的tqe_prev
指針爲空),而TAILQ
就沒有這個煩惱!spa
TAILQ
隊列的遍歷性能Linux
中的list
只將struct list_head
做爲用戶元素的掛接點,所以在正向遍歷鏈表時,須要使用container_of
這類接口才能獲取用戶的數據,而TAILQ
因爲tqe_next
指針直接指向用戶元素的類型,因此理論上,正向遍歷TAILQ
比list
更快.但逆向遍歷時,因爲TAILQ
的取用prev
元素的操做比next
麻煩的多,所以逆向遍歷是比正向慢的:指針
#define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
如下是用附件中的代碼測試的結果:code
遍歷TAILQ
:
TAILQ traversal time is 31955 us TAILQ reverse traversal time is 38699 us
遍歷list
list traversal time is 33062 us list list traversal time is 35864 us
bsd.c
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev;/* addr of previous next element*/ \ } #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_INIT(head) do { \ TAILQ_FIRST((head)) = NULL; \ (head)->tqh_last = &TAILQ_FIRST((head)); \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ TAILQ_NEXT((elm), field) = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &TAILQ_NEXT((elm), field); \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ TAILQ_NEXT((elm), field) = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ } while (0) #define TAILQ_FOREACH(var, head, field) \ for ((var) = TAILQ_FIRST((head)); \ (var); \ (var) = TAILQ_NEXT((var), field)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = TAILQ_LAST((head), headname); \ (var); \ (var) = TAILQ_PREV((var), headname, field)) struct QUEUE_ITEM{ int value; TAILQ_ENTRY(QUEUE_ITEM) entries; }; TAILQ_HEAD(headname,QUEUE_ITEM) queue_head; #define ITEM_NUM 5000000 #define TRAVERSAL 20 int main(int argc,char **argv){ struct QUEUE_ITEM *item; long long totaltime = 0; struct timeval start,end; long long metric[TRAVERSAL]; int i = 0; TAILQ_INIT(&queue_head); for(i=1;i<ITEM_NUM;i+=1){ item=malloc(sizeof(struct QUEUE_ITEM)); item->value=i; TAILQ_INSERT_TAIL(&queue_head, item, entries); } for (i = 0; i < TRAVERSAL; i++) { gettimeofday(&start,NULL); TAILQ_FOREACH(item, &queue_head, entries) { item->value++; } gettimeofday(&end,NULL); metric[i] = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); // get the run time by microsecond } totaltime = 0; for (i=0;i<TRAVERSAL;i++) { totaltime += metric[i]; } printf("TAILQ traversal time is %lld us\n", totaltime/TRAVERSAL); for (i = 0; i < TRAVERSAL; i++) { gettimeofday(&start,NULL); TAILQ_FOREACH_REVERSE(item, &queue_head, headname,entries) { item->value++; } gettimeofday(&end,NULL); metric[i] = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); // get the run time by microsecond } totaltime = 0; for (i=0;i<TRAVERSAL;i++) { totaltime += metric[i]; } printf("TAILQ reverse traversal time is %lld us\n", totaltime/TRAVERSAL); return 0; }
list.c
#include <stdio.h> #include <stdlib.h> #include <stddef.h> /* for offsetof */ #include <sys/time.h> #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) #define list_last_entry(ptr, type, member) \ list_entry((ptr)->prev, type, member) #define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member) #define list_prev_entry(pos, member) \ list_entry((pos)->member.prev, typeof(*(pos)), member) #define list_for_each_entry(pos, head, member) \ for (pos = list_first_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_next_entry(pos, member)) #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_last_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_prev_entry(pos, member)) #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) struct list_head { struct list_head *next, *prev; }; static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } struct QUEUE_ITEM{ int value; struct list_head node; }; LIST_HEAD(queue_head); #define ITEM_NUM 5000000 #define TRAVERSAL 20 int main() { int i = 0; struct QUEUE_ITEM *item; long long totaltime = 0; struct timeval start,end; long long metric[TRAVERSAL]; for(i=1;i<ITEM_NUM;i+=1){ item=malloc(sizeof(struct QUEUE_ITEM)); item->value = i; INIT_LIST_HEAD(&item->node); list_add(&item->node, &queue_head); } for (i = 0; i < TRAVERSAL; i++) { gettimeofday(&start,NULL); list_for_each_entry_reverse(item, &queue_head, node) { item->value++; } gettimeofday(&end,NULL); metric[i] = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); // get the run time by microsecond } totaltime = 0; for (i=0;i<TRAVERSAL;i++) { totaltime += metric[i]; } printf("list traversal time is %lld us\n", totaltime/TRAVERSAL); for (i = 0; i < TRAVERSAL; i++) { gettimeofday(&start,NULL); list_for_each_entry(item, &queue_head, node) { item->value++; } gettimeofday(&end,NULL); metric[i] = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); // get the run time by microsecond } totaltime = 0; for (i=0;i<TRAVERSAL;i++) { totaltime += metric[i]; } printf("list list traversal time is %lld us\n", totaltime/TRAVERSAL); return 0; }