From d59aa279c37b7aba07385a3a2cb3057bce6c5fea Mon Sep 17 00:00:00 2001 From: Grissiom Date: Thu, 26 Sep 2013 15:52:02 +0800 Subject: [PATCH] kernel: use skip list to implement timer list Skip list is a "random" data structure that in high possibilities it would get O(log(N)) time complexity in inserting while the old list get O(N). Forthermore, when set RT_TIMER_SKIP_LIST_LEVEL to 1, it will just the same as the old double linked list, both in time and space complexity. Benchmarks shows that when RT_TIMER_SKIP_LIST_LEVEL is 3, the average time of random insertion of new timer is about 2 times faster than the old timer when there are 100 timers and 3 times faster when there are 200 timers. However, it restores the deprecated funcion rt_system_timer_init. BSPs must invoke it upon system startup. --- include/rtdef.h | 11 ++- src/thread.c | 9 +-- src/timer.c | 176 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 141 insertions(+), 55 deletions(-) diff --git a/include/rtdef.h b/include/rtdef.h index 09574d2233..be8843d85c 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -410,6 +410,15 @@ struct rt_object_information #define RT_TIMER_CTRL_SET_ONESHOT 0x2 /**< change timer to one shot */ #define RT_TIMER_CTRL_SET_PERIODIC 0x3 /**< change timer to periodic */ +#ifndef RT_TIMER_SKIP_LIST_LEVEL +#define RT_TIMER_SKIP_LIST_LEVEL 1 +#endif + +/* 1 or 3 */ +#ifndef RT_TIMER_SKIP_LIST_MASK +#define RT_TIMER_SKIP_LIST_MASK 0x3 +#endif + /** * timer structure */ @@ -417,7 +426,7 @@ struct rt_timer { struct rt_object parent; /**< inherit from rt_object */ - rt_list_t list; /**< the node of timer list */ + rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; void (*timeout_func)(void *parameter); /**< timeout function */ void *parameter; /**< timeout function's parameter */ diff --git a/src/thread.c b/src/thread.c index da77ace289..c2282f6ee6 100644 --- a/src/thread.c +++ b/src/thread.c @@ -61,8 +61,7 @@ static void rt_thread_exit(void) thread->stat = RT_THREAD_CLOSE; /* remove it from timer list */ - rt_list_remove(&(thread->thread_timer.list)); - rt_object_detach((rt_object_t)&(thread->thread_timer)); + rt_timer_detach(&thread->thread_timer); if ((rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) && thread->cleanup == RT_NULL) @@ -613,11 +612,7 @@ rt_err_t rt_thread_resume(rt_thread_t thread) /* remove from suspend list */ rt_list_remove(&(thread->tlist)); - /* remove thread timer */ - rt_list_remove(&(thread->thread_timer.list)); - - /* change timer state */ - thread->thread_timer.parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + rt_timer_stop(&thread->thread_timer); /* enable interrupt */ rt_hw_interrupt_enable(temp); diff --git a/src/timer.c b/src/timer.c index 30ed9b1202..3d14792e66 100644 --- a/src/timer.c +++ b/src/timer.c @@ -34,7 +34,7 @@ #include /* hard timer list */ -static rt_list_t rt_timer_list = RT_LIST_OBJECT_INIT(rt_timer_list); +static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; #ifdef RT_USING_TIMER_SOFT #ifndef RT_TIMER_THREAD_STACK_SIZE @@ -46,7 +46,7 @@ static rt_list_t rt_timer_list = RT_LIST_OBJECT_INIT(rt_timer_list); #endif /* soft timer list */ -static rt_list_t rt_soft_timer_list; +static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; static struct rt_thread timer_thread; ALIGN(RT_ALIGN_SIZE) static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE]; @@ -83,6 +83,8 @@ static void _rt_timer_init(rt_timer_t timer, rt_tick_t time, rt_uint8_t flag) { + int i; + /* set flag */ timer->parent.flag = flag; @@ -96,21 +98,64 @@ static void _rt_timer_init(rt_timer_t timer, timer->init_tick = time; /* initialize timer list */ - rt_list_init(&(timer->list)); + for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) + { + rt_list_init(&(timer->row[i])); + } } -static rt_tick_t rt_timer_list_next_timeout(rt_list_t *timer_list) +/* the fist timer always in the last row */ +static rt_tick_t rt_timer_list_next_timeout(rt_list_t timer_list[]) { struct rt_timer *timer; - if (rt_list_isempty(timer_list)) + if (rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) return RT_TICK_MAX; - timer = rt_list_entry(timer_list->next, struct rt_timer, list); + timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); return timer->timeout_tick; } +rt_inline void _rt_timer_remove(rt_timer_t timer) +{ + int i; + + for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) + { + rt_list_remove(&timer->row[i]); + } +} + +static int rt_timer_count_height(struct rt_timer *timer) +{ + int i, cnt = 0; + + for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++) + { + if (!rt_list_isempty(&timer->row[i])) + cnt++; + } + return cnt; +} + +void rt_timer_dump(rt_list_t timer_heads[]) +{ + rt_list_t *list; + + for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL-1].next; + list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL-1]; + list = list->next) + { + struct rt_timer *timer = rt_list_entry(list, + struct rt_timer, + row[RT_TIMER_SKIP_LIST_LEVEL-1]); + rt_kprintf("%d", rt_timer_count_height(timer)); + } + rt_kprintf("\n"); +} + /** * @addtogroup Clock */ @@ -162,8 +207,7 @@ rt_err_t rt_timer_detach(rt_timer_t timer) /* disable interrupt */ level = rt_hw_interrupt_disable(); - /* remove it from timer list */ - rt_list_remove(&(timer->list)); + _rt_timer_remove(timer); /* enable interrupt */ rt_hw_interrupt_enable(level); @@ -224,8 +268,7 @@ rt_err_t rt_timer_delete(rt_timer_t timer) /* disable interrupt */ level = rt_hw_interrupt_disable(); - /* remove it from timer list */ - rt_list_remove(&(timer->list)); + _rt_timer_remove(timer); /* enable interrupt */ rt_hw_interrupt_enable(level); @@ -246,9 +289,12 @@ RTM_EXPORT(rt_timer_delete); */ rt_err_t rt_timer_start(rt_timer_t timer) { - struct rt_timer *t; + int row_lvl; + rt_list_t *timer_list; register rt_base_t level; - rt_list_t *n, *timer_list; + rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]; + unsigned int tst_nr; + static unsigned int random_nr; /* timer check */ RT_ASSERT(timer != RT_NULL); @@ -271,39 +317,64 @@ rt_err_t rt_timer_start(rt_timer_t timer) if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { /* insert timer to soft timer list */ - timer_list = &rt_soft_timer_list; + timer_list = rt_soft_timer_list; } else #endif { /* insert timer to system timer list */ - timer_list = &rt_timer_list; + timer_list = rt_timer_list; } - for (n = timer_list->next; n != timer_list; n = n->next) + row_head[0] = &timer_list[0]; + for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) { - t = rt_list_entry(n, struct rt_timer, list); + for (;row_head[row_lvl] != timer_list[row_lvl].prev; + row_head[row_lvl] = row_head[row_lvl]->next) + { + struct rt_timer *t; + rt_list_t *p = row_head[row_lvl]->next; - /* - * It supposes that the new tick shall less than the half duration of - * tick max. And if we have two timers that timeout at the same time, - * it's prefered that the timer inserted early get called early. - */ - if ((t->timeout_tick - timer->timeout_tick) == 0) - { - rt_list_insert_after(n, &(timer->list)); - break; - } - else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2) - { - rt_list_insert_before(n, &(timer->list)); - break; + /* fix up the entry pointer */ + t = rt_list_entry(p, struct rt_timer, row[row_lvl]); + + /* If we have two timers that timeout at the same time, it's + * preferred that the timer inserted early get called early. + * So insert the new timer to the end the the some-timeout timer + * list. + */ + if ((t->timeout_tick - timer->timeout_tick) == 0) + { + continue; + } + else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2) + { + break; + } } + if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1) + row_head[row_lvl+1] = row_head[row_lvl]+1; } - /* no found suitable position in timer list */ - if (n == timer_list) + + /* Interestingly, this super simple timer insert counter works very very + * well on distributing the list height uniformly. By means of "very very + * well", I mean it beats the randomness of timer->timeout_tick very easily + * (actually, the timeout_tick is not random and easy to be attacked). */ + random_nr++; + tst_nr = random_nr; + + rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL-1], + &(timer->row[RT_TIMER_SKIP_LIST_LEVEL-1])); + for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) { - rt_list_insert_before(n, &(timer->list)); + if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK)) + rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl], + &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl])); + else + break; + /* Shift over the bits we have tested. Works well with 1 bit and 2 + * bits. */ + tst_nr >>= (RT_TIMER_SKIP_LIST_MASK+1)>>1; } timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; @@ -349,8 +420,7 @@ rt_err_t rt_timer_stop(rt_timer_t timer) /* disable interrupt */ level = rt_hw_interrupt_disable(); - /* remove it from timer list */ - rt_list_remove(&(timer->list)); + _rt_timer_remove(timer); /* enable interrupt */ rt_hw_interrupt_enable(level); @@ -418,9 +488,10 @@ void rt_timer_check(void) /* disable interrupt */ level = rt_hw_interrupt_disable(); - while (!rt_list_isempty(&rt_timer_list)) + while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL-1])) { - t = rt_list_entry(rt_timer_list.next, struct rt_timer, list); + t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); /* * It supposes that the new tick shall less than the half duration of @@ -431,7 +502,7 @@ void rt_timer_check(void) RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t)); /* remove timer from timer list firstly */ - rt_list_remove(&(t->list)); + _rt_timer_remove(t); /* call timeout function */ t->timeout_func(t->parameter); @@ -471,7 +542,7 @@ void rt_timer_check(void) */ rt_tick_t rt_timer_next_timeout_tick(void) { - return rt_timer_list_next_timeout(&rt_timer_list); + return rt_timer_list_next_timeout(rt_timer_list); } #ifdef RT_USING_TIMER_SOFT @@ -489,9 +560,10 @@ void rt_soft_timer_check(void) current_tick = rt_tick_get(); - for (n = rt_soft_timer_list.next; n != &(rt_soft_timer_list);) + for (n = rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL-1].next; + n != &(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL-1]);) { - t = rt_list_entry(n, struct rt_timer, list); + t = rt_list_entry(n, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL-1]); /* * It supposes that the new tick shall less than the half duration of @@ -505,7 +577,7 @@ void rt_soft_timer_check(void) n = n->next; /* remove timer from timer list firstly */ - rt_list_remove(&(t->list)); + _rt_timer_remove(timer); /* call timeout function */ t->timeout_func(t->parameter); @@ -542,7 +614,7 @@ static void rt_thread_timer_entry(void *parameter) while (1) { /* get the next timeout tick */ - next_timeout = rt_timer_list_next_timeout(&rt_soft_timer_list); + next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list); if (next_timeout == RT_TICK_MAX) { /* no software timer exist, suspend self. */ @@ -578,12 +650,15 @@ static void rt_thread_timer_entry(void *parameter) * @ingroup SystemInit * * This function will initialize system timer - * - * @deprecated since 1.1.0, this function does not need to be invoked - * in the system initialization. */ void rt_system_timer_init(void) { + int i; + + for (i = 0; i < sizeof(rt_timer_list)/sizeof(rt_timer_list[0]); i++) + { + rt_list_init(rt_timer_list+i); + } } /** @@ -594,7 +669,14 @@ void rt_system_timer_init(void) void rt_system_timer_thread_init(void) { #ifdef RT_USING_TIMER_SOFT - rt_list_init(&rt_soft_timer_list); + int i; + + for (i = 0; + i < sizeof(rt_soft_timer_list)/sizeof(rt_soft_timer_list[0]); + i++) + { + rt_list_init(rt_soft_timer_list+i); + } /* start software timer thread */ rt_thread_init(&timer_thread,