效率問(wèn)題。因?yàn)閜hp是腳本解釋語(yǔ)言,其特點(diǎn)在于易上手和部署,但在處理需要大量cpu的操作時(shí)(圖片就是)就力不從心了,如果寫(xiě)成php擴(kuò)展的話效率會(huì)提升,但還是沒(méi)直接執(zhí)行C/C++的程序快
創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的漳州網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
不好實(shí)現(xiàn)多線程。這個(gè)就不用多說(shuō)了,和語(yǔ)言定位有關(guān),雖然可以異步調(diào)用,但畢竟不是強(qiáng)項(xiàng)。
1.Bloom filter
適用范圍:可以用來(lái)實(shí)現(xiàn)數(shù)據(jù)字典,進(jìn)行數(shù)據(jù)的判重,或者集合求交集
基本原理及要點(diǎn):
對(duì)于原理來(lái)說(shuō)很簡(jiǎn)單,位數(shù)組+k個(gè)獨(dú)立hash函數(shù)。將hash函數(shù)對(duì)應(yīng)的值的位數(shù)組置1,查找時(shí)如果發(fā)現(xiàn)所有hash函數(shù)對(duì)應(yīng)位都是1說(shuō)明存在,很明顯這個(gè)過(guò)程并不保證查找的結(jié)果是100%正確的。同時(shí)也不支持刪除一個(gè)已經(jīng)插入的關(guān)鍵字,因?yàn)樵撽P(guān)鍵字對(duì)應(yīng)的位會(huì)牽動(dòng)到其他的關(guān)鍵字。所以一個(gè)簡(jiǎn)單的改進(jìn)就是 counting Bloom filter,用一個(gè)counter數(shù)組代替位數(shù)組,就可以支持刪除了。
還有一個(gè)比較重要的問(wèn)題,如何根據(jù)輸入元素個(gè)數(shù)n,確定位數(shù)組m的大小及hash函數(shù)個(gè)數(shù)。當(dāng)hash函數(shù)個(gè)數(shù)k=(ln2)*(m/n)時(shí)錯(cuò)誤率最小。在錯(cuò)誤率不大于E的情況下,m至少要等于n*lg(1/E)才能表示任意n個(gè)元素的集合。但m還應(yīng)該更大些,因?yàn)檫€要保證bit數(shù)組里至少一半為 0,則m 應(yīng)該=nlg(1/E)*lge 大概就是nlg(1/E)1.44倍(lg表示以2為底的對(duì)數(shù))。
舉個(gè)例子我們假設(shè)錯(cuò)誤率為0.01,則此時(shí)m應(yīng)大概是n的13倍。這樣k大概是8個(gè)。
注意這里m與n的單位不同,m是bit為單位,而n則是以元素個(gè)數(shù)為單位(準(zhǔn)確的說(shuō)是不同元素的個(gè)數(shù))。通常單個(gè)元素的長(zhǎng)度都是有很多bit的。所以使用bloom filter內(nèi)存上通常都是節(jié)省的。
擴(kuò)展:
Bloom filter將集合中的元素映射到位數(shù)組中,用k(k為哈希函數(shù)個(gè)數(shù))個(gè)映射位是否全1表示元素在不在這個(gè)集合中。Counting bloom filter(CBF)將位數(shù)組中的每一位擴(kuò)展為一個(gè)counter,從而支持了元素的刪除操作。Spectral Bloom Filter(SBF)將其與集合元素的出現(xiàn)次數(shù)關(guān)聯(lián)。SBF采用counter中的最小值來(lái)近似表示元素的出現(xiàn)頻率。
問(wèn)題實(shí)例:給你A,B兩個(gè)文件,各存放50億條URL,每條URL占用64字節(jié),內(nèi)存限制是4G,讓你找出A,B文件共同的URL。如果是三個(gè)乃至n個(gè)文件呢?
根據(jù)這個(gè)問(wèn)題我們來(lái)計(jì)算下內(nèi)存的占用,4G=2^32大概是40億*8大概是340億,n=50億,如果按出錯(cuò)率0.01算需要的大概是650億個(gè) bit?,F(xiàn)在可用的是340億,相差并不多,這樣可能會(huì)使出錯(cuò)率上升些。另外如果這些urlip是一一對(duì)應(yīng)的,就可以轉(zhuǎn)換成ip,則大大簡(jiǎn)單了。
2.Hashing
適用范圍:快速查找,刪除的基本數(shù)據(jù)結(jié)構(gòu),通常需要總數(shù)據(jù)量可以放入內(nèi)存
基本原理及要點(diǎn):
hash函數(shù)選擇,針對(duì)字符串,整數(shù),排列,具體相應(yīng)的hash方法。
碰撞處理,一種是open hashing,也稱為拉鏈法;另一種就是closed hashing,也稱開(kāi)地址法,opened addressing。 ()
擴(kuò)展:
d-left hashing中的d是多個(gè)的意思,我們先簡(jiǎn)化這個(gè)問(wèn)題,看一看2-left hashing。2-left hashing指的是將一個(gè)哈希表分成長(zhǎng)度相等的兩半,分別叫做T1和T2,給T1和T2分別配備一個(gè)哈希函數(shù),h1和h2。在存儲(chǔ)一個(gè)新的key時(shí),同時(shí)用兩個(gè)哈希函數(shù)進(jìn)行計(jì)算,得出兩個(gè)地址h1[key]和h2[key]。這時(shí)需要檢查T(mén)1中的h1[key]位置和T2中的h2[key]位置,哪一個(gè)位置已經(jīng)存儲(chǔ)的(有碰撞的)key比較多,然后將新key存儲(chǔ)在負(fù)載少的位置。如果兩邊一樣多,比如兩個(gè)位置都為空或者都存儲(chǔ)了一個(gè)key,就把新key 存儲(chǔ)在左邊的T1子表中,2-left也由此而來(lái)。在查找一個(gè)key時(shí),必須進(jìn)行兩次hash,同時(shí)查找兩個(gè)位置。
問(wèn)題實(shí)例:
1).海量日志數(shù)據(jù),提取出某日訪問(wèn)百度次數(shù)最多的那個(gè)IP。
IP的數(shù)目還是有限的,最多2^32個(gè),所以可以考慮使用hash將ip直接存入內(nèi)存,然后進(jìn)行統(tǒng)計(jì)。
3.bit-map
適用范圍:可進(jìn)行數(shù)據(jù)的快速查找,判重,刪除,一般來(lái)說(shuō)數(shù)據(jù)范圍是int的10倍以下
基本原理及要點(diǎn):使用bit數(shù)組來(lái)表示某些元素是否存在,比如8位電話號(hào)碼
擴(kuò)展:bloom filter可以看做是對(duì)bit-map的擴(kuò)展
問(wèn)題實(shí)例:
1)已知某個(gè)文件內(nèi)包含一些電話號(hào)碼,每個(gè)號(hào)碼為8位數(shù)字,統(tǒng)計(jì)不同號(hào)碼的個(gè)數(shù)。
8位最多99 999 999,大概需要99m個(gè)bit,大概10幾m字節(jié)的內(nèi)存即可。
2)2.5億個(gè)整數(shù)中找出不重復(fù)的整數(shù)的個(gè)數(shù),內(nèi)存空間不足以容納這2.5億個(gè)整數(shù)。
將bit-map擴(kuò)展一下,用2bit表示一個(gè)數(shù)即可,0表示未出現(xiàn),1表示出現(xiàn)一次,2表示出現(xiàn)2次及以上?;蛘呶覀儾挥?bit來(lái)進(jìn)行表示,我們用兩個(gè)bit-map即可模擬實(shí)現(xiàn)這個(gè)2bit-map。
4.堆
適用范圍:海量數(shù)據(jù)前n大,并且n比較小,堆可以放入內(nèi)存
基本原理及要點(diǎn):最大堆求前n小,最小堆求前n大。方法,比如求前n小,我們比較當(dāng)前元素與最大堆里的最大元素,如果它小于最大元素,則應(yīng)該替換那個(gè)最大元素。這樣最后得到的n個(gè)元素就是最小的n個(gè)。適合大數(shù)據(jù)量,求前n小,n的大小比較小的情況,這樣可以掃描一遍即可得到所有的前n元素,效率很高。
擴(kuò)展:雙堆,一個(gè)最大堆與一個(gè)最小堆結(jié)合,可以用來(lái)維護(hù)中位數(shù)。
問(wèn)題實(shí)例:
1)100w個(gè)數(shù)中找最大的前100個(gè)數(shù)。
用一個(gè)100個(gè)元素大小的最小堆即可。
5.雙層桶劃分 ----其實(shí)本質(zhì)上就是【分而治之】的思想,重在“分”的技巧上!
適用范圍:第k大,中位數(shù),不重復(fù)或重復(fù)的數(shù)字
基本原理及要點(diǎn):因?yàn)樵胤秶艽?,不能利用直接尋址表,所以通過(guò)多次劃分,逐步確定范圍,然后最后在一個(gè)可以接受的范圍內(nèi)進(jìn)行??梢酝ㄟ^(guò)多次縮小,雙層只是一個(gè)例子。
擴(kuò)展:
問(wèn)題實(shí)例:
1).2.5億個(gè)整數(shù)中找出不重復(fù)的整數(shù)的個(gè)數(shù),內(nèi)存空間不足以容納這2.5億個(gè)整數(shù)。
有點(diǎn)像鴿巢原理,整數(shù)個(gè)數(shù)為2^32,也就是,我們可以將這2^32個(gè)數(shù),劃分為2^8個(gè)區(qū)域(比如用單個(gè)文件代表一個(gè)區(qū)域),然后將數(shù)據(jù)分離到不同的區(qū)域,然后不同的區(qū)域在利用bitmap就可以直接解決了。也就是說(shuō)只要有足夠的磁盤(pán)空間,就可以很方便的解決。
2).5億個(gè)int找它們的中位數(shù)。
這個(gè)例子比上面那個(gè)更明顯。首先我們將int劃分為2^16個(gè)區(qū)域,然后讀取數(shù)據(jù)統(tǒng)計(jì)落到各個(gè)區(qū)域里的數(shù)的個(gè)數(shù),之后我們根據(jù)統(tǒng)計(jì)結(jié)果就可以判斷中位數(shù)落到那個(gè)區(qū)域,同時(shí)知道這個(gè)區(qū)域中的第幾大數(shù)剛好是中位數(shù)。然后第二次掃描我們只統(tǒng)計(jì)落在這個(gè)區(qū)域中的那些數(shù)就可以了。
實(shí)際上,如果不是int是int64,我們可以經(jīng)過(guò)3次這樣的劃分即可降低到可以接受的程度。即可以先將int64分成2^24個(gè)區(qū)域,然后確定區(qū)域的第幾大數(shù),在將該區(qū)域分成2^20個(gè)子區(qū)域,然后確定是子區(qū)域的第幾大數(shù),然后子區(qū)域里的數(shù)的個(gè)數(shù)只有2^20,就可以直接利用direct addr table進(jìn)行統(tǒng)計(jì)了。
6.數(shù)據(jù)庫(kù)索引
適用范圍:大數(shù)據(jù)量的增刪改查
基本原理及要點(diǎn):利用數(shù)據(jù)的設(shè)計(jì)實(shí)現(xiàn)方法,對(duì)海量數(shù)據(jù)的增刪改查進(jìn)行處理。
擴(kuò)展:
問(wèn)題實(shí)例:
7.倒排索引(Inverted index)
適用范圍:搜索引擎,關(guān)鍵字查詢
基本原理及要點(diǎn):為何叫倒排索引?一種索引方法,被用來(lái)存儲(chǔ)在全文搜索下某個(gè)單詞在一個(gè)文檔或者一組文檔中的存儲(chǔ)位置的映射。
以英文為例,下面是要被索引的文本:
T0 = "it is what it is"
T1 = "what is it"
T2 = "it is a banana"
我們就能得到下面的反向文件索引:
"a": {2}
"banana": {2}
"is": {0, 1, 2}
"it": {0, 1, 2}
"what": {0, 1}
檢索的條件"what", "is" 和 "it" 將對(duì)應(yīng)集合的交集。
正向索引開(kāi)發(fā)出來(lái)用來(lái)存儲(chǔ)每個(gè)文檔的單詞的列表。正向索引的查詢往往滿足每個(gè)文檔有序頻繁的全文查詢和每個(gè)單詞在校驗(yàn)文檔中的驗(yàn)證這樣的查詢。在正向索引中,文檔占據(jù)了中心的位置,每個(gè)文檔指向了一個(gè)它所包含的索引項(xiàng)的序列。也就是說(shuō)文檔指向了它包含的那些單詞,而反向索引則是單詞指向了包含它的文檔,很容易看到這個(gè)反向的關(guān)系。
擴(kuò)展:
問(wèn)題實(shí)例:文檔檢索系統(tǒng),查詢那些文件包含了某單詞,比如常見(jiàn)的學(xué)術(shù)論文的關(guān)鍵字搜索。
8.外排序
適用范圍:大數(shù)據(jù)的排序,去重
基本原理及要點(diǎn):外排序的歸并方法,置換選擇 敗者樹(shù)原理,最優(yōu)歸并樹(shù)
擴(kuò)展:
問(wèn)題實(shí)例:
1).有一個(gè)1G大小的一個(gè)文件,里面每一行是一個(gè)詞,詞的大小不超過(guò)16個(gè)字節(jié),內(nèi)存限制大小是1M。返回頻數(shù)最高的100個(gè)詞。
這個(gè)數(shù)據(jù)具有很明顯的特點(diǎn),詞的大小為16個(gè)字節(jié),但是內(nèi)存只有1m做hash有些不夠,所以可以用來(lái)排序。內(nèi)存可以當(dāng)輸入緩沖區(qū)使用。
9.trie樹(shù)
適用范圍:數(shù)據(jù)量大,重復(fù)多,但是數(shù)據(jù)種類(lèi)小可以放入內(nèi)存
基本原理及要點(diǎn):實(shí)現(xiàn)方式,節(jié)點(diǎn)孩子的表示方式
擴(kuò)展:壓縮實(shí)現(xiàn)。
問(wèn)題實(shí)例:
1).有10個(gè)文件,每個(gè)文件1G, 每個(gè)文件的每一行都存放的是用戶的query,每個(gè)文件的query都可能重復(fù)。要你按照query的頻度排序 。
2).1000萬(wàn)字符串,其中有些是相同的(重復(fù)),需要把重復(fù)的全部去掉,保留沒(méi)有重復(fù)的字符串。請(qǐng)問(wèn)怎么設(shè)計(jì)和實(shí)現(xiàn)?
3).尋找熱門(mén)查詢:查詢串的重復(fù)度比較高,雖然總數(shù)是1千萬(wàn),但如果除去重復(fù)后,不超過(guò)3百萬(wàn)個(gè),每個(gè)不超過(guò)255字節(jié)。
10.分布式處理 mapreduce
適用范圍:數(shù)據(jù)量大,但是數(shù)據(jù)種類(lèi)小可以放入內(nèi)存
基本原理及要點(diǎn):將數(shù)據(jù)交給不同的機(jī)器去處理,數(shù)據(jù)劃分,結(jié)果歸約。
擴(kuò)展:
問(wèn)題實(shí)例:
1).The canonical example application of MapReduce is a process to count the appearances of
each different word in a set of documents:
void map(String name, String document):
// name: document name
// document: document contents
for each word w in document:
EmitIntermediate(w, 1);
void reduce(String word, Iterator partialCounts):
// key: a word
// values: a list of aggregated partial counts
int result = 0;
for each v in partialCounts:
result += ParseInt(v);
Emit(result);
Here, each document is split in words, and each word is counted initially with a "1" value by
the Map function, using the word as the result key. The framework puts together all the pairs
with the same key and feeds them to the same call to Reduce, thus this function just needs to
sum all of its input values to find the total appearances of that word.
2).海量數(shù)據(jù)分布在100臺(tái)電腦中,想個(gè)辦法高效統(tǒng)計(jì)出這批數(shù)據(jù)的TOP10。
3).一共有N個(gè)機(jī)器,每個(gè)機(jī)器上有N個(gè)數(shù)。每個(gè)機(jī)器最多存O(N)個(gè)數(shù)并對(duì)它們操作。如何找到N^2個(gè)數(shù)的中數(shù)(median)?
經(jīng)典問(wèn)題分析
上千萬(wàn)or億數(shù)據(jù)(有重復(fù)),統(tǒng)計(jì)其中出現(xiàn)次數(shù)最多的前N個(gè)數(shù)據(jù),分兩種情況:可一次讀入內(nèi)存,不可一次讀入。
可用思路:trie樹(shù)+堆,數(shù)據(jù)庫(kù)索引,劃分子集分別統(tǒng)計(jì),hash,分布式計(jì)算,近似統(tǒng)計(jì),外排序
所謂的是否能一次讀入內(nèi)存,實(shí)際上應(yīng)該指去除重復(fù)后的數(shù)據(jù)量。如果去重后數(shù)據(jù)可以放入內(nèi)存,我們可以為數(shù)據(jù)建立字典,比如通過(guò) map,hashmap,trie,然后直接進(jìn)行統(tǒng)計(jì)即可。當(dāng)然在更新每條數(shù)據(jù)的出現(xiàn)次數(shù)的時(shí)候,我們可以利用一個(gè)堆來(lái)維護(hù)出現(xiàn)次數(shù)最多的前N個(gè)數(shù)據(jù),當(dāng)然這樣導(dǎo)致維護(hù)次數(shù)增加,不如完全統(tǒng)計(jì)后在求前N大效率高。
如果數(shù)據(jù)無(wú)法放入內(nèi)存。一方面我們可以考慮上面的字典方法能否被改進(jìn)以適應(yīng)這種情形,可以做的改變就是將字典存放到硬盤(pán)上,而不是內(nèi)存,這可以參考數(shù)據(jù)庫(kù)的存儲(chǔ)方法。
當(dāng)然還有更好的方法,就是可以采用分布式計(jì)算,基本上就是map-reduce過(guò)程,首先可以根據(jù)數(shù)據(jù)值或者把數(shù)據(jù)hash(md5)后的值,將數(shù)據(jù)按照范圍劃分到不同的機(jī)子,最好可以讓數(shù)據(jù)劃分后可以一次讀入內(nèi)存,這樣不同的機(jī)子負(fù)責(zé)處理各種的數(shù)值范圍,實(shí)際上就是map。得到結(jié)果后,各個(gè)機(jī)子只需拿出各自的出現(xiàn)次數(shù)最多的前N個(gè)數(shù)據(jù),然后匯總,選出所有的數(shù)據(jù)中出現(xiàn)次數(shù)最多的前N個(gè)數(shù)據(jù),這實(shí)際上就是reduce過(guò)程。
實(shí)際上可能想直接將數(shù)據(jù)均分到不同的機(jī)子上進(jìn)行處理,這樣是無(wú)法得到正確的解的。因?yàn)橐粋€(gè)數(shù)據(jù)可能被均分到不同的機(jī)子上,而另一個(gè)則可能完全聚集到一個(gè)機(jī)子上,同時(shí)還可能存在具有相同數(shù)目的數(shù)據(jù)。比如我們要找出現(xiàn)次數(shù)最多的前100個(gè),我們將1000萬(wàn)的數(shù)據(jù)分布到10臺(tái)機(jī)器上,找到每臺(tái)出現(xiàn)次數(shù)最多的前 100個(gè),歸并之后這樣不能保證找到真正的第100個(gè),因?yàn)楸热绯霈F(xiàn)次數(shù)最多的第100個(gè)可能有1萬(wàn)個(gè),但是它被分到了10臺(tái)機(jī)子,這樣在每臺(tái)上只有1千個(gè),假設(shè)這些機(jī)子排名在1000個(gè)之前的那些都是單獨(dú)分布在一臺(tái)機(jī)子上的,比如有1001個(gè),這樣本來(lái)具有1萬(wàn)個(gè)的這個(gè)就會(huì)被淘汰,即使我們讓每臺(tái)機(jī)子選出出現(xiàn)次數(shù)最多的1000個(gè)再歸并,仍然會(huì)出錯(cuò),因?yàn)榭赡艽嬖诖罅總€(gè)數(shù)為1001個(gè)的發(fā)生聚集。因此不能將數(shù)據(jù)隨便均分到不同機(jī)子上,而是要根據(jù)hash 后的值將它們映射到不同的機(jī)子上處理,讓不同的機(jī)器處理一個(gè)數(shù)值范圍。
而外排序的方法會(huì)消耗大量的IO,效率不會(huì)很高。而上面的分布式方法,也可以用于單機(jī)版本,也就是將總的數(shù)據(jù)根據(jù)值的范圍,劃分成多個(gè)不同的子文件,然后逐個(gè)處理。處理完畢之后再對(duì)這些單詞的及其出現(xiàn)頻率進(jìn)行一個(gè)歸并。實(shí)際上就可以利用一個(gè)外排序的歸并過(guò)程。
另外還可以考慮近似計(jì)算,也就是我們可以通過(guò)結(jié)合自然語(yǔ)言屬性,只將那些真正實(shí)際中出現(xiàn)最多的那些詞作為一個(gè)字典,使得這個(gè)規(guī)??梢苑湃雰?nèi)存。
緣起
關(guān)于PHP 很多人的直觀感覺(jué)是PHP是一種靈活的腳本語(yǔ)言 庫(kù)類(lèi)豐富 使用簡(jiǎn)單 安全 非常適合WEB開(kāi)發(fā) 但性能低下 PHP的性能是否真 的就如同大家的感覺(jué)一樣的差呢?本文就是圍繞這么一個(gè)話題來(lái)進(jìn)行探討的 從源碼 應(yīng)用場(chǎng)景 基準(zhǔn)性能 對(duì)比分析等幾個(gè)方面深入分析PHP之性能問(wèn)題 并通 過(guò)真實(shí)的數(shù)據(jù)來(lái)說(shuō)話
從原理分析PHP性能
從原理分析PHP的性能 主要從以下幾個(gè)方面 內(nèi)存管理 變量 函數(shù) 運(yùn)行機(jī)制來(lái)進(jìn)行分析
內(nèi)存管理
類(lèi)似Nginx的內(nèi)存管理方式 PHP在內(nèi)部也是基于內(nèi)存池 并且引入內(nèi)存池的生命周期概念 在內(nèi)存池方面 PHP對(duì)PHP腳本和擴(kuò)展的所有內(nèi) 存相關(guān)操作都進(jìn)行了托管 對(duì)大內(nèi)存和小內(nèi)存的管理采用了不同的實(shí)現(xiàn)方式和優(yōu)化 具體可以參考以下文檔 在內(nèi)存分配和回收的生命周期內(nèi) PHP采用一次初始化申請(qǐng)+動(dòng)態(tài)擴(kuò)容+內(nèi)存標(biāo)識(shí)回收機(jī)制 并且在每次請(qǐng)求結(jié)束后直 接對(duì)內(nèi)存池進(jìn)行重新mask
變量
總所周知 PHP是一種弱變量類(lèi)型的語(yǔ)言 所以在PHP內(nèi)部 所有的PHP變量都對(duì)應(yīng)成一種類(lèi)型Zval 其中具體定義如下
圖一PHP變量
在變量方面 PHP做了大量的優(yōu)化工作 比如說(shuō)Reference counting和copy on writer機(jī)制 這樣能夠保證內(nèi)存使用上的優(yōu)化 并且減少內(nèi)存拷貝次數(shù)(請(qǐng)參考) 在數(shù)組方面 PHP內(nèi)部采用高效的hashtable來(lái)實(shí)現(xiàn)
函數(shù)
在PHP內(nèi)部 所有的PHP函數(shù)都回轉(zhuǎn)化成內(nèi)部的一個(gè)函數(shù)指針 比如說(shuō)擴(kuò)展中函數(shù)
ZEND_FUNCTION?(?my_function?);//類(lèi)似function?my_function(){}
在內(nèi)部展開(kāi)后就會(huì)是一個(gè)函數(shù)
void?zif_my_function?(?INTERNAL_FUNCTION_PARAMETERS?);
void?zif_my_function(
int?ht
zval?*?return_value
zval?*?this_ptr
int?return_value_used
zend_executor_globals?*?executor_globals
);
從這個(gè)角度來(lái)看 PHP函數(shù)在內(nèi)部也是對(duì)應(yīng)一個(gè)函數(shù)指針
運(yùn)行機(jī)制
在話說(shuō)PHP性能的時(shí)候 很多人都會(huì)說(shuō)“C/C++是編譯型 JAVA是半編譯型 PHP是解釋型” 也就是說(shuō)PHP是先動(dòng)態(tài)解析再代碼運(yùn)行的 所以從這個(gè)角度來(lái)看 PHP性能必然很差
的確 從PHP腳本運(yùn)行來(lái)輸出 的確是一個(gè)動(dòng)態(tài)解析再代碼運(yùn)行的過(guò)程 具體來(lái)說(shuō) PHP腳本的運(yùn)行機(jī)制如下圖所示
圖二 PHP運(yùn)行機(jī)制
PHP的運(yùn)行階段也分成三個(gè)階段
Parse 語(yǔ)法分析階段
Compile 編譯產(chǎn)出opcode中間碼
Execute 運(yùn)行 動(dòng)態(tài)運(yùn)行進(jìn)行輸出
所以說(shuō) 在PHP內(nèi)部 本身也是存在編譯的過(guò)程 并且據(jù)此產(chǎn)生了大量的opcode cache工具 比如說(shuō)apc eacc xcache等等 這些opcode cache在生產(chǎn)環(huán)境基本上在標(biāo)配 基于opcode cache 能到做到“PHP腳本編譯一次 多次運(yùn)行”的效果 從這點(diǎn)上 PHP就和JAVA的半編譯機(jī)制非常類(lèi)似
所以 從運(yùn)行機(jī)制上來(lái)看 PHP的運(yùn)行模式和JAVA是非常類(lèi)似的 都是先產(chǎn)生中間碼 然后運(yùn)行在不同虛擬機(jī)上
動(dòng)態(tài)運(yùn)行
從上面的幾個(gè)分析來(lái)看 PHP在內(nèi)存管理 變量 函數(shù) 運(yùn)行機(jī)制等幾個(gè)方面都做了大量的工作 所以從原理來(lái)看 PHP 不應(yīng)該存在性能問(wèn)題 性能至少也應(yīng)該和Java 比較接近
這個(gè)時(shí)候就不得不談PHP動(dòng)態(tài)語(yǔ)言的特性所帶來(lái)的性能問(wèn)題了 由于PHP是動(dòng)態(tài)運(yùn)行時(shí) 所以所有的變量 函數(shù) 對(duì)象調(diào)用 作用域?qū)崿F(xiàn)等等都是在 執(zhí)行階段中才確定的 這個(gè)從根本上決定了PHP性能中很難改變的一些東西 在C/C++等能夠在靜態(tài)編譯階段確定的變量 函數(shù) 在PHP中需要在動(dòng)態(tài)運(yùn)行 中確定 也就決定了PHP中間碼不能直接運(yùn)行而需要運(yùn)行在Zend Engine上
說(shuō)到PHP變量的具體實(shí)現(xiàn) 又不得不說(shuō)一個(gè)東西了 Hashtable Hashtable可以說(shuō)在PHP靈魂之一 在PHP內(nèi)部廣泛用到 包含變量符號(hào)棧 函數(shù)符號(hào)棧等等都是基于hashtable的
以PHP變量為例來(lái)說(shuō)明下PHP的動(dòng)態(tài)運(yùn)行特點(diǎn) 比如說(shuō)代碼
?php
$var?=?“hello ?blog xiuwz ”;
?
該代碼的執(zhí)行結(jié)果就是在變量符號(hào)棧(是一個(gè)hashtable)中新增一個(gè)項(xiàng)
當(dāng)要使用到該變量時(shí)候 就去變量符合棧中去查找(也就是變量調(diào)用對(duì)出了一個(gè)hash查找的過(guò)程)
同樣對(duì)于函數(shù)調(diào)用也基本上類(lèi)似有一個(gè)函數(shù)符號(hào)棧(hashtable)
其實(shí)關(guān)于動(dòng)態(tài)運(yùn)行的變量查找特點(diǎn) 在PHP的運(yùn)行機(jī)制中也能看出一些 PHP代碼通過(guò)解釋 編譯后的流程下圖
圖 PHP運(yùn)行實(shí)例
從上圖可以看出 PHP代碼在pile之后 產(chǎn)出的了類(lèi)符號(hào)表 函數(shù)符號(hào)表 和OPCODE 在真正執(zhí)行的時(shí)候 zend Engine會(huì)根據(jù)op code去對(duì)應(yīng)的符號(hào)表中進(jìn)行查找 處理
從某種程度上 在這種問(wèn)題的上 很難找到解決方案 因?yàn)檫@是由于PHP語(yǔ)言的動(dòng)態(tài)特性所決定的 但是在國(guó)內(nèi)外也有不少的人在尋找解決方案 因?yàn)?通過(guò)這樣 能夠從根本上完全的優(yōu)化PHP 典型的列子有facebook的hiphop
結(jié)論
從上面分析來(lái)看 在基礎(chǔ)的內(nèi)存管理 變量 函數(shù) 運(yùn)行機(jī)制方面 PHP本身并不會(huì)存在明顯的性能差異 但由于PHP的動(dòng)態(tài)運(yùn)行特性 決定了 PHP和其他的編譯型語(yǔ)言相比 所有的變量查找 函數(shù)運(yùn)行等等都會(huì)多一些hash查找的CPU開(kāi)銷(xiāo)和額外的內(nèi)存開(kāi)銷(xiāo) 至于這種開(kāi)銷(xiāo)具體有多大 可以通過(guò)后 續(xù)的基準(zhǔn)性能和對(duì)比分析得出
因此 也可以大體看出PHP不太適合的一些場(chǎng)景 大量計(jì)算性任務(wù) 大數(shù)據(jù)量的運(yùn)算 內(nèi)存要求很?chē)?yán)格的應(yīng)用場(chǎng)景 如果要實(shí)現(xiàn)這些功能 也建議通過(guò)擴(kuò)展的方式實(shí)現(xiàn) 然后再提供鉤子函數(shù)給PHP調(diào)用 這樣可以減低內(nèi)部計(jì)算的變量 函數(shù)等系列開(kāi)銷(xiāo)
基準(zhǔn)性能
對(duì)于PHP基準(zhǔn)性能 目前缺少標(biāo)準(zhǔn)的數(shù)據(jù) 大多數(shù)同學(xué)都存在感性的認(rèn)識(shí) 有人認(rèn)為 QPS就是PHP的極限了 此外 對(duì)于框架的性能和框架對(duì)性能的影響很沒(méi)有響應(yīng)的權(quán)威數(shù)字
本章節(jié)的目的是給出一個(gè)基準(zhǔn)的參考性能指標(biāo) 通過(guò)數(shù)據(jù)給大家一個(gè)直觀的了解
具體的基準(zhǔn)性能有以下幾個(gè)方面
裸PHP性能 完成基本的功能
裸框架的性能 只做最簡(jiǎn)單的路由分發(fā) 只走通核心功能
標(biāo)準(zhǔn)模塊的基準(zhǔn)性能 所謂標(biāo)準(zhǔn)模塊的基準(zhǔn)性能 是指一個(gè)具有完整服務(wù)模塊功能的基準(zhǔn)性能
環(huán)境說(shuō)明
測(cè)試環(huán)境
Uname aPnux db forum test db baidu _ # SMP Wed Aug : : CST x _ x _ x _ GNU/Pnux
Red Hat Enterprise Pnux AS release (Nahant Update )
Intel(R) Xeon(R) CPU?????????? E ? @ GHz
軟件相關(guān)
Nginx nginx version: nginx/ ? built by gcc (Red Hat )
Php (采用php fpm)
PHP (cP) (built: Mar? : : )
Copyright (c) The PHP Group
Zend Engine v Copyright (c) Zend Technologies
with eAccelerator v Copyright (c) eAccelerator by eAccelerator
bingo
PHP框架
其他說(shuō)明
目標(biāo)機(jī)器的部署方式 nginx php fpm php腳本
測(cè)試壓力機(jī)器和目標(biāo)機(jī)器獨(dú)立部署
裸PHP性能
最簡(jiǎn)單的PHP腳本
?php
require_once?‘ /actions/indexAction php’;
$objAction?=?new?indexAction();
$objAction init();
$objAction execute();
?
Acitons/indexAction php里面的代碼如下
?php
class?indexAction
{
pubPc?function?execute()
{
echo?‘hello ?world!’;
}
}
?
通過(guò)壓力工具測(cè)試結(jié)果如下
裸PHP框架性能
為了和 的對(duì)比 基于bingo 框架實(shí)現(xiàn)了類(lèi)似的功能 代碼如下
?php
require_once?‘Bingo/Controller/Front php’;
$objFrontController?=?Bingo_Controller_Front::getInstance(array(
‘a(chǎn)ctionDir’?=?‘ /actions’
));
$objFrontController dispatch();
壓力測(cè)試結(jié)果如下
從該測(cè)試結(jié)果可以看出 框架雖然有一定的消耗 但對(duì)整體的性能來(lái)說(shuō)影響是非常小的
標(biāo)準(zhǔn)PHP模塊的基準(zhǔn)性能
所謂標(biāo)準(zhǔn)PHP模塊 是指一個(gè)PHP模塊所必須要具體的基本功能
路由分發(fā)
自動(dòng)加載
LOG初始化Notice日志打印 所以的UI請(qǐng)求都一條標(biāo)準(zhǔn)的日志
錯(cuò)誤處理
時(shí)間校正
自動(dòng)計(jì)算每個(gè)階段耗時(shí)開(kāi)銷(xiāo)
編碼識(shí)別編碼轉(zhuǎn)化
標(biāo)準(zhǔn)配置文件的解析和調(diào)用
采用bingo 的代碼自動(dòng)生成工具產(chǎn)生標(biāo)準(zhǔn)的測(cè)試PHP模塊 test
測(cè)試結(jié)果如下
結(jié)論
從測(cè)試數(shù)據(jù)的結(jié)論來(lái)看 PHP本身的性能還是可以的 基準(zhǔn)性能完全能夠達(dá)到幾千甚至上W的QPS 至于為什么在大多數(shù)的PHP模塊中表現(xiàn)不佳 其實(shí)這個(gè)時(shí)候更應(yīng)該去找出系統(tǒng)的瓶頸點(diǎn) 而是簡(jiǎn)單的說(shuō)OK PHP不行 那我們換C來(lái)搞吧 (下一個(gè)章節(jié) 會(huì)通過(guò)一些例子來(lái)對(duì)比 采用C來(lái)處理不見(jiàn)得有特 別的優(yōu)勢(shì))
通過(guò)基準(zhǔn)數(shù)據(jù) 可以得出以下幾個(gè)具體的結(jié)論
PHP本身性能也很不錯(cuò) 簡(jiǎn)單功能下能夠達(dá)到 QPS 極限也能過(guò)W
PHP框架本身對(duì)性能影響非常有限 尤其是在有一定業(yè)務(wù)邏輯和數(shù)據(jù)交互的情況下 幾乎可以忽略
一個(gè)標(biāo)準(zhǔn)的PHP模塊 基準(zhǔn)性能能夠達(dá)到 QPS( cpu idle)
對(duì)比分析
lishixinzhi/Article/program/PHP/201311/21287
大數(shù)據(jù)的話可以進(jìn)行以下操作:
減少對(duì)數(shù)據(jù)庫(kù)的讀取,也就是減少調(diào)用數(shù)據(jù)庫(kù),
進(jìn)行數(shù)據(jù)緩存,
利用數(shù)據(jù)庫(kù)的自身優(yōu)化技術(shù),如索引等
精確查詢條件,有利于提高查找速度
其實(shí)沒(méi)有什么特別的優(yōu)勢(shì)(尤其是對(duì)C#),不過(guò)PHP通常面向中小型應(yīng)用,對(duì)于大數(shù)據(jù)并沒(méi)有太多成熟的框架可以借鑒。
另外編譯運(yùn)行的代碼在復(fù)雜應(yīng)用上比php確實(shí)是要有優(yōu)勢(shì)的,包括執(zhí)行速度、穩(wěn)定性、并發(fā)控制等。
但是歸根結(jié)底大數(shù)據(jù)方面最大的差距來(lái)自于數(shù)據(jù)庫(kù),如果java + mysql,你搞死了也不會(huì)比 php + oracle快。
所以我的看法是對(duì)于大數(shù)據(jù),開(kāi)發(fā)語(yǔ)言不是很重要,畢竟核心是數(shù)據(jù),數(shù)據(jù)庫(kù)的能力與設(shè)計(jì)才是影響性能的最重點(diǎn)。
使用緩存,比如memcache,redis,因?yàn)樗鼈兪窃趦?nèi)存中運(yùn)行,所以處理數(shù)據(jù),返回?cái)?shù)據(jù)非???,所以可以應(yīng)對(duì)高并發(fā)。
2.增加帶寬和機(jī)器性能,1M的帶寬同時(shí)處理的流量肯定有限,所以在資源允許的情況下,大帶寬,多核cpu,高內(nèi)存是一個(gè)解決方案。
3.分布式,讓多個(gè)訪問(wèn)分到不同的機(jī)器上去處理,每個(gè)機(jī)器處理的請(qǐng)求就相對(duì)減少了。
簡(jiǎn)單說(shuō)些常用技術(shù),負(fù)載均衡,限流,加速器等
本文名稱:php在大數(shù)據(jù)中的表現(xiàn) php大數(shù)據(jù)分析
URL標(biāo)題:http://www.sd-ha.com/article20/docjjco.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營(yíng)銷(xiāo)推廣、定制開(kāi)發(fā)、自適應(yīng)網(wǎng)站、建站公司、手機(jī)網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)