contributed by < SmallHanley
>
1
解釋上述程式碼運作原理
首先是資料結構的部分:
struct hlist_node
這個結構體包含指標 next
,用來指向下一個節點,以及指標的指標 prev
,指向前一個節點的 next
指標,可以在接到 hlist_head
時不用額外判斷,避免分支。
這裡不懂開發者選擇使用雙向鏈結串列的考量,查看 history 最早出現 hash_node
是在 v2.5.64,github commit history 沒有這麼舊的資料,ChangeLog-2.5.64 也沒有查到有用的資訊。
struct hlist_head
結構體則是 hash table 各個 bucket 的 head,其中 first
指標指向第一個 struct hlist_node
節點。
而 map_t
是 hash table,bits
代表 bucket 的多寡 (以多少位元表示),因此 bucket 的數量為 2 的冪。ht
則是一個陣列,用來存放每個 bucket 的 head。
struct hash_key
結構體是用來包裝每個 key-value pair,key 會根據 hash function 找到對應的 bucket,該 bucket 的 struct hlist_head
會串接所有映射到這個 bucket 的 struct hlist_node
,示意圖如下:
bits
來決定 hash table 的大小,bits
代表 bucket 的多寡 (以多少位元表示),因此 hash table 的 size 為 2 的冪。配置 map_t
以及 map->ht
的空間,並將所有 entry 的 first
指標指向 NULL
。0x61C88647
,看後續分析 linux kernel 相關實作會不會探討,這裡先跳過。struct hlist_node
結構體,如果該結構體的 key
是我們要找的,則回傳該結構體,若找不到則回傳 NULL
。find_key()
取得目標結構體,並回傳其 data
。研讀 Linux 核心原始程式碼 include/linux/hashtable.h 及對應的文件 How does the kernel implements Hashtables?,解釋 hash table 的設計和實作手法,並留意到 tools/include/linux/hash.h 的 GOLDEN_RATIO_PRIME
,探討其實作考量
光是 hash table 的 define 就有分三種:
中間多了 __read_mostly
,追蹤相關程式碼,發現 __read_mostly
的定義是被分散在不同處理器架構的 cache.h (arch/xxx/include/asm/cache.h),其中,有部分架構定義如下,包含 arm
、arm64
、ia64
、mips
、parisc
、powerpc
、s390
、sh
、sparc
、x86
:
其中 __section
又被定義在 include/linux/compiler_attributes.h 中,在 GNU C and C++ 中,可以用來描述函式的特定性質 (參見 Declaring Attributes of Functions)。而這裡使用到的是 section
這個 attribute (參見 Function-Attributes-section),概念上是將一段程式放置在指定的 special section,推測跟 cache 的相關優化有關。
Linux hashtable.h 有 include linux/rculist.h
,可以參考 linux Documentation rcu.rst、listRCU.rst。
重點整理:
struct hlist_head
組成的陣列,每個 struct hlist_head
是映射到同一個 bucket 的鏈結串列的 head。將每個結構體的成員 first
初始化成 NULL
。Also add various WRITE_ONCE() in __hlist_del(), hlist_add_head()
and hlist_add_before()/hlist_add_behind() to pair with
the READ_ONCE().
在 tools/include/linux/hash.h 可以發現一個巨集 GOLDEN_RATIO_PRIME
,
在 commit 689de1d 後改成以下實作版本:
其中有討論到有關 GOLDEN_RATIO_PRIME
的實作考量,出自於 The Art of Computer Programming (Knuth vol 3, section 6.4, exercise 9) 這本書,以下取自這本書的片段。
Theorem S. Let be any irrational number. When the points {}, {}, …, {} are placed in the line segment [0 … 1], then line segments formed have at most three different lengths. Moreover, the next point {} will fall in one of the largest existing segments.
(用 Three-gap theorem 推導。)
{} 代表 的小數部分。
之所以要取小數部分,原理是這個分割問題可以 model 成 Three-gap theorem。line segment [0 … 1] 對應到 Three-gap theorem 中周長為 1 的圓, 的整數部分相當於繞了圓多少圈 (即在 line segment 循環多少次),而小數部分相當於新的點坐落於圓上的哪一個位置 (即 line segment 中新分割點的位置)。
有關 Theorem S 的證明在 exercise 8
任意數乘上這兩個數再取其高位元,可呈現好的數值分布。實際的 hash function (舉 32-bit 為例) 是定義在 tools/include/linux/hash.h:
上述的實作是將 0 ~ 1 的浮點數以 32-bit 無號整數表示。第 6 行將欲進行 hash 的數值 val
乘上 GOLDEN_RATIO_32
,第 12 行是取最高的 bits
個位元。對應上述 Fibonacci Hashing 理論,val
對應到 ,GOLDEN_RATIO_32
為 以 32-bit 無號整數表示,兩者相乘,因為會產生溢位 (一次溢位相當於 Three-gap theorem 中繞了圓一圈),相乘的結果為 {} 以 32-bit 無號整數表示,取其高位元,則相當於取小數前幾位數。
實際對比新、舊版本的 GOLDEN_RATIO_PRIME
實作,新版的實作可以讓 hash function 結果的分佈更為均勻。以下分別測試 0 到 1000、10000 映射到 hash table 不同 locations 的次數,hash table 的大小為 1024 (10 bits):
並行程式設計: Atomics 操作
6.46 When is a Volatile Object Accessed?
Documentation/memory-barriers
Golden_ratio
Fibonacci_number#Relation_to_the_golden_ratio