讀Redis學C程式設計一:怎麼實現rand

來源:互聯網
上載者:User

標籤:c   redis   linux   

在開始這個系列之前,首先說說什麼是redis。redis是一個ANSI C編寫的高效能Key-Value記憶體資料庫,也是現在nosql資料庫的代表之一。通過對redis2.8.17程式碼數進行了統計,包括注釋總共大約5萬行,在開源家族裡面算是非常短小精悍了,而且項目從2009年開始,距離現在很近,代碼風格也非常適合我們80,90後程式員的知識結構。當具備一定編碼經驗之後,優秀的源碼是我們最好的老師,尤其是已經被實踐驗證了的代碼。特別由於最近正在苦讀APUE和UNP,讀到許多東西還是感到疑惑,而這種疑惑我感覺更應該到實際的代碼中找答案。

網上各種關於redis的源碼分析博文已算是海量了,但作為以學習目的在unix/linux c中的菜鳥,筆者更喜歡先從代碼模組本身自低向上分析redis,尤其注重去學習其優秀的程式碼片段。好了,閑話少說,先從今天看到的一個小模組rand來開始。對於有一定c語言編程經驗的程式員來說,rand是一個內在功能,ANSI C提供了關於rand和srand實現。那為什麼redis還要做重複造輪子的活動呢?

/* Pseudo random number generation functions derived from the drand48() * function obtained from pysam source code. * * This functions are used in order to replace the default math.random() * Lua implementation with something having exactly the same behavior * across different systems (by default Lua uses libc's rand() that is not * required to implement a specific PRNG generating the same sequence * in different systems if seeded with the same integer). * * The original code appears to be under the public domain. * I modified it removing the non needed functions and all the * 1960-style C coding stuff... * * ----------------------------------------------------------------------------
這是關rand.c前面的注釋,這裡我們可以看到兩個內容。第一,作者認為不同的系統中實現math.random的方式不一樣,由此作者使用了這一個模組代替系統中rand的實現方式,第二,它也不是redis的原生的模組,而是來自一個pysam項目。不過對於我們來說也先不在乎它到底為了什麼,還是從哪裡來,而更簡單地看看它是如何?的。rand採用的演算法叫drand48,為什麼是drand48,是因為它使用的是48bit的空間一種線性同餘演算法。那什麼又是線性同餘演算法?我們看看維基百科的解釋

線性同餘方法(LCG)是個產生偽隨機數的方法。

它是根據遞迴公式:

其中是產生器設定的常數。

LCG的周期最大為,但大部分情況都會少於M。要令LCG達到最大周期,應符合以下條件:

  1. 互質;
  2. 的所有質因子都能整除;
  3. 若是4的倍數,也是;
  4. 都比小;
  5. 是正整數。
實際上就是一種迭代取餘來求服從均勻分布偽隨機數的演算法,在drand48中這個A選擇了0x5DEECE66D,B選擇了0xB,M選擇了2^48,M的選擇可以理解,因為最大值設定是2^48,至於A和B的選擇那麼首先是符合前面五條設定,而至於為什麼在符合設定的數中選擇了它們,估計還有更嚴格的數學證明吧,在這裡筆者水平有限也無力深究了。那麼接下來有意思的事情來了,對於我們來說,如果拿到這麼一個式子,直接一句話不解決了麼n = (a*n+b)%m?那我們看看作者怎麼做。
#include <stdint.h>#define N16#define MASK((1 << (N - 1)) + (1 << (N - 1)) - 1)#define LOW(x)((unsigned)(x) & MASK)#define HIGH(x)LOW((x) >> N)#define MUL(x, y, z){ int32_t l = (long)(x) * (long)(y); (z)[0] = LOW(l); (z)[1] = HIGH(l); }#define CARRY(x, y)((int32_t)(x) + (long)(y) > MASK)#define ADDEQU(x, y, z)(z = CARRY(x, (y)), x = LOW(x + (y)))#define X00x330E#define X10xABCD#define X20x1234#define A00xE66D#define A10xDEEC#define A20x5#define C0xB#define SET3(x, x0, x1, x2)((x)[0] = (x0), (x)[1] = (x1), (x)[2] = (x2))#define SETLOW(x, y, n) SET3(x, LOW((y)[n]), LOW((y)[(n)+1]), LOW((y)[(n)+2]))#define SEED(x0, x1, x2) (SET3(x, x0, x1, x2), SET3(a, A0, A1, A2), c = C)#define REST(v)for (i = 0; i < 3; i++) { xsubi[i] = x[i]; x[i] = temp[i]; } return (v);#define HI_BIT(1L << (2 * N - 1))static uint32_t x[3] = { X0, X1, X2 }, a[3] = { A0, A1, A2 }, c = C;static void next(void);int32_t redisLrand48() {    next();    return (((int32_t)x[2] << (N - 1)) + (x[1] >> 1));}void redisSrand48(int32_t seedval) {    SEED(X0, LOW(seedval), HIGH(seedval));}static void next(void) {    uint32_t p[2], q[2], r[2], carry0, carry1;    MUL(a[0], x[0], p);    ADDEQU(p[0], c, carry0);    ADDEQU(p[1], carry0, carry1);    MUL(a[0], x[1], q);    ADDEQU(p[1], q[0], carry0);    MUL(a[1], x[0], r);    x[2] = LOW(carry0 + carry1 + CARRY(p[1], r[0]) + q[1] + r[1] +            a[0] * x[2] + a[1] * x[1] + a[2] * x[0]);    x[1] = LOW(p[1] + r[0]);    x[0] = LOW(p[0]);}
首先,我們發現好像比我們想的要複雜的多。每個數按照16位為一個單位被分到了一個長度為三的數組中,而其四則運算也發生了改變,通過定義MUL來表示乘法,ADDEQU來表示加法,LOW表示取低16位,HIGH表示取高十六位,CARRAY來判斷是否有進位發生。從而實現了與一句運算式相同的功能。這時我們心中有疑問why,我們試著解讀邊界條件,由於用到48bit的結果,而實際運算結果更是超出48bit,比如A就為36bit,如果n為48bit則總公可能產生的為84bit,甚至超出了long long的範圍。而採用數組的方式實現四則運算則不會出現這一問題。代碼中大量使用了宏函數和位元運算,在此實現中我們可以看到這些技巧是C程式設計中處理數值類問題的利器,值得借鑒和學習。

讀Redis學C程式設計一:怎麼實現rand

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.