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

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

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

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

位運算使用場景

面試經常問

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

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價值最大化。

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章