分類: 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容器中有兩個位...