---
# System prepended metadata

title: 2024q1 Homework6 (integration)
tags: [Linux核心實作]

---

# 2024q1 Homework6 (integration)
contributed by < `dockyu` >

## 自我檢查清單

- [x] 研讀前述 Linux 效能分析 描述，在自己的實體電腦運作 GNU/Linux，做好必要的設定和準備工作 從中也該理解為何不希望在虛擬機器中進行實驗;
- [x] 閱讀〈Linux 核心模組運作原理〉並對照 Linux 核心原始程式碼 (v6.1+)，解釋 insmod 後，Linux 核心模組的符號 (symbol) 如何被 Linux 核心找到 (使用 List API)、MODULE_LICENSE 巨集指定的授權條款又對核心有什麼影響 (GPL 與否對於可用的符號列表有關)，以及藉由 strace 追蹤 Linux 核心的掛載，涉及哪些些系統呼叫和子系統？ 
- [ ] 探討 Timsort, Pattern Defeating Quicksort (pdqsort) 及 Linux 核心 lib/sort.c 在排序過程中的平均比較次數，並提供對應的數學證明;
- [ ] 解釋 xoroshiro128+ 的原理 (對照〈Scrambled Linear Pseudorandom Number Generators〉論文)，並利用 ksort 提供的 xoro 核心模組，比較 Linux 核心內建的 /dev/random 及 /dev/urandom 的速度，說明 xoroshiro128+ 是否有速度的優勢？其弱點又是什麼？ 

#### Linux 效能分析的重點

:::danger
查閱第一手材料了嗎？
:::

1. 時間的計算: 我看不懂 jiffies ， ktime_t 就是一個可以用來取得當前時間並計算時間差的結構
2. 不受干擾的環境: CPU affinity 可以將行程綁定在某顆 CPU 上，也可以在開機時指定 CPU 只給綁定的行程使用。文中還有提到一些可以排除干擾的指令
3. 統計方法: 排除分佈兩端的極端值

:::danger
注意用語：
* virtual machine 是「虛擬機器」，不該簡稱為「機」，否則無法區分 engine 的翻譯
* physical 不作為學科時，翻譯為「實體」
> 了解，已修改
:::

就算在虛擬機器上建立了排除干擾，但是在實體的 CPU 上其實還是存在，因為虛擬機只是模擬而無法直接操作硬體，所以不應該在虛擬機進行

#### 閱讀〈Linux 核心模組運作原理〉
參考 [vax-r/linux2024-homework6](https://hackmd.io/@vax-r/linux2024-homework6) 
strace insmod 可以找到 `finit_module(3, "", 0)` ， finit_module -> load_module -> [simplify_symbols](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L2237)
###### [kernel/module.c](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L2237)
```c 
static int simplify_symbols(struct module *mod, const struct load_info *info)
{
    Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
    Elf_Sym *sym = (void *)symsec->sh_addr;
    
    const struct kernel_symbol *ksym;
    for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) {
        switch (sym[i].st_shndx) {

        switch (sym[i].st_shndx) {
            case SHN_COMMON:
            case SHN_ABS:
            case SHN_LIVEPATCH:
            case SHN_UNDEF:
            case SHN_UNDEF:
                ksym = resolve_symbol_wait(mod, info, name);
            default:
        }
}
```

可以從上面的程式碼知道首先會從核心模組的 ELF 中提取 symbol table 的 section ，並遍歷所有 symbol ，如果是 undefined 那就代表這個 symbol 是在外部的所以要用 resolve_symbol_wait 去查找
[resolve_symbol_wait](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L1427) -> [resolve_symbol](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L1385) -> [find_symbol](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L576) -> [each_symbol_section](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L441) -> [list_for_each_entry_rcu](https://elixir.bootlin.com/linux/v4.18/source/include/linux/rculist.h#L351)

###### rculist.h/list_for_each_entry_rcu
```c 
/**
 * list_for_each_entry_rcu	-	iterate over rcu list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 *
 * This list-traversal primitive may safely run concurrently with
 * the _rcu list-mutation primitives such as list_add_rcu()
 * as long as the traversal is guarded by rcu_read_lock().
 */
#define list_for_each_entry_rcu(pos, head, member) \
	for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
		&pos->member != (head); \
		pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
```
list_entry_rcu 是一個用 List API 用來遍歷 rcu list

#### linux 檔案系統

:::danger
注意用語：
* file 是「檔案」，而非「文件」(document)
> 已修改
:::

:::danger
注意用語：
* access 是「存取」，而非「訪問」(visit)
* 參見[詞彙對照表](https://hackmd.io/@l10n-tw/glossaries)
:::

:::danger
查閱第一手材料，避免閱讀低劣的簡體中文翻譯。
:::

試著執行 `cat /dev/random` 會一直顯示無法解碼的隨機數到終端
kernel module 提供的功能可以透過 device (`/dev/<device>`)讓用戶空間透過讀寫操作使用，這也是這一次作業要做的

:::danger
什麼是「噴出」？
> 已修改

expose 不該翻譯為「暴露」，你學習漢語多年，可以好好說話嗎？
:::

## 作業主題一: 並行的混合排序

ksort 已經定義 /dev/sort 的 read 操作如下(有簡化過)，會掉用 sort_main 函式
```c 
static ssize_t sort_read(struct file *file,
                         char *buf,
                         size_t size,
                         loff_t *offset)
{
    void *sort_buffer = kmalloc(size, GFP_KERNEL);
    len = copy_from_user(sort_buffer, buf, size);
    sort_main(sort_buffer, size / es, es, num_compare);
    len = copy_to_user(buf, sort_buffer, size);
    kfree(sort_buffer);
}
static const struct file_operations fops = {
    .read = sort_read,
}
```
sort_main 的定義如下(有簡化過)，會執行 qsort_algo
```c 
static void init_qsort(struct qsort *q,
                       void *elems,
                       size_t size,
                       struct common *common)
{
    INIT_WORK(&q->w, qsort_algo);
}
static void qsort_algo(struct work_struct *w)
{
    // qsort algorithm
}
void sort_main(void *sort_buffer, size_t size, size_t es, cmp_t cmp)
{
    init_qsort(q, sort_buffer, size, &common);
    queue_work(workqueue, &q->w);
}
```
這次作業的目標是要在 sort module 實做3種排序演算法，並且可以切換，我認為應該要在 sort 設備的 write 操作進行設定，並在 sort_main 判斷要用哪一個
用一個枚舉表示
```c 
typedef enum { QSORT, TIMSORT, PDQSORT, LIB_SORT } sort_method_t;
```
sort(read) 前先設定(write)
```c 
sort_method_t method = TIMSORT;
if (write(fd, &method, sizeof(method)) != sizeof(method)) {
    perror("Failed to set sort method");
    close(fd);
    goto error;
}

ssize_t r_sz = read(fd, inbuf, size);
```
> [commit 80b7ece](https://github.com/dockyu/ksort/commit/80b7ecef54de4db6a9fa91a02cf1203e6ad4c8fd)

sort 時判斷要用哪一個演算法
```c 
switch (sort_method) {
    case QSORT:
        printk(KERN_INFO "Start sorting: qsort\n");
        break;
    case TIMSORT:
        printk(KERN_INFO "Start sorting: timsort\n");
        break;
    case PDQSORT:
        printk(KERN_INFO "Start sorting: pdqsort\n");
        break;
    case LIB_SORT:
        printk(KERN_INFO "Start sorting: lib/sort\n");
        break;
}
```
> [commit a201a1b](https://github.com/dockyu/ksort/commit/a201a1b178fa0c2a05d1452af6ccb97e47ec4eb4)

### workqueue 運作方式

```c 
struct workqueue_struct *workqueue;

struct qsort {
    struct work_struct w;
    struct common *common;
    void *a;
    size_t n;
};
```

查看 [workqueue_types.h](https://elixir.bootlin.com/linux/latest/source/include/linux/workqueue_types.h#L16) 
```c 
typedef void (*work_func_t)(struct work_struct *work);
struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
}
```
可以發現 func 是一個 pointer to function ，接著看到 ksort 的 qsort_algo 函式，這是一個排序演算法的實現也就是這個 work 實際要執行的函式，函式原型吻合 work_func_t 的型別
```c 
static void qsort_algo(struct work_struct *w)
```
在看到 init_qsort 函式使用的一個 `INIT_WORK(&q->w, qsort_algo);`
```c 
static void init_qsort(struct qsort *q,
                       void *elems,
                       size_t size,
                       struct common *common)
{
    INIT_WORK(&q->w, qsort_algo);
    q->a = elems;
    q->n = size;
    q->common = common;
}
```
INIT_WORK 定義在 [workqueue.h#L288](https://elixir.bootlin.com/linux/latest/source/include/linux/workqueue.h#L288)
```c 
#define INIT_WORK(_work, _func)						\
	__INIT_WORK((_work), (_func), 0)
```
展開 `INIT_WORK`
```c 
__INIT_WORK((&q->w), (qsort_algo), 0)
```
`__INIT_WORK` 的定義
```c 
#define __INIT_WORK(_work, _func, _onstack)				\
	do {								\
		static __maybe_unused struct lock_class_key __key;	\
									\
		__INIT_WORK_KEY(_work, _func, _onstack, &__key);	\
	} while (0)
```
展開 `__INIT_WORK`
```c 
do {
    static __maybe_unused struct lock_class_key __key;
    __INIT_WORK_KEY(&q->w, qsort_algo, 0, &__key);
} while (0)
```
`__INIT_WORK_KEY` 的定義
```c 
#define __INIT_WORK_KEY(_work, _func, _onstack, _key)			\
	do {								\
		__init_work((_work), _onstack);				\
		(_work)->data = (atomic_long_t) WORK_DATA_INIT();	\
		INIT_LIST_HEAD(&(_work)->entry);			\
		(_work)->func = (_func);				\
	} while (0)
```
展開 `__INIT_WORK_KEY`
```c 
do {
    static __maybe_unused struct lock_class_key __key;
	do {
		__init_work((&q->w), 0);
		(&q->w)->data = (atomic_long_t) WORK_DATA_INIT();
		INIT_LIST_HEAD(&(&q->w)->entry);
		(&q->w)->func = (qsort_algo);
	} while (0)
} while (0)
```
可以看到 `(&q->w)->func = (qsort_algo);` 會把 work_struct 的 func 設為 qsort_algo
```c 
init_qsort(q, sort_buffer, size, &common);
```
接著 init_qsort 後半部將要排序的資料 (sort_buffer) 放進 struct qsort

init_qsort 之後是 queue_work
```c 
queue_work(workqueue, &q->w);
```
查看 [workqueue.h#L545](https://elixir.bootlin.com/linux/latest/source/include/linux/workqueue.h#L545)
```c 
static inline bool queue_work(struct workqueue_struct *wq,
			      struct work_struct *work)
{
	return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
```
查看 [kernel/workqueue.c#L1828](https://elixir.bootlin.com/linux/latest/source/kernel/workqueue.c#L1828) `queue_work_on` 裡面呼叫
```c 
__queue_work(cpu, wq, work);
```
查看 [kernel/workqueue.c#L1705](https://elixir.bootlin.com/linux/latest/source/kernel/workqueue.c#L1705) `__queue_work` 裡面呼叫
```c 
static void __queue_work(int cpu, struct workqueue_struct *wq,
			 struct work_struct *work)
{
    struct pool_workqueue *pwq;
    struct worker_pool *last_pool, *pool;
    if (likely(pwq->nr_active < pwq->max_active)) {
        insert_work(pwq, work, &pool->worklist, work_flags);
    } else {
        insert_work(pwq, work, &pwq->inactive_works, work_flags);
    }
```
查看 [kernel/workqueue.c#L1647](https://elixir.bootlin.com/linux/latest/source/kernel/workqueue.c#L1647) `insert_work` ，參數的 head 在正常情況下是 worker_pool 的 worklist 屬性， work_struct 的 entry 被加進去
```c 
static void insert_work(struct pool_workqueue *pwq, struct work_struct *work,
			struct list_head *head, unsigned int extra_flags)
{
    list_add_tail(&work->entry, head);
```
從 [kernel/workqueue.c](https://elixir.bootlin.com/linux/latest/source/kernel/workqueue.c#L171) 可以看到 worklist 的註解
```c 
struct worker_pool {
	struct list_head	worklist;	/* L: list of pending works */
```
閱讀 [kernel/workqueue.c#L2539](https://elixir.bootlin.com/linux/latest/source/kernel/workqueue.c#L2539) 可以看到我覺得最精妙的設計，先複習一下 struct qsort 裡的屬性有 work_struct 以及 要排序的資料以及 compare function ，接著 work_struct 裡的屬性有一個 func 這是一個指到要執行的函式的指標，這個函式接受一個 work_struct ????? **這不是套娃嗎??????**，但是又好像非常合理因為要排序的資料確實可以用 container_of 巨集拿到，我認為這種作法的好處是可以統一 work_struct ，不用為每種功能都定義自己 work_struct 而是將 work_struct 當成一個屬性，而 work_queue 只管理 work_struct ，需要的資料使用者可以自己用 container_of 拿到，以下程式碼展示 kernel 是怎麼做到 **我的屬性拿我自己當作參數傳入**
```c 
static void process_one_work(struct worker *worker, struct work_struct *work)
{
    worker->current_func = work->func;
    worker->current_func(work);
```

### Timsort
嘗試使用 [2024q1 第 1 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1) 中的 timsort 程式碼，只需要修改 compare function 為整數的形式
參考 [vax-r/lab0-c](https://github.com/vax-r/lab0-c)， [millaker/lab0-c](https://github.com/millaker/lab0-c)
commit: [8366658](https://github.com/dockyu/ksort/commit/83666581028a2a22bcfded9d2a0aca8c6cd203df)

### linux sort
原本的程式碼已經有 `#include <linux/sort.h>`
```c 
void sort(void *base, size_t num, size_t size,
	  cmp_func_t cmp_func,
	  swap_func_t swap_func);
```
只需要有適合的 compare function 和 swap funciton 就可以使用， compare funciton 直接使用和 qsort 同一個就好
commit: [d3b5381](https://github.com/dockyu/ksort/commit/d3b53814e28d5bd6204e875341b78dc41ad817e3)

### userspace 測量時間
commit: [a58c220](https://github.com/dockyu/ksort/commit/a58c220e4299a40640e99a8d00841c513d0625b9)
|最小資料大小|最大資料大小|步數|
|:-:|:-:|:-:|
|1000|20000|500|

資料太多或是跑太多次都有可能卡住，建議從較小的數字開始測試

|糟糕的資料|正常的資料|
|:-:|:-:|
|![image](https://hackmd.io/_uploads/B1BgyJVzA.png)|![userspace_sorting_times](https://hackmd.io/_uploads/S1iAwJNfR.png)|

### kernelspace 測量時間
```c 
start = ktime_get();
queue_work(workqueue, &q->w);

/* Ensure completion of all work before proceeding, as reliance on
 * objects allocated on the stack necessitates this. If not, there is a
 * risk of the work item referencing a pointer that has ceased to exist.
 */
drain_workqueue(workqueue);
end = ktime_get();
```

|kernel space|user space|
|:-:|:-:|
|![image](https://hackmd.io/_uploads/SkyqHzHMC.png)|![userspace_sorting_times](https://hackmd.io/_uploads/HyKX8zBG0.png)|

commit: [7a9366c](https://github.com/dockyu/ksort/commit/7a9366c92b93b57b5627764ada6af3ed8bded7f7)

### 指定cpu執行

未指定前的輸出
```bash 
[92911.088946] Working on CPU 13
[92911.088955] Set sort method: Quick Sort
[92911.089297] Set sort method: Tim Sort
[92911.089312] Start sorting: timsort
[92911.090026] Working on CPU 16
[92911.090027] Start timsort_algo
[92911.092800] Set sort method: Tim Sort
[92911.093131] Set sort method: Library Sort
[92911.093141] Start sorting: linux/sort
[92911.093145] Working on CPU 16
[92911.094921] Set sort method: Library Sort
[92911.095267] Set sort method: Quick Sort
[92911.095278] Start sorting: qsort
[92911.095281] Working on CPU 16
```

```diff 
- queue_work(workqueue, &l->w);
+ queue_work_on(cpu_id, workqueue, &l->w);
```
指定後的輸出
```bash 
[93789.082169] Working on CPU 10
[93789.082170] Working on CPU 10
[93789.082170] Working on CPU 10
[93789.082177] Set sort method: Quick Sort
[93789.082452] Set sort method: Tim Sort
[93789.082458] Start sorting: timsort
[93789.082800] Working on CPU 10
[93789.082800] Start timsort_algo
[93789.085116] Set sort method: Tim Sort
[93789.085365] Set sort method: Library Sort
[93789.085370] Start sorting: linux/sort
[93789.085372] Working on CPU 10
[93789.086783] Set sort method: Library Sort
[93789.134480] sort: unloaded
[93789.178624] XORO: Exit
```
### 綁定 cpu

![image](https://hackmd.io/_uploads/S1GeLXIGR.png)

```bash 
$ ps -eo pid,psr,cmd | awk '$2 == 10'
     44  10 [cpuhp/10]
     45  10 [idle_inject/10]
     46  10 [migration/10]
     47  10 [ksoftirqd/10]
    143  10 [oom_reaper]
    169  10 [mld]
    667  10 [card0-crtc0]
    668  10 [card0-crtc1]
    669  10 [card0-crtc2]
    913  10 /lib/systemd/systemd-logind
   1015  10 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 129:137
   2169  10 /usr/libexec/packagekitd
   2550  10 /usr/libexec/gvfs-afc-volume-monitor
   2575  10 /usr/libexec/goa-identity-service
   2595  10 /usr/libexec/gdm-x-session --run-script env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu
   2718  10 /usr/libexec/gnome-session-ctl --monitor
   2859  10 /snap/snapd-desktop-integration/157/usr/bin/snapd-desktop-integration
  12350  10 /usr/libexec/gnome-terminal-server
  49373  10 /usr/share/code/code --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --crashpad-handler-pid=49350 --enable-crash-reporter=ae5778e7-98d3-44f9-a135-93a16359f3ea,no_channel --user-data-dir=/home/dockyu/.config/Code --standard-schemes=vscode-webview,vscode-file --enable-sandbox --secure-schemes=vscode-webview,vscode-file --cors-schemes=vscode-webview,vscode-file --fetch-schemes=vscode-webview,vscode-file --service-worker-schemes=vscode-webview --code-cache-schemes=vscode-webview,vscode-file --shared-files=v8_context_snapshot_data:100 --field-trial-handle=0,i,17914157464607285421,3578834844614996182,262144 --disable-features=CalculateNativeWinOcclusion,SpareRendererForSitePerProcess
  49493  10 /home/dockyu/.vscode/extensions/ms-vscode.cpptools-1.19.9-linux-x64/bin/cpptools
  50449  10 [kworker/10:5H-ttm]
  50451  10 [kworker/10:7H-ttm]
  50874  10 [kworker/10:0H-ttm]
  50933  10 [kworker/10:1H-ttm]
  50934  10 [kworker/10:2H-ttm]
  50935  10 [kworker/10:3H-ttm]
  50936  10 [kworker/10:4H-ttm]
  50937  10 [kworker/10:6H-ttm]
  50938  10 [kworker/10:8H-ttm]
  52961  10 [kworker/10:9H-ttm]
  52962  10 [kworker/10:10H-ttm]
  52963  10 [kworker/10:11H-ttm]
  52964  10 [kworker/10:12H-ttm]
  52965  10 [kworker/10:13H-ttm]
  52966  10 [kworker/10:14H-ttm]
  52967  10 [kworker/10:15H-ttm]
  52968  10 [kworker/10:16H]
  53616  10 [kworker/10:1-events]
  60136  10 [kworker/10:0-mm_percpu_wq]
  60272  10 /snap/firefox/4173/usr/lib/firefox/firefox -contentproc -childID 149 -isForBrowser -prefsLen 33447 -prefMapSize 249080 -jsInitLen 234952 -parentBuildID 20240419214016 -greomni /snap/firefox/4173/usr/lib/firefox/omni.ja -appomni /snap/firefox/4173/usr/lib/firefox/browser/omni.ja -appDir /snap/firefox/4173/usr/lib/firefox/browser {a0f3b1dc-0b7e-4f91-b433-f31d23320b5d} 11743 true tab
```

更改 `/etc/default/grub`
```diff 
- GRUB_CMDLINE_LINUX_DEFAULT=" "
+ GRUB_CMDLINE_LINUX_DEFAULT="isolcpus=10"
```

```bash 
$ ps -eo pid,psr,cmd | awk '$2 == 10'
     16  10 [rcu_preempt]
     44  10 [cpuhp/10]
     45  10 [idle_inject/10]
     46  10 [migration/10]
     47  10 [ksoftirqd/10]
     48  10 [kworker/10:0-events]
     49  10 [kworker/10:0H-kblockd]
    134  10 [kdevtmpfs]
    136  10 [kauditd]
    183  10 [kworker/10:1-events]
    343  10 [jbd2/nvme0n1p7-8]
   2993  10 [kworker/10:1H-kblockd]
```

## 論文閱讀 Xorshift RNGs

提出 Xorshift RNGS，可以通過各種亂度檢驗
各種 xorshift RNGs 的變形的週期(period) 可以達到 $2^{160}$ ，代表這個方法快又可靠(週期足夠大)
其他 RNG 也可以達到這麼大的週期，但是通常會需要乘法運算，而 xorshift RNG 只需要 xor 和 shift

:::success
T 是 nonsingular matrix 代表 T 有反矩陣
T 的 order 是 K 代表 $T^K$ 是單位矩陣，且 K 為最小的可能
Cayley-Hamilton 定理可以將矩陣的高維運算轉為低維的，ex: $T^3=3T+2I$
可以用 奇異值分解（SVD）方法快速判斷一個矩陣是不是 nonsingular matrix
:::
這句好像寫錯
![image](https://hackmd.io/_uploads/H1PQOGwyC.png)

---
### 推導
#### 週期性函數
$\beta$ 是一個 $1*n$ 的 binary vector ex: [0, 1, 1, 0, 1, 1, 0, 0,...] 
$T$ 是一個 $n*n$ 的 binary matrix

$T$ 的 order 為 $2^n-1$ 的**充要條件**為 $T$ 是 non-singular matrix
如果 $T$ 的 order 為 $2^n-1$ 代表 sequence: $\beta, \beta T, \beta T^2, \beta T^3 \dots$ 的週期是 $2^n-1$ ，因為 $T^{2^n-1}=I$ ，得出 $\beta T^{2^n-1}=\beta I=\beta$ 直到第 $2^n-1$ 個才會重複

如果找到一個 $T$ 那麼就可以做出一個週期為 $2^n-1$ 的 RNG，且因為 $\beta$ 是 binary vector 剛好就是二進制表示的數字形式
所有 non-singular 的 nxn matrix 都可以作為 $T$，但是**矩陣乘法太慢了**

---
#### 快速操作
如果 $\beta T$ 能變成在電腦上的簡單操作(ex: xor, shift) 就可以加快速度 ，但是並不是每一個 $T$ 都可以轉換成這種操作
論文提出一種方法
如果假設 $y$ 是一個 n-bits 表示的數字，
$y\oplus (y<<a)$ 操作等同於 $y * (I+L^a)$ ，$L^a$ 是一個 nxn 矩陣，是基於 $a$ 的(並非a次方)

:::info
##### 範例
如果 $y=[0,1,1,0]$ ， $a=2$
那麼 $y\ll a = [1,0,0,0]$
$L^a$ 是 $I$ 向下位移 $a$ 個位子
\begin{align}
I=
\begin{bmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & 1
\end{bmatrix}
,L^a=
\begin{bmatrix}
0 & 0 & 0 & 0\\
0 & 0 & 0 & 0\\
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0
\end{bmatrix}
\end{align}

\begin{align}
I+L^a=
\begin{bmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
1 & 0 & 1 & 0\\
0 & 1 & 0 & 1
\end{bmatrix}
\end{align}

\begin{align}
y(I+L^a)=
\begin{bmatrix}
0 & 1 & 1 & 0
\end{bmatrix}
\begin{bmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
1 & 0 & 1 & 0\\
0 & 1 & 0 & 1
\end{bmatrix}
\end{align}
可以發現 $y(I+L^a)$ 之後在對每一格取 $\mod 2$ 其實等同於 $y\oplus (y \ll a)$
:::
###### $T=(I+L^a)$
所以找到一個數字 $a$ 可以得出 $L^a$ ，如果$T=I+L^a$ 且 $T$ 是 non-singular 那麼 $\beta T$ 可以視為 $\beta\oplus(\beta\ll a)$ ，在電腦上可以快速做到
因為電腦上數字是用 32-bits 表示，所以 n 要是 32 的倍數 ex: 32, 64, 96，當 n 為 32 時 $a$ 只有可能是 1~32(因為 $\beta$ 的長度是 32)，這 32 個 $a$ 得到的 $T$ 是 non-singular 嗎? 可惜都不是，這代表 $T=I+L^a$ 這個形式的 $T$ 的 order 不會是 $2^n-1$ 週期無法保證夠長

---
###### $T=(I+L^a)(I+R^b)$
如果 $T=(I+L^a)(I+R^b)$
\begin{align}
\beta T = \beta (I+L^a)(I+R^b)
\end{align}

等同先左移再右移

\begin{align}
\beta=\beta\oplus(\beta\ll a) \\
\beta=\beta\oplus(\beta\gg b)
\end{align}

這個形式的 $T$ 由 $(a,b)$ 決定 $1\le a \le 32,1\le b \le 32$ ， $(a,b)$ 共有 $32*32=1024$ 種可能，所以 $T$ 也有 $1024$ 種
那麼這 $1024$ 種 $T$ 有 non-singular 嗎?可惜也沒有
###### $T=(I+L^a)(I+R^b)(I+L^c)$
這種形式的 $T$ 做 $\beta T$ 等同於

\begin{align}
\beta=\beta\oplus(\beta\ll a) \\
\beta=\beta\oplus(\beta\gg b) \\
\beta=\beta\oplus(\beta\ll c)
\end{align}

$T$ 是由 $(a,b,c)$ 得出的，$1\le a \le 32,1\le b \le 32,1\le c \le 32$ ， $(a,b,c)$ 共有 $32*32*32=32768$ 種可能，這 32768 種 $T$ 有多種是 non-singular 也就是 order $2^n-1$
其中有 81 種 $a<c$，同時會在下面的式子代表的 $T$ 也會滿足 non-singular 所以也可以視作找到 $32*8=648$ 種 xorshift 的參數使週期等於 $2^{32}-1$ (在 n 為 32 的情況下)
###### 32-bit 週期為 $2^{32}-1$ 的 xorshift 參數
|$(a,b,c)$|||||||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 1, 3,10| 1, 5,16| 1, 5,19| 1, 9,29| 1,11, 6| 1,11,16| 1,19, 3| 1,21,20| 1,27,27|
| 2, 5,15| 2, 5,21| 2, 7, 7| 2, 7, 9| 2, 7,25| 2, 9,15| 2,15,17| 2,15,25| 2,21, 9|
| 3, 1,14| 3, 3,26| 3, 3,28| 3, 3,29| 3, 5,20| 3, 5,22| 3, 5,25| 3, 7,29| 3,13, 7|
| 3,23,25| 3,25,24| 3,27,11| 4, 3,17| 4, 3,27| 4, 5,15| 5, 3,21| 5, 7,22| 5, 9,7 |
| 5, 9,28| 5, 9,31| 5,13, 6| 5,15,17| 5,17,13| 5,21,12| 5,27, 8| 5,27,21| 5,27,25|
| 5,27,28| 6, 1,11| 6, 3,17| 6,17, 9| 6,21, 7| 6,21,13| 7, 1, 9| 7, 1,18| 7, 1,25|
| 7,13,25| 7,17,21| 7,25,12| 7,25,20| 8, 7,23| 8,9,23 | 9, 5,1 | 9, 5,25| 9,11,19|
| 9,21,16|10, 9,21|10, 9,25|11, 7,12|11, 7,16|11,17,13|11,21,13|12, 9,23|13, 3,17|
|13, 3,27|13, 5,19|13,17,15|14, 1,15|14,13,15|15, 1,29|17,15,20|17,15,23|17,15,26|
###### 32-bit 週期為 $2^{32}-1$ 的 xorshift 作法
```txt
yˆ=y<<a; yˆ=y>>b; yˆ=y<<c;
yˆ=y<<c; yˆ=y>>b; yˆ=y<<a;
yˆ=y>>a; yˆ=y<<b; yˆ=y>>c;
yˆ=y>>c; yˆ=y<<b; yˆ=y>>a;
yˆ=y<<a; yˆ=y<<c; yˆ=y>>b;
yˆ=y<<c; yˆ=y<<a; yˆ=y>>b;
yˆ=y>>a; yˆ=y>>c; yˆ=y<<b;
yˆ=y>>c; yˆ=y>>a; yˆ=y<<b;
```

---
64-bit 下則有 275 組 triple $(a,b,c)$ 可以讓 order 來到 $2^{64}-1$
也可以視為 $8*275=2200$ 組

---
### 論文證明的邏輯
1. non-singular matrix 的 order 為 $2{n}-1$
2. non-singular matrix 可以讓 binary vector(可以視為 32-bit 數字) $\beta$ 作為 seed 時 ，RNG: $f(\beta)=\beta T$ 的週期為 $2^{n}-1$
3. 如果 matrix 可以寫成這種形式 $T=(I+L^a)$ 就可以用 xorshift $\beta\text{^}(\beta\ll a)$ 取代 $\beta T$
4. 暴力找所有 $(a,b,c)$ 的組合(32768 個)，得出多個(32768 個) $T=(I+L^a)(I+R^b)(I+L^c)$
5. 找出其中是 non-singular 的 T，共 81 個

---
### 總結
因為 $\beta T$ 的週期為 $2^{n}-1$ 是在 $\beta$ 為 binary vector 的 space 成立的，也就是說只要是 binary vector(ex: 電腦的 unsigned int)就可以當作 seed ，用得出的任一 triple (a,b,c) 做 xorshift 週期必定為 $2^n-1$
|xorshift|其他 RNG|
|:-:|:-:|
|任一(for all) seed 都有很長的週期|某些 seed 週期可能很短|
|只有 xor, shift 操作，速度快|可能會使用到乘法|
