Skip to content

Commit 2a047ce

Browse files
committed
Implement linux-kernel like list_sort
We change the original merge sort into linux-kernel like list_sort[1]. The list_sort also break the circular linked list into a singly linked list, but the decision rule for merging two list is different in order to prevent cache thrashing. [1]:https://github.com/torvalds/linux/blob/master/lib/list_sort.c Change-Id: Id152c588b74cc411b1b5483d29d754ed4f010bfc
1 parent 4b67f9a commit 2a047ce

File tree

1 file changed

+109
-59
lines changed

1 file changed

+109
-59
lines changed

queue.c

Lines changed: 109 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -294,95 +294,145 @@ void q_reverseK(struct list_head *head, int k)
294294
}
295295

296296
/* Start of sort */
297-
typedef int (*compare_func_t)(struct list_head *, struct list_head *);
297+
typedef int (*list_cmp_func_t)(const struct list_head *,
298+
const struct list_head *);
298299

299-
/* Sort elements of queue in ascending/descending order */
300-
static int cmp_func(struct list_head *a, struct list_head *b)
300+
static int cmp(const struct list_head *a, const struct list_head *b)
301301
{
302302
const element_t *element_a = list_entry(a, element_t, list);
303303
const element_t *element_b = list_entry(b, element_t, list);
304304
return strcmp(element_a->value, element_b->value);
305305
}
306306

307-
static struct list_head *merge_list(struct list_head *first,
308-
struct list_head *second,
309-
compare_func_t cmp)
307+
typedef int (*compare_func_t)(struct list_head *, struct list_head *);
308+
309+
static struct list_head *merge(list_cmp_func_t cmp,
310+
struct list_head *a,
311+
struct list_head *b)
310312
{
311-
struct list_head dummy;
312-
struct list_head *tail = &dummy;
313-
dummy.next = NULL;
314-
315-
while (first && second) {
316-
if (cmp(first, second) <= 0) {
317-
tail->next = first;
318-
first->prev = tail;
319-
first = first->next;
313+
struct list_head *head = NULL, **tail = &head;
314+
315+
for (;;) {
316+
/* if equal, take 'a' -- important for sort stability */
317+
if (cmp(a, b) <= 0) {
318+
*tail = a;
319+
tail = &a->next;
320+
a = a->next;
321+
if (!a) {
322+
*tail = b;
323+
break;
324+
}
320325
} else {
321-
tail->next = second;
322-
second->prev = tail;
323-
second = second->next;
326+
*tail = b;
327+
tail = &b->next;
328+
b = b->next;
329+
if (!b) {
330+
*tail = a;
331+
break;
332+
}
324333
}
325-
tail = tail->next;
326334
}
327-
328-
tail->next = (first ? first : second);
329-
if (tail->next) {
330-
tail->next->prev = tail;
331-
}
332-
return dummy.next;
335+
return head;
333336
}
334337

335-
static struct list_head *merge_two_list(struct list_head *head,
336-
compare_func_t cmp)
338+
static void merge_final(list_cmp_func_t cmp,
339+
struct list_head *head,
340+
struct list_head *a,
341+
struct list_head *b)
337342
{
338-
if (!head || !head->next) {
339-
return head;
340-
}
341-
342-
struct list_head *slow = head;
343-
struct list_head *fast = head->next;
344-
345-
while (fast && fast->next) {
346-
slow = slow->next;
347-
fast = fast->next->next;
348-
}
349-
struct list_head *second = slow->next;
350-
slow->next = NULL;
351-
if (second) {
352-
second->prev = NULL;
343+
struct list_head *tail = head;
344+
unsigned char count = 0;
345+
346+
for (;;) {
347+
/* if equal, take 'a' -- important for sort stability */
348+
if (cmp(a, b) <= 0) {
349+
tail->next = a;
350+
a->prev = tail;
351+
tail = a;
352+
a = a->next;
353+
if (!a)
354+
break;
355+
} else {
356+
tail->next = b;
357+
b->prev = tail;
358+
tail = b;
359+
b = b->next;
360+
if (!b) {
361+
b = a;
362+
break;
363+
}
364+
}
353365
}
354366

355-
head = merge_two_list(head, cmp);
356-
second = merge_two_list(second, cmp);
367+
/* Finish linking remainder of list b on to tail */
368+
tail->next = b;
369+
do {
370+
if (__glibc_unlikely(!++count))
371+
cmp(b, b);
372+
b->prev = tail;
373+
tail = b;
374+
b = b->next;
375+
} while (b);
357376

358-
return merge_list(head, second, cmp);
377+
/* And the final links to make a circular doubly-linked list */
378+
tail->next = head;
379+
head->prev = tail;
359380
}
360381

361-
void merge_sort(struct list_head *head, compare_func_t cmp)
382+
void list_sort(struct list_head *head, list_cmp_func_t cmp)
362383
{
363-
if (!head || list_empty(head) || head->next->next == head) {
384+
struct list_head *list = head->next, *pending = NULL;
385+
size_t count = 0; /* Count of pending */
386+
387+
if (list == head->prev) /* Zero or one elements */
364388
return;
365-
}
366389

367-
struct list_head *first = head->next;
368-
first->prev->next = NULL;
390+
/* Convert to a null-terminated singly-linked list. */
369391
head->prev->next = NULL;
370392

371-
first = merge_two_list(first, cmp);
393+
do {
394+
size_t bits;
395+
struct list_head **tail = &pending;
396+
397+
/* Find the least-significant clear bit in count */
398+
for (bits = count; bits & 1; bits >>= 1)
399+
tail = &(*tail)->prev;
400+
/* Do the indicated merge */
401+
if (__glibc_likely(bits)) {
402+
struct list_head *a = *tail, *b = a->prev;
403+
404+
a = merge(cmp, b, a);
405+
/* Install the merged result in place of the inputs */
406+
a->prev = b->prev;
407+
*tail = a;
408+
}
372409

373-
struct list_head *tail = first;
374-
while (tail->next) {
375-
tail = tail->next;
410+
/* Move one element from input list to pending */
411+
list->prev = pending;
412+
pending = list;
413+
list = list->next;
414+
pending->next = NULL;
415+
count++;
416+
} while (list);
417+
418+
/* End of input; merge together all the pending lists. */
419+
list = pending;
420+
pending = pending->prev;
421+
for (;;) {
422+
struct list_head *next = pending->prev;
423+
424+
if (!next)
425+
break;
426+
list = merge(cmp, pending, list);
427+
pending = next;
376428
}
377-
head->next = first;
378-
first->prev = head;
379-
tail->next = head;
380-
head->prev = tail;
429+
/* The final merge, rebuilding prev links */
430+
merge_final(cmp, head, pending, list);
381431
}
382432

383433
void q_sort(struct list_head *head, bool descend)
384434
{
385-
merge_sort(head, cmp_func);
435+
list_sort(head, cmp);
386436

387437
if (descend) {
388438
q_reverse(head);

0 commit comments

Comments
 (0)