一個提高N倍系統新能的程式設計點,卻總是被普通開發們遺忘

龍躍十二發表於2020-09-23

位運算這個概念並不陌生,大多數程式設計師在進入這個領域的時候或多或少都接觸過位運算,估計當時都寫過不少練習題的。

位運算本身不難,困難的是大家沒有學會在系統設計時用上它,提高系統效能,增加你的不可替代性。

就不做太多鋪墊了,直接說下今天講述的乾貨內容:

位運算使用場景

面試經常問

比如我曾經在面試騰訊的時候

O(1) 時間如何檢測整數 n 是否是 2 的冪次?

在看一道Google面試題:

有64瓶藥,其中63瓶是無毒的,一瓶是有毒的。如果做實驗的小白鼠喝了有毒的藥,3天后會死掉,當然喝了其它的藥,包括同時喝幾種就沒事。現在只剩下3天時間,請問最少需要多少隻小白鼠才能試出那瓶藥有毒?

這就不用龍su囉嗦了吧,穩穩的都是和位運算有關的。

類似面試題目還有很多,一個不注意就會被撂倒。

這部分的題目整體難度不大,本身不是一個很大的知識點,但是很容易被大家忽略,今天龍su就拿出來好好說說,大家可要記住喔,不然…

系統設計經常用

喜歡看原始碼的同學就會注意到,經常在裡面看到這樣的程式碼。

lucene原始碼

redis原始碼

龍叔的原始碼

有沒有發現這些程式碼驚人的相似,好的設計總是這樣不謀而合

看了這麼多,想必大家已經知道這東西還是有些作用的,應該好好搞清楚他的原理。接下來就一起來盤他。

位運算原理

指的是位元位(bit),不是byte,所以位運算指的就是位元位計算。

CPU所有計算都是二進位制的計算,一個高效能的服務一定是把CPU資源利用到極致,也就是用最少資源換取最大收益。

當然隨著現代CPU的計算速度不斷加快,很多人在設計系統的時候完全不會去考慮這些效能點,然而真正的高併發系統都是極致效能的。

看看我們日常開發都是啥樣的,只要不涉及到高併發,開發程式碼就算是一坨屎,也沒關係,大多數人都是在這坨屎上繼續CRUD,也就會變成了一大坨。

沒辦法,老闆只看結果,懶得管你的程式碼是什麼樣的。哎呀,好像暴露了龍叔是個CRUD菜雞選手。

等到有一天發現加機器加到扛不住了,這時候就是最幸運的一批程式設計師誕生的時候,必須開始重構系統。為什麼最幸運,大家都知道了吧?機會不是天天有的,這就是千載難逢的良機啊。

哈,好像有點說遠了。

在計算機世界裡,萬物皆0、1,0、1生萬物。萬物到0、1的過程叫做編碼。

一個數在計算機中的二進位制表示形式, 叫做這個數的機器數。機器數是帶符號的,在計算機中用一個數的最高位存放符號, 正數為0, 負數為1。

計算機中對數字的編碼表示有三種方式:原碼反碼補碼

原碼:原碼錶示法在數值前面增加了一位符號位(即最高位為符號位):正數該位為0,負數該位為1。比如十進位制3如果用8個二進位制位來表示就是 00000011, -3就是 10000011。

反碼:反碼錶示方法:正數的反碼是其本身;負數的反碼是在其原碼的基礎上,符號位不變,其餘各個位取反。

補碼:補碼錶示方法:正數的補碼是其本身;負數的補碼是在其原碼的基礎上,符號位不變,其餘各位取反,最後+1。 (即在反碼的基礎上+1)

這三種是編碼方式,但是在計算機系統中,數值一律用補碼來表示(儲存)。

舉個例子:

110
  原碼           反碼         補碼
00001010  --> 00001010 --> 00001010
2. -15
10001111  --> 11110000 --> 11110001  

說完了資料編碼,基本已經知道一個資料是怎麼儲存在計算機中的,接下來就看看資料位元位之間是如何計算的。

各種程式語言都提供了對補碼的二進位制位直接進行運算的方法,即位運算

符號 描述 規則
& 相同位的兩個數字都為1,則為1;若有一個不為1,則為0。
| 相同位只要一個為1即為1。
~ 0和1全部取反。
^ 亦或 相同位不同則為1,相同則為0。
<< 左移 a << b就表示把a轉為二進位制後左移b位(在後面添b個0)。
>> 右移 a >> b表示二進位制右移b位(去掉末b位),相當於a除以2的b次方(取整)。

舉幾個例子

10 & -15 = 00001010 & 11110001

按位進行相與,相同為1則為1,否則為0,最終算的結果為00000000 即0

10 & 15 = 00001010 & 00001111

按位進行相與,相同為1則為1,否則為0,最終算的結果為00001010 即10

10 | 15 = 00001010 | 00001111

按位進行或邏輯,相同位只要一個為1即為1 ,00001111即15

15>>2

二進位制右移2位,左邊填符號號位,右邊抹掉,得到00000011 即3

15<<2

二進位制左移2位,左邊抹掉,符號位不變,右邊填0,得到00111100

原理還是比較簡單,主要就是對位元位進行邏輯操作。

位運算為什麼那麼快?

看到這裡其實大多數人已經明白為什麼位運算快了,但暖心的龍叔還是在囉嗦下原因,就算是錦上添花(畫蛇添足)了。

  • 儲存更友好,位元位儲存,不用轉換後在儲存
  • CPU更友好,直接位元位操作,減少機器數到位元位的轉換
  • 定址次數更少,左移一位就乘2

說一個搜尋裡面位運算帶來的效能提升

比如你在百度搜尋 廣東富婆 ,分詞會分為 廣東 富婆 兩個詞,分別從兩個倒排中召回,假設 廣東 這個詞召回了100w個doc,富婆 召回了1000W個。

此時兩個doc鏈會進行一個合併,合併的返回結果是存在廣東的同時又要存在富婆的doc。

這個合併如果是通過位元位的方式操作的話,一個64位的CPU一個指令週期可以處理64個doc,如果採用普通合併的話,一次只能合併一個doc,這個效能提升很明顯的吧,是不是感覺高效能有點意思了。

像這種效能上的提升,是無法通過增加機器解決的。

總結

這次內容不難,講出來是希望大家在做系統設計時,效能考慮不是簡單的加機器,而是真的把CPU價值最大化。

小改動、大效果,一些小的改動,會對效能提升產生很多的效果。反正我這次設計時基本把一些計算都改為了位運算。

我是龍叔,一個在網際網路大器晚成的設計師,我們下期見。喜歡我,記得關注我。

相關文章