前面我們討論了內(nèi)存的工作原理,也進(jìn)行了一些性能相關(guān)的測(cè)試。那么今天開(kāi)始我們來(lái)看幾個(gè)在實(shí)踐中的應(yīng)用。首先我們先從PHP開(kāi)始。2015年,PHP7的發(fā)布可以說(shuō)是在技術(shù)圈里引起了不小的轟動(dòng),因?yàn)樗膱?zhí)行效率比PHP5直接翻了一倍。PHP7在內(nèi)存方面,你是否知道作者都進(jìn)行了哪些優(yōu)化?幾個(gè)核心結(jié)構(gòu)體的改進(jìn)只是表面上看起來(lái)優(yōu)化的幾個(gè)字節(jié)那么簡(jiǎn)單?讓我們從幾個(gè)核心的數(shù)據(jù)結(jié)構(gòu)改進(jìn)開(kāi)始看起。
1 PHP7zval的變化
1、php5.3中的zval:
我們這里只討論64位操作系統(tǒng)下的情況。該zval_struct結(jié)構(gòu)體中的由四個(gè)成員構(gòu)成,其中zvalue_value稍微復(fù)雜一些,是一個(gè)聯(lián)合體。聯(lián)合體中最長(zhǎng)的成員是一個(gè)指針加一個(gè)int,8+4=12字節(jié)。但是默認(rèn)情況下,會(huì)進(jìn)行內(nèi)存對(duì)齊,故zval_struct會(huì)占用16字節(jié)。那么。
_zval_struct總的字節(jié) = value(16)+ refcount__gc(4)+ type(1)+ is_ref__gc(1)= 占用22字節(jié)。
最后再考慮下內(nèi)存對(duì)齊,實(shí)際占用24字節(jié)。(如果算的有點(diǎn)暈話(huà),感興趣的同學(xué)可以寫(xiě)段簡(jiǎn)單的測(cè)試代碼,使用sizeof查看一下)
2、PHP7.2中的zval
7.2中的zval_struct結(jié)構(gòu)體里由3個(gè)成員構(gòu)成,其中zend_value看起來(lái)比較復(fù)雜,實(shí)際上只是一個(gè)8字節(jié)的聯(lián)合體。u1也是一個(gè)聯(lián)合體,占用是4個(gè)字節(jié)。u2也一樣。這樣zval_struct就實(shí)際占用16個(gè)字節(jié)。
2 PHP7 HashTable的變化
1、PHP5.3里的HashTable:
在5.3里HashTable就是一個(gè)大struct, 有點(diǎn)小復(fù)雜,我們拆開(kāi)了細(xì)說(shuō),
uint nTableSize 4字節(jié)
uint nTableMask 4字節(jié)
uint nNumOfElements 4字節(jié),
ulong nNextFreeElement 8字節(jié) 注意這前面的4個(gè)字節(jié)會(huì)被浪費(fèi)掉,因?yàn)閚NextFreeElement的開(kāi)始地址需要對(duì)齊
Bucket *pInternalPointer 8字節(jié)
Bucket *pListHead 8字節(jié)
Bucket *pListTail 8字節(jié)
Bucket **arBuckets 8字節(jié)
dtor_func_t pDestructor 8字節(jié)
zend_bool persistent 1字節(jié)
unsigned char nApplyCoun 1字節(jié)
zend_bool bApplyProtection 1字節(jié)
最終,總字節(jié)數(shù) = 4+4+4+4(nNextFreeElement前面這四個(gè)字節(jié)會(huì)留空)+8+8+8+8+8+8+1+1+1 = 67字節(jié)。再加上結(jié)構(gòu)體本身要對(duì)齊到8的整數(shù)倍,所以實(shí)際占用72字節(jié)。
2、PHP7.2里的HashTable:
在7.2里HashTable
zend_refcounted_h gc 看起來(lái)唬人,實(shí)際就是個(gè)long,占用8字節(jié)
union... u 占用4字節(jié)
uint32_t 占用4字節(jié)
Bucket* 指針占用8字節(jié)
uint32_t nNumUsed 占用4字節(jié)
uint32_t nNumOfElements 占用4字節(jié)
uint32_t nTableSize 占用4字節(jié)
uint32_t nInternalPointer 占用4字節(jié)
zend_long nNextFreeElement 占用8字節(jié)
dtor_func_t pDestructor 占用8字節(jié)
總占用
字節(jié)數(shù) = 8+4+4+8+4+4+4+4+8+8 = 56字節(jié),并且正好達(dá)到了內(nèi)存對(duì)齊的狀態(tài),沒(méi)有額外的浪費(fèi)。
另外還有PHP源代碼里經(jīng)常出鏡的Buckets也從72下降到了32字節(jié),這里我就不翻源代碼了。
3 優(yōu)化思想精髓
當(dāng)當(dāng)當(dāng),敲黑板,重點(diǎn)來(lái)了!我們看了兩個(gè)核心數(shù)據(jù)結(jié)構(gòu)的結(jié)構(gòu)體變化,這上面的優(yōu)化都是什么含義呢?拿HashTable舉例,貌似從72字節(jié)優(yōu)化到了56字節(jié),這內(nèi)存節(jié)約的也不是特別多嘛,才20%多而已!但這中間其實(shí)隱藏了兩個(gè)較深層次優(yōu)化思路:
第一、你是否記得我們前面CPU在向內(nèi)存要數(shù)據(jù)的時(shí)候是以Cache Line為單位進(jìn)行的,而我們說(shuō)過(guò)Cache Line的大小就是64字節(jié)?;剡^(guò)頭來(lái)看HashTable,在7.2里的56字節(jié),只需要CPU向內(nèi)存進(jìn)行一次Cache Line大小的burst IO,就夠了。而在5.3里的72字節(jié),雖然只比Cache Line大了那么一丟丟,但是對(duì)不起,必須得進(jìn)行兩次burst IO才可以。所以,在計(jì)算機(jī)里,56字節(jié)相對(duì)72字節(jié)實(shí)際上是翻倍的性能提升??!
第二、CPU的L1、L2、L3的容量是固定的幾十K或者幾十M。假設(shè)Cache的都是HashTable,那么Cache容量不變的條件下,能Cache住的HashTable將會(huì)翻倍,緩存命中率提升一大截。要知道L1命中后只需要1ns多一點(diǎn)的耗時(shí),而如果穿透到內(nèi)存的話(huà)可能就需要40多納秒的延時(shí)了,整整差了幾十倍。
所以PHP內(nèi)核的作者大牛深諳CPU與內(nèi)存的工作原理,表面上看起來(lái)只是幾個(gè)字節(jié)的節(jié)約,但是實(shí)際上爆發(fā)出了巨大的性能提升??!
審核編輯:劉清
-
cpu
+關(guān)注
關(guān)注
68文章
10807瀏覽量
210852 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
2978瀏覽量
73817 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6689瀏覽量
123142 -
PHP
+關(guān)注
關(guān)注
0文章
452瀏覽量
26630
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論