J11: sort
主講人: jserv / 課程討論區: 2021 年系統軟體課程
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
返回「Linux 核心設計」課程進度表
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
預期目標
Hybrid sorting
除了在教科書出現的經典排序演算法,如 merge sort 和 quick sort,在諸多軟體會採用截長補短的策略,融合兩項或更多排序演算法,這樣的案例包含 Timsort 和 Introsort。
Introsort 可看做 quick sort 的強化,遞迴分割陣列,區間越來越短,數字也幾乎排序好。對於幾乎已經排序好的短區間,quick sort 表現不佳,於是切換為 heapsort。分水嶺通常設定成 ,N
是輸入資料的長度。之前學員已嘗試實作 Introsort,可見:
以下是參考的 Introsort 實作:
#include <limits.h>
#include <stdint.h>
#include <string.h>
typedef int (*comparator_t)(const void *, const void *);
typedef struct {
char *low, *high;
} stack_node_t;
#define idx(x) (x) * data_size
#define STACK_SIZE (sizeof(size_t) * CHAR_BIT)
static inline int __log2(size_t x)
{
return 63 - __builtin_clzll(x);
}
#define WORD_ALIGNED 1
static inline void swap(void *restrict _a, void *restrict _b, size_t data_size)
{
#if WORD_ALIGNED
long *a = (long *) _a, *b = (long *) _b;
while ((data_size--) / sizeof(long) > 0) {
long tmp = *a;
*a++ = *b;
*b++ = tmp;
}
#else
char *a = (char *) _a, *b = (char *) _b;
while (data_size-- > 0) {
char tmp = *a;
*a++ = *b;
*b++ = tmp;
}
#endif
}
void sort(void *_array,
size_t length,
size_t data_size,
comparator_t comparator)
{
if (length == 0)
return;
char *array = (char *) _array;
const size_t max_thresh = data_size << 2;
const int max_depth = __log2(length) << 1;
char tmp[data_size];
if (length > 16) {
char *low = array, *high = array + idx(length - 1);
stack_node_t stack[STACK_SIZE] = {0};
stack_node_t *top = stack + 1;
int depth = 0;
while (stack < top) {
if (depth > max_depth) {
size_t part_length = (size_t)((high - low) / data_size) - 1;
if (part_length > 0) {
size_t i, j, k = part_length >> 1;
do {
i = k;
j = (i << 1) + 2;
memcpy(tmp, low + idx(i), data_size);
while (j <= part_length) {
if (j < part_length)
j += (comparator(low + idx(j),
low + idx(j + 1)) < 0);
if (comparator(low + idx(j), tmp) <= 0)
break;
memcpy(low + idx(i), low + idx(j), data_size);
i = j;
j = (i << 1) + 2;
}
memcpy(low + idx(i), tmp, data_size);
} while (k-- > 0);
do {
i = part_length;
j = 0;
memcpy(tmp, low + idx(part_length), data_size);
while (j < part_length) {
if (j < part_length - 1)
j += (comparator(low + idx(j),
low + idx(j + 1)) < 0);
memcpy(low + idx(i), low + idx(j), data_size);
i = j;
j = (i << 1) + 2;
}
while (i > 1) {
j = (i - 2) >> 1;
if (comparator(tmp, low + idx(j)) <= 0)
break;
memcpy(low + idx(i), low + idx(j), data_size);
i = j;
}
memcpy(low + idx(i), tmp, data_size);
} while (part_length-- > 0);
}
--top;
--depth;
low = top->low;
high = top->high;
continue;
}
char *mid = low + data_size * ((high - low) / data_size >> 1);
if (comparator(mid, low) < 0)
swap(mid, low, data_size);
if (comparator(mid, high) > 0)
swap(mid, high, data_size);
else
goto skip;
if (comparator(mid, low) < 0)
swap(mid, low, data_size);
skip:;
char *left = low + data_size, *right = high - data_size;
do {
while (comparator(left, mid) < 0)
left += data_size;
while (comparator(mid, right) < 0)
right -= data_size;
if (left < right) {
swap(left, right, data_size);
if (mid == left)
mid = right;
else if (mid == right)
mid = left;
left += data_size, right -= data_size;
} else if (left == right) {
left += data_size, right -= data_size;
break;
}
} while (left <= right);
if ((size_t)(right - low) <= max_thresh &&
(size_t)(high - left) <= max_thresh) {
--top;
--depth;
low = top->low;
high = top->high;
}
else if ((size_t)(right - low) <= max_thresh &&
(size_t)(high - left) > max_thresh) {
low = left;
}
else if ((size_t)(right - low) > max_thresh &&
(size_t)(high - left) <= max_thresh) {
high = right;
} else {
if ((right - low) > (high - left)) {
top->low = low, top->high = right;
low = left;
++top;
++depth;
} else {
top->low = left, top->high = high;
high = right;
++top;
++depth;
}
}
}
}
const size_t gaps[2] = {1ul, 4ul};
int i = 1;
do {
if (length < 4)
continue;
for (size_t j = gaps[i], k = j; j < length; k = ++j) {
memcpy(tmp, array + idx(k), data_size);
while (k >= gaps[i] &&
comparator(array + idx(k - gaps[i]), tmp) > 0) {
memcpy(array + idx(k), array + idx(k - gaps[i]), data_size);
k -= gaps[i];
}
memcpy(array + idx(k), tmp, data_size);
}
} while (i-- > 0);
}
Introsort 又可進一步擴充為 Pattern Defeating Quicksort,可簡稱為 pdqsort,縮減比較的次數,進行細部改良,可參見論文 Pattern-defeating Quicksort。

swenson/sort 彙整多項排序實作,並提供多種情境的效能評估,參考執行輸出:
延伸閱讀:
- CppCon 2019: Andrei Alexandrescu "Speed Is Found In The Minds of People"
In all likelihood, sorting is one of the most researched classes of algorithms. It is a fundamental task in Computer Science, both on its own and as a step in other algorithms. Efficient algorithms for sorting and searching are now taught in core undergraduate classes. Are they at their best, or is there more blood to squeeze from that stone? This talk will explore a few less known – but more allegro! – variants of classic sorting algorithms. And as they say, the road matters more than the destination. Along the way, we'll encounter many wondrous surprises and we'll learn how to cope with the puzzling behavior of modern complex architectures.
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Pseudorandom number generator
xoroshiro128+ 是個 Pseudorandom number generator,有著優異的效能表現,被若干 JavaScript 引擎採用 (如 V8, Chrome, Firefox, Safari),儘管無法通過 BigCrush 測試。
詳閱 xoshiro / xoroshiro generators and the PRNG shootout
sysprog21/ksort 整合 xoroshiro128+ 作為亂數產生器,並實作對應的 Linux 核心模組,從而允許使用者層級的資料存取。
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
ksort: 改寫自 Linux 核心的排序實作
sysprog21/ksort 從 Linux 核心原始程式碼中,萃取以下檔案:
注意 lib/sort.c 採用 heapsort,平均可達到 次比較,在最差的狀況下則是 次。
開發環境的需求與 fibdrv 描述相同,不該在虛擬機器環境裡測試,否則會有效能偏差的疑慮
取得原始程式碼並測試:
預期可見二次 Passed [-]
訊息。
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
作業要求