[asterisk-commits] russell: branch russell/heap r175770 - in /team/russell/heap: include/asteris...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Feb 14 10:27:09 CST 2009


Author: russell
Date: Sat Feb 14 10:27:08 2009
New Revision: 175770

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=175770
Log:
Convert the scheduler.  Next up, performance analysis

Modified:
    team/russell/heap/include/asterisk/heap.h
    team/russell/heap/main/heap.c
    team/russell/heap/main/sched.c
    team/russell/heap/tests/test_heap.c

Modified: team/russell/heap/include/asterisk/heap.h
URL: http://svn.digium.com/svn-view/asterisk/team/russell/heap/include/asterisk/heap.h?view=diff&rev=175770&r1=175769&r2=175770
==============================================================================
--- team/russell/heap/include/asterisk/heap.h (original)
+++ team/russell/heap/include/asterisk/heap.h Sat Feb 14 10:27:08 2009
@@ -26,19 +26,20 @@
 
 struct ast_heap;
 
-typedef int (*ast_heap_cmp_fn)(const void *elm1, const void *elm2);
+typedef int (*ast_heap_cmp_fn)(void *elm1, void *elm2);
 
-struct ast_heap *ast_heap_create(unsigned int init_depth, ast_heap_cmp_fn cmp_fn);
+struct ast_heap *ast_heap_create(unsigned int init_depth, ast_heap_cmp_fn cmp_fn,
+		size_t index_offset);
 
 struct ast_heap *ast_heap_destroy(struct ast_heap *h);
 
-int ast_heap_push(struct ast_heap *h, const void *elm);
+int ast_heap_push(struct ast_heap *h, void *elm);
 
-const void *ast_heap_pop(struct ast_heap *h);
+void *ast_heap_pop(struct ast_heap *h);
 
-const void *ast_heap_peek(struct ast_heap *h);
+void *ast_heap_remove(struct ast_heap *h, void *elm);
 
-void ast_heap_print(struct ast_heap *h, int fd);
+void *ast_heap_peek(struct ast_heap *h);
 
 int ast_heap_verify(struct ast_heap *h);
 

Modified: team/russell/heap/main/heap.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/heap/main/heap.c?view=diff&rev=175770&r1=175769&r2=175770
==============================================================================
--- team/russell/heap/main/heap.c (original)
+++ team/russell/heap/main/heap.c Sat Feb 14 10:27:08 2009
@@ -33,9 +33,10 @@
 
 struct ast_heap {
 	ast_heap_cmp_fn cmp_fn;
+	size_t index_offset;
 	size_t cur_len;
 	size_t avail_len;
-	const void **heap;
+	void **heap;
 };
 
 static inline int left_node(int i)
@@ -53,25 +54,23 @@
 	return i / 2;
 }
 
-static inline const void *heap_get(struct ast_heap *h, int i)
+static inline void *heap_get(struct ast_heap *h, int i)
 {
 	return h->heap[i - 1];
 }
 
-static inline void heap_set(struct ast_heap *h, int i, const void *elm)
-{
+static inline size_t get_index(struct ast_heap *h, void *elm)
+{
+	size_t *index = elm + h->index_offset;
+	return *index;
+}
+
+static inline void heap_set(struct ast_heap *h, int i, void *elm)
+{
+	size_t *index = elm + h->index_offset;
+
 	h->heap[i - 1] = elm;
-}
-
-void ast_heap_print(struct ast_heap *h, int fd)
-{
-	unsigned int i;
-
-	ast_cli(fd, "Heap Contents:\n");
-	for (i = 0; i < h->cur_len; i++) {
-		ast_cli(fd, "Index: %u - %p\n", i, h->heap[i]);
-	}
-	ast_cli(fd, "---\n");
+	*index = i;
 }
 
 int ast_heap_verify(struct ast_heap *h)
@@ -98,7 +97,8 @@
 	return 0;
 }
 
-struct ast_heap *ast_heap_create(unsigned int init_depth, ast_heap_cmp_fn cmp_fn)
+struct ast_heap *ast_heap_create(unsigned int init_depth, ast_heap_cmp_fn cmp_fn,
+		size_t index_offset)
 {
 	struct ast_heap *h;
 
@@ -116,6 +116,7 @@
 	}
 
 	h->cmp_fn = cmp_fn;
+	h->index_offset = index_offset;
 	h->avail_len = (1 << init_depth) - 1;
 
 	if (!(h->heap = ast_calloc(1, h->avail_len * sizeof(void *)))) {
@@ -151,7 +152,7 @@
 
 static inline void heap_swap(struct ast_heap *h, int i, int j)
 {
-	const void *tmp;
+	void *tmp;
 
 	tmp = heap_get(h, i);
 	heap_set(h, i, heap_get(h, j));	
@@ -185,7 +186,7 @@
 	}
 }
 
-int ast_heap_push(struct ast_heap *h, const void *elm)
+int ast_heap_push(struct ast_heap *h, void *elm)
 {
 	int i;
 
@@ -204,28 +205,38 @@
 	return 0;
 }
 
-const void *ast_heap_pop(struct ast_heap *h)
-{
-	const void *ret;
-
+static void *_ast_heap_remove(struct ast_heap *h, unsigned int index)
+{
+	void *ret;
+
+	if (!index || index > h->cur_len) {
+		return NULL;
+	}
+
+	ret = heap_get(h, index);
+	heap_set(h, index, heap_get(h, h->cur_len--));
+
+	max_heapify(h, index);
+
+	return ret;
+}
+
+void *ast_heap_remove(struct ast_heap *h, void *elm)
+{
+	return _ast_heap_remove(h, get_index(h, elm));
+}
+
+void *ast_heap_pop(struct ast_heap *h)
+{
+	return _ast_heap_remove(h, 1);
+}
+
+void *ast_heap_peek(struct ast_heap *h)
+{
 	if (!h->cur_len) {
 		return NULL;
 	}
 
-	ret = heap_get(h, 1);
-	heap_set(h, 1, heap_get(h, h->cur_len--));
-
-	max_heapify(h, 1);
-
-	return ret;
-}
-
-const void *ast_heap_peek(struct ast_heap *h)
-{
-	if (!h->cur_len) {
-		return NULL;
-	}
-
 	return heap_get(h, 1);
 }
 

Modified: team/russell/heap/main/sched.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/heap/main/sched.c?view=diff&rev=175770&r1=175769&r2=175770
==============================================================================
--- team/russell/heap/main/sched.c (original)
+++ team/russell/heap/main/sched.c Sat Feb 14 10:27:08 2009
@@ -45,15 +45,17 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/dlinkedlists.h"
 #include "asterisk/hashtab.h"
+#include "asterisk/heap.h"
 
 struct sched {
-	AST_DLLIST_ENTRY(sched) list;
+	AST_LIST_ENTRY(sched) list;
 	int id;                       /*!< ID number of event */
 	struct timeval when;          /*!< Absolute time event should take place */
 	int resched;                  /*!< When to reschedule */
 	int variable;                 /*!< Use return value from callback to reschedule */
 	const void *data;             /*!< Data */
 	ast_sched_cb callback;        /*!< Callback */
+	size_t __heap_index;
 };
 
 struct sched_context {
@@ -61,8 +63,8 @@
 	unsigned int eventcnt;                  /*!< Number of events processed */
 	unsigned int schedcnt;                  /*!< Number of outstanding schedule events */
 	unsigned int highwater;					/*!< highest count so far */
-	AST_DLLIST_HEAD_NOLOCK(, sched) schedq;   /*!< Schedule entry and main queue */
 	struct ast_hashtab *schedq_ht;             /*!< hash table for fast searching */
+	struct ast_heap *sched_heap;
 
 #ifdef SCHED_MAX_CACHE
 	AST_LIST_HEAD_NOLOCK(, sched) schedc;   /*!< Cache of unused schedule structures and how many */
@@ -229,6 +231,14 @@
 	return h;
 }
 
+static int sched_time_cmp(void *a, void *b)
+{
+	struct sched *as = a;
+	struct sched *bs = b;
+	
+	return ast_tvcmp(bs->when, as->when);
+}
+
 struct sched_context *sched_context_create(void)
 {
 	struct sched_context *tmp;
@@ -240,7 +250,13 @@
 	tmp->eventcnt = 1;
 	
 	tmp->schedq_ht = ast_hashtab_create(23, sched_cmp, ast_hashtab_resize_java, ast_hashtab_newsize_java, sched_hash, 1);
-	
+
+	if (!(tmp->sched_heap = ast_heap_create(8, sched_time_cmp,
+			offsetof(struct sched, __heap_index)))) {
+		sched_context_destroy(tmp);
+		return NULL;
+	}
+
 	return tmp;
 }
 
@@ -256,9 +272,13 @@
 		ast_free(s);
 #endif
 
-	/* And the queue */
-	while ((s = AST_DLLIST_REMOVE_HEAD(&con->schedq, list)))
-		ast_free(s);
+	if (con->sched_heap) {
+		while ((s = ast_heap_pop(con->sched_heap))) {
+			ast_free(s);
+		}
+		ast_heap_destroy(con->sched_heap);
+		con->sched_heap = NULL;
+	}
 
 	ast_hashtab_destroy(con->schedq_ht, NULL);
 	con->schedq_ht = NULL;
@@ -310,16 +330,18 @@
 int ast_sched_wait(struct sched_context *con)
 {
 	int ms;
+	struct sched *s;
 
 	DEBUG(ast_debug(1, "ast_sched_wait()\n"));
 
 	ast_mutex_lock(&con->lock);
-	if (AST_DLLIST_EMPTY(&con->schedq)) {
+	if ((s = ast_heap_peek(con->sched_heap))) {
+		ms = ast_tvdiff_ms(s->when, ast_tvnow());
+		if (ms < 0) {
+			ms = 0;
+		}
+	} else {
 		ms = -1;
-	} else {
-		ms = ast_tvdiff_ms(AST_DLLIST_FIRST(&con->schedq)->when, ast_tvnow());
-		if (ms < 0)
-			ms = 0;
 	}
 	ast_mutex_unlock(&con->lock);
 
@@ -334,53 +356,17 @@
  */
 static void schedule(struct sched_context *con, struct sched *s)
 {
-	struct sched *cur = NULL;
-	int ret;
-	int df = 0;
-	int de = 0;
-	struct sched *first = AST_DLLIST_FIRST(&con->schedq);
-	struct sched *last = AST_DLLIST_LAST(&con->schedq);
-
-	if (first)
-		df = ast_tvdiff_us(s->when, first->when);
-	if (last)
-		de = ast_tvdiff_us(s->when, last->when);
-
-	if (df < 0)
-		df = -df;
-	if (de < 0)
-		de = -de;
-
-	if (df < de) {
-		AST_DLLIST_TRAVERSE(&con->schedq, cur, list) {
-			if (ast_tvcmp(s->when, cur->when) == -1) {
-				AST_DLLIST_INSERT_BEFORE(&con->schedq, cur, s, list);
-				break;
-			}
-		}
-		if (!cur) {
-			AST_DLLIST_INSERT_TAIL(&con->schedq, s, list);
-		}
-	} else {
-		AST_DLLIST_TRAVERSE_BACKWARDS(&con->schedq, cur, list) {
-			if (ast_tvcmp(s->when, cur->when) == 1) {
-				AST_DLLIST_INSERT_AFTER(&con->schedq, cur, s, list);
-				break;
-			}
-		}
-		if (!cur) {
-			AST_DLLIST_INSERT_HEAD(&con->schedq, s, list);
-		}
-	}
-
-	ret = ast_hashtab_insert_safe(con->schedq_ht, s);
-	if (!ret)
-		ast_log(LOG_WARNING,"Schedule Queue entry %d is already in table!\n",s->id);
+	ast_heap_push(con->sched_heap, s);
+
+	if (!ast_hashtab_insert_safe(con->schedq_ht, s)) {
+		ast_log(LOG_WARNING,"Schedule Queue entry %d is already in table!\n", s->id);
+	}
 
 	con->schedcnt++;
 
-	if (con->schedcnt > con->highwater)
+	if (con->schedcnt > con->highwater) {
 		con->highwater = con->schedcnt;
+	}
 }
 
 /*! \brief
@@ -480,31 +466,25 @@
 int _ast_sched_del(struct sched_context *con, int id, const char *file, int line, const char *function)
 #endif
 {
-	struct sched *s, tmp;
+	struct sched *s, tmp = {
+		.id = id,	
+	};
 
 	DEBUG(ast_debug(1, "ast_sched_del(%d)\n", id));
 	
 	ast_mutex_lock(&con->lock);
-
-	/* OK, this is the heart of the sched performance upgrade.
-	   If we have 4700 peers, we can have 4700+ entries in the
-	   schedq list. searching this would take time. So, I add a 
-	   hashtab to the context to keep track of each entry, by id.
-	   I also leave the linked list alone, almost, --  I implement
-       a doubly-linked list instead, because it would do little good
-	   to look up the id in a hashtab, and then have to run thru 
-	   a couple thousand entries to remove it from the schedq list! */
-	tmp.id = id;
 	s = ast_hashtab_lookup(con->schedq_ht, &tmp);
 	if (s) {
-		struct sched *x = AST_DLLIST_REMOVE(&con->schedq, s, list);
-		
-		if (!x)
-			ast_log(LOG_WARNING,"sched entry %d not in the schedq list?\n", s->id);
-
-		if (!ast_hashtab_remove_this_object(con->schedq_ht, s))
+		if (!ast_heap_remove(con->sched_heap, s)) {
+			ast_log(LOG_WARNING,"sched entry %d not in the sched heap?\n", s->id);
+		}
+
+		if (!ast_hashtab_remove_this_object(con->schedq_ht, s)) {
 			ast_log(LOG_WARNING,"Found sched entry %d, then couldn't remove it?\n", s->id);
+		}
+
 		con->schedcnt--;
+
 		sched_release(con, s);
 	}
 	
@@ -527,19 +507,17 @@
 	
 	return 0;
 }
-
-
 char *ast_sched_report(struct sched_context *con, char *buf, int bufsiz, struct ast_cb_names *cbnames)
 {
 	int *countlist,i;
-	struct sched *cur;
+	//struct sched *cur;
 	char buf2[1200];
 	ast_sched_cb xxx = NULL;
 	
 	buf[0] = 0;
 	sprintf(buf, " Highwater = %d\n schedcnt = %d\n", con->highwater, con->schedcnt);
 	countlist = ast_calloc(sizeof(int),cbnames->numassocs+1);
-	
+#if 0	
 	AST_DLLIST_TRAVERSE(&con->schedq, cur, list) {
 		/* match the callback to the cblist */
 		for (i=0;i<cbnames->numassocs;i++) {
@@ -553,6 +531,7 @@
 			countlist[cbnames->numassocs]++;
 		}
 	}
+#endif
 	for (i=0;i<cbnames->numassocs;i++) {
 		sprintf(buf2,"    %s : %d\n", cbnames->list[i], countlist[i]);
 		strcat(buf, buf2);
@@ -562,13 +541,11 @@
 	return buf;
 }
 
-
-	
 /*! \brief Dump the contents of the scheduler to LOG_DEBUG */
 void ast_sched_dump(const struct sched_context *con)
 {
-	struct sched *q;
-	struct timeval when = ast_tvnow();
+	//struct sched *q;
+	//struct timeval when = ast_tvnow();
 #ifdef SCHED_MAX_CACHE
 	ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache, %d high-water)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt, con->highwater);
 #else
@@ -578,6 +555,7 @@
 	ast_debug(1, "=============================================================\n");
 	ast_debug(1, "|ID    Callback          Data              Time  (sec:ms)   |\n");
 	ast_debug(1, "+-----+-----------------+-----------------+-----------------+\n");
+#if 0
 	AST_DLLIST_TRAVERSE(&con->schedq, q, list) {
 		struct timeval delta = ast_tvsub(q->when, when);
 
@@ -588,6 +566,7 @@
 			(long)delta.tv_sec,
 			(long int)delta.tv_usec);
 	}
+#endif
 	ast_debug(1, "=============================================================\n");
 	
 }
@@ -606,19 +585,22 @@
 		
 	ast_mutex_lock(&con->lock);
 
-	for (numevents = 0; !AST_DLLIST_EMPTY(&con->schedq); numevents++) {
+	for (numevents = 0; (current = ast_heap_peek(con->sched_heap)); numevents++) {
 		/* schedule all events which are going to expire within 1ms.
 		 * We only care about millisecond accuracy anyway, so this will
 		 * help us get more than one event at one time if they are very
 		 * close together.
 		 */
 		when = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
-		if (ast_tvcmp(AST_DLLIST_FIRST(&con->schedq)->when, when) != -1)
+		if (ast_tvcmp(current->when, when) != -1) {
 			break;
+		}
 		
-		current = AST_DLLIST_REMOVE_HEAD(&con->schedq, list);
-		if (!ast_hashtab_remove_this_object(con->schedq_ht, current))
+		current = ast_heap_pop(con->sched_heap);
+
+		if (!ast_hashtab_remove_this_object(con->schedq_ht, current)) {
 			ast_log(LOG_ERROR,"Sched entry %d was in the schedq list but not in the hashtab???\n", current->id);
+		}
 
 		con->schedcnt--;
 
@@ -642,8 +624,9 @@
 			 */
 			if (sched_settime(&current->when, current->variable? res : current->resched)) {
 				sched_release(con, current);
-			} else
+			} else {
 				schedule(con, current);
+			}
 		} else {
 			/* No longer needed, so release it */
 		 	sched_release(con, current);

Modified: team/russell/heap/tests/test_heap.c
URL: http://svn.digium.com/svn-view/asterisk/team/russell/heap/tests/test_heap.c?view=diff&rev=175770&r1=175769&r2=175770
==============================================================================
--- team/russell/heap/tests/test_heap.c (original)
+++ team/russell/heap/tests/test_heap.c Sat Feb 14 10:27:08 2009
@@ -32,14 +32,19 @@
 #include "asterisk/utils.h"
 #include "asterisk/heap.h"
 
-static int node_cmp(const void *_n1, const void *_n2)
-{
-	long n1 = (long) _n1;
-	long n2 = (long) _n2;
-
-	if (n1 < n2) {
+struct node {
+	long val;
+	size_t index;
+};
+
+static int node_cmp(void *_n1, void *_n2)
+{
+	struct node *n1 = _n1;
+	struct node *n2 = _n2;
+
+	if (n1->val < n2->val) {
 		return -1;
-	} else if (n1 == n2) {
+	} else if (n1->val == n2->val) {
 		return 0;
 	} else {
 		return 1;
@@ -49,9 +54,14 @@
 static int test1(int fd)
 {
 	struct ast_heap *h;
-	const void *obj;
-
-	if (!(h = ast_heap_create(8, node_cmp))) {
+	struct node *obj;
+	struct node nodes[3] = {
+		{ 1, },
+		{ 2, },
+		{ 3, },
+	};
+
+	if (!(h = ast_heap_create(8, node_cmp, offsetof(struct node, index)))) {
 		return -1;
 	}
 
@@ -60,35 +70,24 @@
 	ast_cli(fd, "Test #1 - Push a few elements onto a heap and make sure that they "
 			"come back off in the right order.\n");
 
-	ast_heap_print(h, fd);
-
-	ast_heap_push(h, (const void *) (long) 1);
-	ast_heap_print(h, fd);
-
-	ast_heap_push(h, (const void *) (long) 2);
-	ast_heap_print(h, fd);
-
-	ast_heap_push(h, (const void *) (long) 3);
-	ast_heap_print(h, fd);
-
-	obj = ast_heap_pop(h);
-	ast_cli(fd, "Popped: %p\n", obj);
-	ast_heap_print(h, fd);
-	if (obj != (const void *) (long) 3) {
+	ast_heap_push(h, &nodes[0]);
+
+	ast_heap_push(h, &nodes[1]);
+
+	ast_heap_push(h, &nodes[2]);
+
+	obj = ast_heap_pop(h);
+	if (obj->val != 3) {
 		return -2;
 	}
 
 	obj = ast_heap_pop(h);
-	ast_cli(fd, "Popped: %p\n", obj);
-	ast_heap_print(h, fd);
-	if (obj != (const void *) (long) 2) {
+	if (obj->val != 2) {
 		return -3;
 	}
 	
 	obj = ast_heap_pop(h);
-	ast_cli(fd, "Popped: %p\n", obj);
-	ast_heap_print(h, fd);
-	if (obj != (const void *) (long) 1) {
+	if (obj->val != 1) {
 		return -4;
 	}
 
@@ -106,51 +105,67 @@
 
 static int test2(int fd)
 {
-	struct ast_heap *h;
+	struct ast_heap *h = NULL;
 	static const unsigned int one_million = 1000000;
+	struct node *nodes = NULL;
+	struct node *node;
 	unsigned int i = one_million;
 	long last = LONG_MAX, cur;
+	int res = 0;
 
 	ast_cli(fd, "Test #2 - Push a million random elements on to a heap, "
 			"verify that the heap has been properly constructed, "
 			"and then ensure that the elements are come back off in the proper order\n");
 
-	if (!(h = ast_heap_create(20, node_cmp))) {
-		return -1;
+	if (!(nodes = ast_malloc(one_million * sizeof(*node)))) {
+		res = -1;
+		goto return_cleanup;
+	}
+
+	if (!(h = ast_heap_create(20, node_cmp, offsetof(struct node, index)))) {
+		res = -2;
+		goto return_cleanup;
 	}
 
 	while (i--) {
-		long val;
-		while (!(val = ast_random()) || val == LONG_MAX) {
-			/* Stuff. */
-		}
-		ast_heap_push(h, (const void *) val);
+		nodes[i].val = ast_random();
+		ast_heap_push(h, &nodes[i]);
 	}
 
 	if (ast_heap_verify(h)) {
-		return -2;
+		res = -3;
+		goto return_cleanup;
 	}
 
 	i = 0;
-	while ((cur = (long) ast_heap_pop(h))) {
+	while ((node = ast_heap_pop(h))) {
+		cur = node->val;
 		if (cur > last) {
 			ast_cli(fd, "i: %u, cur: %ld, last: %ld\n", i, cur, last);
-			return -3;
+			res = -4;
+			goto return_cleanup;
 		}
 		last = cur;
 		i++;
 	}
 
-	h = ast_heap_destroy(h);
-
 	if (i != one_million) {
 		ast_cli(fd, "Stopped popping off after only getting %u nodes\n", i);
-		return -4;
+		res = -5;
+		goto return_cleanup;
 	}
 
 	ast_cli(fd, "Test #2 successful.\n");
 
-	return 0;
+return_cleanup:
+	if (h) {
+		h = ast_heap_destroy(h);
+	}
+	if (nodes) {
+		ast_free(nodes);
+	}
+
+	return res;
 }
 
 static char *handle_cli_heap_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)




More information about the asterisk-commits mailing list