PHP核心中的神器之HashTable

2021-07-02 01:36:51 字數 4734 閱讀 5079

分類: c/c++

php 2013-04-25 17:23

7137人閱讀收藏 

舉報目錄(?)

[+]

雜湊表(或雜湊表),是將鍵名key按指定的雜湊函式hash經過hash(key)計算後對映到表中乙個記錄,而這個陣列就是雜湊表。

這裡的hash指任意的函式,例如md5、crc32、sha1或你自定義的函式實現。

hashtable是一種查詢效能極高的資料結構,在很多語言內部都實現了hashtable。

理想情況下hashtable的效能是o(1)的,效能消耗主要集中在雜湊函式hash(key),通過hash(key)直接定位到表中的記錄。

而在實際情況下經常會發生key1 != key2,但hash(key1) = hash(key2),這種情況即hash碰撞問題,碰撞的概率越低hashtable的效能越好。當然hash演算法太過複雜也會影響hashtable效能。

在php核心也同樣實現了hashtable並廣泛應用,包括執行緒安全、全域性變數、資源管理等基本上所有的地方都能看到它的身影。

不僅如此,在php指令碼中陣列(php的陣列實質就是hashtable)也是被廣泛使用的,例如陣列形式的配置檔案、資料庫的查詢結果等,可以說是無處不在。

那麼既然php的陣列使用率這麼高,內部是如何實現的?它如何解決hash碰撞及實現均勻分布的?php指令碼使用陣列應該注意哪些?

首先通過**,大致理解php hashtable的實現。

修正:之前認為php解決hahs衝突時,鍊錶使用的是單向鍊錶。

檢視\zend\zend_hash.c的zend_hash_move_backwards_ex方法與zend_hash_del_key_or_index方法後,實際上使用的是雙向鍊錶

下面通過原始碼來一步一步分析。

php實現hashtable主要是通過兩個資料結構bucket(桶)和hashtable。

從php指令碼端來看,hashtable相當於array物件,而bucket相當於array物件裡的某個元素。對於多維陣列實際就是hashtable的某個bucket裡儲存著另乙個hashtable。

hashtable結構:

[cpp]view plain

copy

typedef

struct

_hashtable  hashtable;  

bucket結構:

[cpp]view plain

copy

typedef

struct

bucket  bucket;  

php核心雜湊表的雜湊函式很簡單,直接使用 (hashtable->ntablesize & hashtable->ntablemask)的結果作為雜湊函式的實現。這樣做的目的可能也是為了降低hash演算法的複雜度和提高效能。

1.1)在php中初始化乙個空陣列時,對應核心中是如何建立hashtable的

[php]view plain

copy

$array

= new

array();  

[cpp]view plain

copy

//省略了部分**,提出主要的邏輯

zend_api int

_zend_hash_init(hashtable *ht, uint nsize, hash_func_t phashfunction, dtor_func_t pdestructor, zend_bool persistent zend_file_line_dc)  

else

ht->ntablesize = 1 <

}  ht->ntablemask = ht->ntablesize - 1;  

....  

return

success;  

}  

從上看出,即使在php中初始化乙個空陣列或不足8個元素的陣列,都會被建立8個長度的hashtable。同樣建立100個元素的陣列,也會被分配128長度的hashtable。依次類推。

php陣列中,鍵名可以為數字或字串型別。而在核心中只允許數字索引,對於字串索引,核心採用了time33演算法將字串轉換為整型。具體的實現下面會詳細說明。

[php]view plain

copy

$array

[0] = 

"hello hashtable"

;  

[cpp]view plain

copy

//省略了部分**,提出主要的邏輯

zend_api int

_zend_hash_index_update_or_next_insert(hashtable *ht, ulong h, 

void

*pdata, uint ndatasize, 

void

**pdest, 

intflag zend_file_line_dc)  

p->nkeylength = 0; /* numeric indices are marked by ****** the nkeylength == 0 */

p->h = h;  

init_data(ht, p, pdata, ndatasize);  

if(pdest)   

ht->arbuckets[nindex] = p;  

ht->nnumofelements++;  

return

success;  

}  

上述也說明了,核心中雜湊表的雜湊函式就是簡單的h & ht->ntablemask,其中h代表php中設定的索引號,ntablemask等於雜湊表分配的長度-1。

[php]view plain

copy

$array

['index'

] = 

"hello hashtable"

;  

與數字索引相比,只是多了一步將字串轉換為整型。用到的演算法是time33

下面貼出了演算法的實現,就是對字串的每個字元轉換為ascii碼乘上33並且相加得到的結果。

[cpp]view plain

copy

static

inline

ulong zend_inline_hash_func(

const

char

*arkey, uint nkeylength)  

switch

(nkeylength)   

return

hash;  

}  zend_hash.c  

//下面省略了部分**,提出主要的邏輯

zend_api int

_zend_hash_add_or_update(hashtable *ht, 

const

char

*arkey, uint nkeylength, 

void

*pdata, uint ndatasize, 

void

**pdest, 

intflag zend_file_line_dc)  

p->nkeylength = 0; /* numeric indices are marked by ****** the nkeylength == 0 */

p->h = h;  

init_data(ht, p, pdata, ndatasize);  

if(pdest)   

ht->arbuckets[nindex] = p;  

ht->nnumofelements++;  

return

success;  

}  

2.1) 均勻分布

均勻分布是指,將需要儲存的各個元素均勻的分布到hashtable中。

而負責計算具體分布到表中哪個位置的函式就是雜湊函式做的事情,所以雜湊函式的實現直接關係到均勻分布的效率。

上面也提到了php核心中用了簡單的方式實現:h & ht->ntablemask;

hash碰撞是指,經過hash演算法後得到的值會出現key1 != key2, 但hash(key1)卻等於hash(key2)的情況,這就是碰撞問題。

在php核心來看,就是會出現key1 != key2, 但key1 & ht->ntablemask卻等於 key2 & ht->ntablemask的情況。

php核心使用雙向鍊錶的方式來儲存衝突的資料。即bucket本身也是乙個雙向鍊錶,當發生衝突時,會將資料按順序向後排列。

如果不發生衝突,bucket即是長度為1的的雙向鍊錶。

[cpp]view plain

copy

zend_api 

intzend_hash_find(

const

hashtable *ht, 

const

char

*arkey, uint nkeylength, 

void

**pdata)  

}  p = p->pnext;  

}  return

failure;  

}  

之後,將會寫一篇關於利用hash演算法,進行分布式儲存的介紹。

PHP核心中的HashTable

本文和大家分享的主要是php核心的hashtable相關使用,希望通過本文的分享,對大家學習php有所幫助。typedef struct bucket bucket typedef struct hashtable hashtable 這個是乙個簡化過的雜湊表結構 bucket是乙個鍊錶,而 has...

php核心中的變數

php是弱型別語言,它可以儲存任何的資料型別。但是php是使用c語言編寫的,而c語言是強型別語言。每個變數都有固定的型別,不能隨意改變變數的型別。在zend zend.h中,檢視結構體 zval結構體就是通常用到的php變數在核心總的表示形式,在zval結構體中,可以看到四個成員變數,分別是 zva...

PHP變數在核心中的實現

我們都知道php是乙個弱型別語言,它的變數理論上可以儲存任何型別的資料。那麼,php的變數在核心中究竟是怎麼實現的呢?在php核心中,變數稱為zval,變數的值稱為zend value,注意這是兩個不同的東西。php中變數的記憶體是通過引用計數的方式進行管理的,在php7之前,zval容器中有兩個位...