從底層深入go的基礎模型是什麼(從底層深入Go的基礎模型)
2023-04-17 19:27:42 2
作者:得物技術
出處:https://segmentfault.com/a/1190000037749103
interfaceIn Object-oriented programming, a protocol or interface is a common means for unrelated Object (computer science) to communicate with each other. These are definitions of Method (computer programming) and values which the objects agree upon in order to co-operate. —— wiki
在wiki中是這樣定義的,interface和protocol類似,都是雙方為了交流而作出的約定。
直接拿這套定義去理解golang中的interface可能難以理解,那麼可以換種說法,即interface就像包餃子。
具體類型就像餃子餡,而預先定義的接口則就是餃子皮,也就是對具體類型進行了一層封裝後,放入桌上(itabTable),隨需隨拿。最後吃的仍然是餡。
(真正使用的仍然是接口中包著的具體類型方法)
01.為什麼要使用interface在golang中不支持泛型,如果不使用interface的話,需要考慮不同類型的入參,複製粘貼n份,累得慌。
而有了interface之後,各種類型都能封裝成interface,因此間接的實現了泛型編程,能夠用來寫接受多種類型入參的函數。
這一點可以利用來做單元測試 (mock入參) ,以及各服務之間的解偶 (比如只提供一個redis的增刪改查接口,實現層可隨意替換實現方式不影響業務) 。
1.2 隱藏具體實現用戶只能使用interface提供的方法,而具體的實現細節則不需要暴露。(典型案例context.Context)
1.3 提供插入點有點像java的靜態代理,就是在調用函數的時候,在前面做點別的事情。
舉個例子就是在http請求之前加header的實現:
eface顧名思義 empty interface,沒有定義方法的interface底層結構即為eface。
eface只有_type以及指向數據(拷貝)的指針。
2.2 iface定義了方法的interface底層結構為iface。
iface則還有定義接口方法,因此有有一個tab屬性以及指向數據(拷貝)的指針。
golang有一個itabTable哈希表,即利用空間換時間的思路,存放所有的itab,具體實現方式則通過一個數組(entries)實現。
3.2 如何對itab進行哈希取itab中的接口類型與實際類型,分別哈希後取異或。
itabTable作為一個哈希表,插入和讀取肯定不可能是每次遍歷整個數組,這樣非常耗費性能。
因此go中itabTable使用的是quadratic probing。
公式為**h(i) = h0 i(i 1)/2 mod 2^k
h0:起點,也就是一開始 將itab哈希之後的值 。(在這裡對itab哈希的實現是通過將interfacetype和itab分別哈希之後異或獲得)
i(i 1)/2:用於防止哈希衝突**,其實就是趨向於1 2 3 4 5 6...的函數表達式,在實現中是一個for循環,不斷增加偏移量,比如一開始a 1,如果bucket已被佔位或不是目標內容,則下一次找a 1 2的位置,還不是就a 1 2 3。為了防止一直遞增超過哈希表(數組)的大小,所以加一個mod 2^k(mod數組的長度)
其實也就是一種 二次尋址法的實現 。
04.itabTable的增與刪遍歷接口類型與具體類型比較具體類型是否實現了所有接口類型, 理論上時間複雜度為O(n^2)。
但是實際上因為接口類型與具體類型的插入都是按照字典序排序的,因此實際上 時間複雜度為O(mn) ,使用雙指針遍歷即可。
itab使用接口類型(interfaceType)以及具體類型(_type)初始化之後,就能將itab放置與itabTable。
使用的插入方法正是之前的 二次尋址法 。
在itabTable中根據接口類型以及具體類型尋找itab。
使用的搜索模式也是 二次尋址法 。
代碼皆不可信,我們再用彙編來證實一下以上的思路。
彙編代碼:
全文讀至此,應該能讀懂runtime.convI2I方法了
有興趣的可以根據上文的內容,解讀一下上面彙編中使用的方法。
END
作者:得物技術
出處:https://segmentfault.com/a/1190000037749103
,