# 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 操作,速度快|可能會使用到乘法|