編譯指令的代價是什麼
❶ 高層語言與底層語言的區別
一般來講高級語言和低級語言有一下特點:
高級語言:實現效率高,執行效率低,對硬體的可控性弱,目標代碼大,可維護性好,可移植性好
低級語言:實現效率低,執行效率高,對硬體的可控性強,目標代碼小,可維護性差,可移植性差
我們都知道CPU運行的是二進制指令,所有的語言編寫的程序最終都要翻譯成二進制代碼,但是為什麼實現會有以上眾多差異呢?下面以c語言為高級語言代表,匯編語言為低級語言代表來解釋一下。
越低級的語言,形式上越接近機器指令,匯編語言就是與機器指令一一對應的。而越高級的語言,一條語句對應的指令數越多,其中原因就是高級語言對底層操作進行了抽象和封裝,使編寫程序的過程更符合人類的思維習慣,並且極大了簡化了人力勞動。也就是說你用高級語言寫一句,會被轉換成許多底層操作,大部分的工作交給了負責轉換的機器(即編譯器),從而人力得到了解放。因為機器就是用來為人類提供便利的,所以說高級語言的出現是計算機發展的必然結果。
下面重點解釋為何低級語言的執行效率更高:
1.低級語言可以通過控制硬體訪問來優化效率
越低級的語言月接近底層,即控制硬體訪問的能力越強,對硬體資源的利用效率越高。比如說匯編語言能夠訪問寄存器,而C語言就做不到。通過對寄存器等硬體的訪問,我們可以將程序的運行效率優化到最大,而像C這樣的高級語言用的最多的是堆棧這樣的內存結構,訪問速度自然不如寄存器了。
2.高級語言程序存在工作冗餘,有效率損失。
各種語言需要通過編譯器翻譯成機器碼,不管編譯多麼智能和強大,都是會產生冗餘。這里的冗餘不是指指令的多少,而是有沒有做沒有必要的事情。 產生冗餘的多少關鍵要看語言跟機器指令之間的耦合度。耦合度越大,編譯器翻譯過程越簡單,產生的冗餘越少。對應匯編來書,由於與機器碼一一對應,所以翻譯後基本沒有冗餘。而高級語言由於進行了抽象和封裝,所以與機器指令間的耦合度較低,因此整個翻譯過程較復雜,因此在高級語言在具體化的過程中不可避免會產生較多的冗餘。據說C語言有10%的效率損失。
3.效率高不高,還取決於程序員水平。
一個差的程序員用匯編寫程序,可能存在很多沒有用的操作,而程序高手用c語言寫,可以將程序優化到最大。最終的結果可能是匯編的程序跑不過C語言程序。
總之,完成一項工作的工作量是不變的,機器做的 多了,人就做的就少了,同時人對程序的很多細節的控制性也減弱了。各種語言都是在這個平衡點附近糾結。從C/C++的注重機器運算效率的優化,到C#/JAVA注重開發效率的優化。人操作起來更加方便了,更高效了,代價就是,機器要處理的東西更多,運算效率被進一步壓縮。但是這個壓縮在許可范圍內,那麼這也是一種進步。
❷ 突破封鎖!國產晶元終於有了自己的指令集
在半導體晶元領域, 指令系統是一切軟硬體生態的起點 。
以大家最熟悉的ARM和X86為例,它們就分別隸屬於RISC精簡指令集和CISC復雜指令集。
隨著物聯網、5G、AI新興領域的興起,RISC-V和MIPS兩大精簡指令集架構也頻繁出現在我們的視野內。
所謂晶元,其實都是由半導體堆出來的硬體電路,晶體管越多往往代表性能和功能越強。但無論是超級計算機還是智能手環, 它們搭載的處理器都只能識別二進制數據 。
想讓這些晶元正常運行,處理復雜的應用場景,首先就要教會它們學會類似九九乘法表的「演算法口訣」和「數學公式」, 而這些演算法口御橋早訣/公式其實就是所謂的「指令集」 。
換句話說, 指令集的功能和效率(演算法口訣/公式的類型),在很大程度上就決定了各類晶元的成就和算力的上限 。
雖然海思麒麟、龍芯、兆芯、海光、紫光、澎湃等國產晶元都在各自領域取得了不俗的成績,但無論是它們,還是其他採用X86、ARM、MIPS、RISC-V、Alpha和Power,選擇封閉、授權還是開源的國產晶元項目,其底層的指令集根基都掌握在別人手裡。
因此, 只有從指令系統的根源上實現自主,才能打破軟體生態發展受制於人的枷鎖 。
好消息是,日前龍芯中科就正式發布了自主指令系統架構「Loongson Architecture」,簡稱為「龍芯架構」或者「LoongArch」。它包括基礎架構部分,以及向量擴展LSX、高級向量擴展LASX、虛擬化LVZ、二進制翻譯LBT等擴展部分,總共接近2000條指令。同時不包含龍芯此前使用的MIPS指令系統, 並具有完全自主、技術先進、兼容生態三個方面的特點 。
目前,採用LoongArch的龍芯3A5000處理器晶元已經流片成功,完整操作系統也已穩定運行,它能對多種國際主流指令系統的高效二進制翻譯鏈,並成功演示了運行基於其它主流指令系統的復雜應用程序。
LoongArch對MIPS指令的翻譯效率是100%性能,對ARM指令翻譯的效率是90%性能,對x86的翻譯效率是80%性能。
此外,龍芯中科還在聯合產業鏈夥伴在適當的時間建立開放指令系統聯盟,在聯盟成員內免費共享LoongArch及有關龍芯IP核。
所謂IP核,我們可以理解為ARM旗下的Cortex-A78和Cortex-A55等,後置都是基於ARMv8指令集打造的核心IP架構,並授權給了高通、三星、聯發科等晶元商開發SoC移動平台。
目前,ARM剛剛發布了ARMv9指令集,如果不出意外將在下半年發布的Cortex-A79和Cortex-X2架構就將採用這套指令集。
近10年來32位手機處理器都是基於ARMv7指令集打造,在A75之前的處理器則是基於ARMv8-A設計,隨後都是ARMv8.2-A一統江湖
ARM指令集可以細分為Cortex-A(ARMv-A)、Cortex-R(ARMv-R)和Cortex-M(ARMv-M),分別適用於不同類型的晶元
比如車載晶元使用的就是Cortex-R(ARMv-R)核心IP
總之, 設計出一個純國產的自主指令集只是萬里長征的第一步 ,關鍵是後續要做出懂這個指令集的CPU(已經有了龍芯3A5000),再往後還需要鎮雀讓和人類交互的「翻譯家」——編譯器懂這個指令集。也就是需要不斷完善軟硬體生態,讓我們熟悉的系統、辦公、 娛樂 和 游戲 程序都能運行在這套指令集打造的晶元之上。如果做不到這一步,國產指令集和相關晶元也只是空中樓閣而已、
作為國人,我們真心希望LoongArch這種國產指令集可以取得成功,今後無論手機、電腦、車載還是其消沖他半導體晶元都能以使用國產指令集為榮,並走向世界。
擴展小知識
那麼,指令集又是如何影響晶元執行效率的?
我們以RISC和CISC,讓它們分別執行「清潔地面」的命令為例,看看其背後的指令邏輯差異。
邏輯上,「清潔地面」的大概思路是先拿起掃帚,掃地;拿起簸箕,用掃帚把垃圾掃進簸箕;放下掃帚和簸箕,潤濕墩布;再用墩布擦地,直至清潔地面完成。
對CISC復雜指令集而言,很容易理解「清潔地面」這套邏輯,下達「清潔地面」命令後,就能按照規則和順序,一步步自動完成。
對於RISC精簡指令集而言,它一下子可理解不了如此復雜的邏輯,必須將復雜的邏輯順序拆分,然後按照一項項簡單的命令去完成復雜的操作。
比如,想讓RISC精簡指令集完成「清潔地面」命令,就必須依次下達「拿起掃帚」、「掃地」、「拿起簸箕」、「把垃圾掃進簸箕」、「放下掃帚和簸箕」、「潤濕墩布」、「墩地」……
看起來CISC復雜指令集方便又強大?沒錯,如果要同時清潔無數房間地面,你只要對著不同的房屋說「清潔地面」、「清潔地面」、「清潔地面」……即可。
而對RISC精簡指令集,你需要對著每個房間都重復一整套復雜的命令,如果下達指令的人嘴巴不夠快(帶寬不夠大),那清潔地面的效率自然受到影響,難以和CISC復雜指令集抗衡。
但是, 現實生活中,並非所有房間的地面都需要一整套的清潔流程,比如你只需要墩地一個步驟。
對RISC精簡指令集而言,你只需對著需要清潔的房間說「墩地」、「墩地」、「墩地」即可。而由於CISC復雜指令集沒有單獨的「墩地」動作,操作起來就要麻煩許多,完成相同的墩地操作會消耗更多資源,翻譯過來就是發熱更高更費電。
這就是RISC和CISC的本質區別。 說不上誰好誰壞,只能說它們所擅長的領域各不相同。
以ARM架構為代表的RISC精簡指令集,最適合針對常用的命令進行優化,賦予它更簡潔和高效的執行環境,對不常用的功能則通過各種精簡指令組合起來完成。
RISC是將復雜度交給了編譯器,犧牲了程序大小和指令帶寬,從而換取了簡單和低功耗的硬體實現。
對以X86架構為代表的CISC復雜指令集,則適合更加復雜的應用環境。
CISC是以增加處理器本身復雜度作為代價,以犧牲功耗為代價去換取更高的性能。不過,X86架構則可通過對新型指令集的支持(如SSE4.1、AVX-512等),在一定程度上提高指定任務的執行效率和降低功耗。
現在晶元領域是RISC攻,CISC守的格局。以蘋果M1為代表的ARM架構RISC指令集晶元正在染指傳統的X86 PC市場,而且大概率會取得成功。雖然以英特爾為代表的X86陣營曾多次試圖反擊Android生態(如早期的Atom晶元),但最終卻都以失敗告終。ARM最新發布的ARMv9指令集,就給了ARM晶元入侵X86 PC大本營更多彈葯,也許用不了多久Windows ARM版PC也將成為一個更加重要的PC品類。
❸ ARM X86 CISC RISC
參考 ARM X86 區別
RISC的英文全稱是「reced instruction set computer」,即「精簡指令集計算機」;
CISC的英文全稱為「Complex Instruction Set Computer」,即「復雜指令系統計算機」。
X86是復雜指令集(CISC)的代表,而ARM(Advanced RISC Machine——高級RISC機)則是精簡指令集(RISC)的代表。
關於X86架構和ARM架構這兩者誰將統一市場的爭執一直都有,但是也有人說這兩者根本不具備可比性,X86無法做到ARM的功耗,而ARM也無法做到X86的性能。
我們要明白CPU是一個執行部件,它之所以能執行,也是因為人們在裡面製作了執行各種功能的硬體電路,然後再用一定的邏輯讓它按照一定的順序工作,這樣就能完成人們給它的任務。也就是說,如果把CPU看作一個人,首先它要有正常的工作能力(既執行能力),然後又有足夠的邏輯能力(能明白做事的順序),最後還要聽的懂別人的話(既指令集),才能正常工作。而這些集中在一起就構成了所謂的「架構」,它可以理解為一套「工具」、「方法」和「規范」的集合。不同的架構之間,工具可能不同,方法可能不同,規范也可能不同,這也造成了它們之間的不兼容——你給一個義大利泥瓦匠看一份中文寫成的烹飪指南,他當然不知道應該干什麼了。
從CPU發明到現在,有非常多種架構,從我們熟悉的X86,ARM,到不太熟悉的MIPS,IA64,它們之間的差距都非常大。但是如果從最基本的邏輯角度來分類的話,它們可以被分為兩大類,即所謂的「復雜指令集」與「精簡指令集」系統,也就是經常看到的「CISC」與「RISC」。屬於這兩種類中的各種架構之間最大的區別,在於它們的設計者考慮問題方式的不同。
我們可以繼續舉個例子,比如說我們要命令一個人吃飯,那麼我們應該怎麼命令呢?我們可以直接對他下達「吃飯」的命令,也可以命令他「先拿勺子,然後舀起一勺飯,然後張嘴,然後送到嘴裡,最後咽下去」。從這里可以看到,對於命令別人做事這樣一件事情,不同的人有不同的理解,有人認為,如果我首先給接受命令的人以足夠的訓練,讓他掌握各種復雜技能(即在硬體中實現對應的復雜功能),那麼以後就可以用非常簡單的命令讓他去做很復雜的事情——比如只要說一句「吃飯」,他就會吃飯。但是也有人認為這樣會讓事情變的太復雜,畢竟接受命令的人要做的事情很復雜,如果你這時候想讓他吃菜怎麼辦?難道繼續訓練他吃菜的方法?我們為什麼不可以把事情分為許多非常基本的步驟,這樣只需要接受命令的人懂得很少的基本技能,就可以完成同樣的工作,無非是下達命令的人稍微累一點——比如現在我要他吃菜,只需要把剛剛吃飯命令里的「舀起一勺飯」改成「舀起一勺菜」,問題就解決了,多麼簡單。
這就是「復雜指令集」和「精簡指令集」的邏輯區別。可能有人說,明顯是精簡指令集好啊,但是我們不好去判斷它們之間到底誰好誰壞,因為目前他們兩種指令集都在蓬勃發展,而且都很成功——X86是復雜指令集(CISC)的代表,而ARM則是精簡指令集(RISC)的代表,甚至ARM的名字就直接表明了它的技術:Advanced RISC Machine——高級RISC機。
到了這里你就應該明白為什麼RISC和CISC之間不好直接比較性能了,因為它們之間的設計思路差異太大。這樣的思路導致了CISC和RISC分道揚鑣——前者更加專注於高性能但同時高功耗的實現,而後者則專注於小尺寸低功耗領域。實際上也有很多事情CISC更加合適,而另外一些事情則是RISC更加合適,比如在執行高密度的運算任務的時候CISC就更具備優勢,而在執行簡單重復勞動的時候RISC就能佔到上風,比如假設我們是在舉辦吃飯大賽,那麼CISC只需要不停的喊「吃飯吃飯吃飯」就行了,而RISC則要一遍一遍重復吃飯流程,負責喊話的人如果嘴巴不夠快(即內存帶寬不夠大),那麼RISC就很難吃的過CISC。但是如果我們只是要兩個人把飯舀出來,那麼CISC就麻煩得多,因為CISC里沒有這么簡單的舀飯動作,而RISC就只需要不停喊「舀飯舀飯舀飯」就OK。
這就是CISC和RISC之間的區別。但是在實際情況中問題要比這復雜許許多多,因為各個陣營的設計者都想要提升自家架構的性能。這裡面最普遍的就是所謂的「發射」概念。什麼叫發射?發射就是同時可以執行多少指令的意思,例如雙發射就意味著CPU可以同時拾取兩條指令,三發射則自然就是三條了。現代高級處理器已經很少有單發射的實現,例如Cortex A8和A9都是雙發射的RISC,而Cortex A15則是三發射。ATOM是雙發射CISC,Core系列甚至做到了四發射——這個方面大家倒是不相上下,但是不要忘了CISC的指令更加復雜,也就意味著指令更加強大,還是吃飯的例子,CISC只需要1個指令,而RISC需要5個,那麼在內存帶寬相同的情況下,CISC能達到的性能是要超過RISC的(就吃飯而言是5倍),而實際中CISC的Core i處理器內存帶寬已經超過了100GB/s,而ARM還在為10GB/s而苦苦奮斗,一個更加吃帶寬的架構,帶寬卻只有別人的十分之一,性能自然會受到非常大的制約。為什麼說ARM和X86不好比,這也是很重要的一個原因,因為不同的應用對帶寬需求是不同的。一旦遇到帶寬瓶頸,哪怕ARM處理器已經達到了很高的運算性能,實際上根本發揮不出來,自然也就會落敗了。
說到這兒大家應該也已經明白CISC和RISC的區別和特色了。簡而言之,CISC實際上是以增加處理器本身復雜度作為代價,去換取更高的性能,而RISC則是將復雜度交給了編譯器,犧牲了程序大小和指令帶寬,換取了簡單和低功耗的硬體實現。但如果事情就這樣發展下去,為了提升性能,CISC的處理器將越來越大,而RISC需要的內存帶寬則會突破天際,這都是受到技術限制的。所以進十多年來,關於CISC和RISC的區分已經慢慢的在模糊,例如自P6體系(即Pentium Pro)以來,作為CISC代表的X86架構引入了微碼概念,與此對應的,處理器內部也增加了所謂的解碼器,負責將傳統的CISC指令「拆包」為更加短小的微碼(uOPs)。一條CISC指令進來以後,會被解碼器拆分為數量不等的微碼,然後送入處理器的執行管線——這實際上可以理解為RISC內核+CISC解碼器。而RISC也引入了指令集這個就邏輯角度而言非常不精簡的東西,來增加運算性能。正常而言,一條X86指令會被拆解為2~4個uOPs,平均來看就是3個,因此同樣的指令密度下,目前X86的實際指令執行能力應該大約是ARM的3倍左右。不過不要忘了這是基於「同樣指令密度」下的一個假設,實際上X86可以達到的指令密度是十倍甚至百倍於ARM的。
最後一個需要考慮的地方就是指令集。這個東西的引入,是為了加速處理器在某些特定應用上性能而設計的,已經有了幾十年的歷史了。而實際上在目前的應用環境內,起到決定作用的很多時候是指令集而不是CPU核心。X86架構的強大,很多時候也源於指令集的強大,比如我們知道的ATOM,雖然它的X86核心非常羸弱,但是由於它支持SSE3,在很多時候性能甚至可以超過核心性能遠遠強大於它的Pentium M,這就是指令集的威力。目前X86指令集已經從MMX,發展到了SSE,AVX,而ARM依然還只有簡單而基礎的NEON。它們之間不成比例的差距造成了實際應用中成百上千倍的性能落差,例如即便是現今最強大的ARM內核依然還在為軟解1080p H.264而奮斗,但一顆普通的中端Core i處理器卻可以用接近十倍播放速度的速度去壓縮1080p H.264視頻。至少在這點上,說PC處理器的性能百倍於ARM是無可辯駁的,而實際中這樣的例子比比皆是。這也是為什麼我在之前說平均下來ARM只有X86幾十分之一的性能的原因。
打了這么多字,其實就是為了說明一點,雖然現在ARM很強大,但它距離X86還是非常遙遠,並沒有因為這幾年的進步而縮短,實際上反而在被更快的拉大。畢竟它們設計的出發點不一樣,因此根本不具備多少可比性,X86無法做到ARM的功耗,而ARM也無法做到X86的性能。這也是為什麼ATOM一直以來都不成功的原因所在——Intel試圖用自己的短處去和別人的長處對抗,結果自然是不太好的,要不是Intel擁有這個星球上最先進的半導體工藝,ATOM根本都不可能出現。而ARM如果嘗試去和X86拼性能,那結果自然也好不到哪兒去,原因剛剛也解釋過了。不過這也不意味著ARM以後就只能占據低端,畢竟任何架構都有其優點,一旦有應用針對其進行優化,那麼就可以揚長避短。X86的繁榮也正是因為整個世界的資源都針對它進行了優化所致。只要能為ARM找到合適的應用與適合的領域,未來ARM也未必不可以進入更高的層次。
❹ 根據你的經驗談談寫php程序需要注意哪些問題
1、如果能將類的方法定義成static,就盡量定義成static,它的速度會提升將近4倍。
2、$row[』id』] 的速度是$row[id]的7倍。
3、echo 比 print 快,並且使用echo的多重參數(譯註:指用逗號而不是句點)代替字元串連接,比如echo $str1,$str2。
4、在執行for循環之前確定最大循環數,不要每循環一次都計算最大值,最好運用foreach代替。
5、注銷那些不用的變數尤其是大數組,以便釋放內存。
6、盡量避免使用__get,__set,__autoload。
7、require_once()代價昂貴。
8、include文件時盡量使用絕對路徑,因為它避免了PHP去include_path里查找文件的速度,解析操作系統路徑所需的時間會更少。
9、如果你想知道腳本開始執行(譯註:即伺服器端收到客戶端請求)的時刻,使用$_SERVER[『REQUEST_TIME』]要好於time()。
10、函數代替正則表達式完成相同功能。
11、str_replace函數比preg_replace函數快,但strtr函數的效率是str_replace函數的四倍。
12、如果一個字元串替換函數,可接受數組或字元作為參數,並且參數長度不太長,那麼可以考慮額外寫一段替換代碼,使得每次傳遞參數是一個字元,而不是只寫一行代碼接受數組作為查詢和替換的參數。
13、使用選擇分支語句(譯註:即switch case)好於使用多個if,else if語句。
14、用@屏蔽錯誤消息的做法非常低效,極其低效。
15、打開apache的mod_deflate模塊,可以提高網頁的瀏覽速度。
16、資料庫連接當使用完畢時應關掉,不要用長連接。
17、錯誤消息代價昂貴。
18、在方法中遞增局部變數,速度是最快的。幾乎與在函數中調用局部變數的速度相當。
19、遞增一個全局變數要比遞增一個局部變數慢2倍。
20、遞增一個對象屬性(如:$this->prop++)要比遞增一個局部變數慢3倍。
21、遞增一個未預定義的局部變數要比遞增一個預定義的局部變數慢9至10倍。
22、僅定義一個局部變數而沒在函數中調用它,同樣會減慢速度(其程度相當於遞增一個局部變數)。PHP大概會檢查看是否存在全局變數。
23、方法調用看來與類中定義的方法的數量無關,因為我(在測試方法之前和之後都)添加了10個方法,但性能上沒有變化。
24、派生類中的方法運行起來要快於在基類中定義的同樣的方法。
25、調用帶有一個參數的空函數,其花費的時間相當於執行7至8次的局部變數遞增操作。類似的方法調用所花費的時間接近於15次的局部變數遞增操作。
26、Apache解析一個PHP腳本的時間要比解析一個靜態HTML頁面慢2至10倍。盡量多用靜態HTML頁面,少用腳本。
27、除非腳本可以緩存,否則每次調用時都會重新編譯一次。引入一套PHP緩存機制通常可以提升25%至100%的性能,以免除編譯開銷。
28、盡量做緩存,可使用memcached。memcached是一款高性能的內存對象緩存系統,可用來加速動態Web應用程序,減輕資料庫負載。對運算碼 (OP code)的緩存很有用,使得腳本不必為每個請求做重新編譯。
29、當操作字元串並需要檢驗其長度是否滿足某種要求時,你想當然地會使用strlen()函數。此函數執行起來相當快,因為它不做任何計算,只返回在zval 結構(C的內置數據結構,用於存儲PHP變數)中存儲的已知字元串長度。但是,由於strlen()是函數,多多少少會有些慢,因為函數調用會經過諸多步驟,如字母小寫化(譯註:指函數名小寫化,PHP不區分函數名大小寫)、哈希查找,會跟隨被調用的函數一起執行。在某些情況下,你可以使用isset() 技巧加速執行你的代碼。
(舉例如下)
if (strlen($foo) < 5) { echo 「Foo is too short」$$ }
(與下面的技巧做比較)
if (!isset($foo{5})) { echo 「Foo is too short」$$ }
調用isset()恰巧比strlen()快,因為與後者不同的是,isset()作為一種語言結構,意味著它的執行不需要函數查找和字母小寫化。也就是說,實際上在檢驗字元串長度的頂層代碼中你沒有花太多開銷。
34、當執行變數$i的遞增或遞減時,$i++會比++$i慢一些。這種差異是PHP特有的,並不適用於其他語言,所以請不要修改你的C或Java代碼並指望它們能立即變快,沒用的。++$i更快是因為它只需要3條指令(opcodes),$i++則需要4條指令。後置遞增實際上會產生一個臨時變數,這個臨時變數隨後被遞增。而前置遞增直接在原值上遞增。這是最優化處理的一種,正如Zend的PHP優化器所作的那樣。牢記這個優化處理不失為一個好主意,因為並不是所有的指令優化器都會做同樣的優化處理,並且存在大量沒有裝配指令優化器的互聯網服務提供商(ISPs)和伺服器。
35、並不是事必面向對象(OOP),面向對象往往開銷很大,每個方法和對象調用都會消耗很多內存。
36、並非要用類實現所有的數據結構,數組也很有用。
37、不要把方法細分得過多,仔細想想你真正打算重用的是哪些代碼?
38、當你需要時,你總能把代碼分解成方法。
39、盡量採用大量的PHP內置函數。
40、如果在代碼中存在大量耗時的函數,你可以考慮用C擴展的方式實現它們。
41、評估檢驗(profile)你的代碼。檢驗器會告訴你,代碼的哪些部分消耗了多少時間。Xdebug調試器包含了檢驗程序,評估檢驗總體上可以顯示出代碼的瓶頸。
42、mod_zip可作為Apache模塊,用來即時壓縮你的數據,並可讓數據傳輸量降低80%。
43、在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情況下,盡量用file_get_contents,因為他的效率高得多!但是要注意file_get_contents在打開一個URL文件時候的PHP版本問題;
44、盡量的少進行文件操作,雖然PHP的文件操作效率也不低的;
45、優化Select SQL語句,在可能的情況下盡量少的進行Insert、Update操作(在update上,我被惡批過);
46、盡可能的使用PHP內部函數(但是我卻為了找個PHP裡面不存在的函數,浪費了本可以寫出一個自定義函數的時間,經驗問題啊!);
47、循環內部不要聲明變數,尤其是大變數:對象(這好像不只是PHP裡面要注意的問題吧?);
48、多維數組盡量不要循環嵌套賦值;
49、在可以用PHP內部字元串操作函數的情況下,不要用正則表達式;
50、foreach效率更高,盡量用foreach代替while和for循環;
51、用單引號替代雙引號引用字元串;
52、「用i+=1代替i=i+1。符合c/c++的習慣,效率還高」;
53、對global變數,應該用完就unset()掉;
具體參考 http://jingyan..com/article/a948d651432cb30a2dcd2ef8.html
❺ 關於C語言預處理命令
C程序的源代碼中可包括各種編譯指令,這些指令稱為預處理命令。雖然它們實際上不是C語言的一部分,但卻擴展了C程序設計的環境。本節將介紹如何應用預處理程序和注釋簡化程序開發過程,並提高程序的可讀性。ANSI標準定義的C語言預處理程序包括下列命令:
#define,#error,#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。非常明顯,所有預處理命令均以符號#開頭,下面分別加以介紹。
一 #define
命令#define定義了一個標識符及一個串。在源程序中每次遇到該標識符時,均以定義的串代換它。ANSI標准將標識符定義為宏名,將替換過程稱為宏替換。命令的一般形式為:
#define identifier string
注意:
1該語句沒有分號。在標識符和串之間可以有任意個空格,串一旦開始,僅由一新行結束。
2宏名定義後,即可成為其它宏名定義中的一部分。
3 宏替換僅僅是以文本串代替宏標識符,前提是宏標識符必須獨立的識別出來,否則不進行替換。例如:
#define XYZ this is a tes
使用宏printf("XYZ");//該段不列印"this is a test"而列印"XYZ"。因為預編譯器識別出的是"XYZ"
4如果串長於一行,可以在該行末尾用一反斜杠' \'續行。
#defineLONG_STRING"this is a very long\
string that is used as an example"
5 C語言程序普遍使用大寫字母定義標識符。
6 用宏代換代替實在的函數的一大好處是宏替換增加了代碼的速度,因為不存在函數調用的開銷。但增加速度也有代價:由於重復編碼而增加了程序長度。
二 #error
命令#error強迫編譯程序停止編譯,主要用於程序調試。
#error指令使預處理器發出一條錯誤消息,該消息包含指令中的文本.這條指令的目的就是在程序崩潰之前能夠給出一定的信息。
三 #include
命令#i nclude使編譯程序將另一源文件嵌入帶有#include的源文件,被讀入的源文件必須用雙引號或尖括弧括起來。例如:
#include"stdio.h"或者#include<stdio.h>
這兩行代碼均使用C編譯程序讀入並編譯用於處理磁碟文件庫的子程序。
將文件嵌入#i nclude命令中的文件內是可行的,這種方式稱為嵌套的嵌入文件,嵌套層次依賴於具體實現。
如果顯式路徑名為文件標識符的一部分,則僅在那些子目錄中搜索被嵌入文件。否則,如果文件名用雙引號括起來,則首先檢索當前工作目錄。如果未發現文件,則在命令行中說明的所有目錄中搜索。如果仍未發現文件,則搜索實現時定義的標准目錄。
如果沒有顯式路徑名且文件名被尖括弧括起來,則首先在編譯命令行中的目錄內檢索。如果文件沒找到,則檢索標准目錄,不檢索當前工作目錄。
四 條件編譯命令
有幾個命令可對程序源代碼的各部分有選擇地進行編譯,該過程稱為條件編譯。商業軟體公司廣泛應用條件編譯來提供和維護某一程序的許多顧客版本。
#if、#else,#elif及#endif
#if的一般含義是如果#if後面的常量表達式為true,則編譯它與#endif之間的代碼,否則跳過這些代碼。命令#endif標識一個#if塊的結束。
#if constant-expression
statement sequence
#endif
Eg:
#define MAX 91
#include <iostream>
using namespace std;
int main()
{
#if MAX > 99
cout<<"MAX is bigger than 99"<<endl;
#elif MAX > 90
cout<<"MAX is bigger than 90"<<endl;
#else
cout<<"MAX is smaller than 90"<<endl;
#endif
return 0;
}
跟在#if後面的表達式在編譯時求值,因此它必須僅含常量及已定義過的標識符,不可使用變數。表達式不許含有操作符sizeof(sizeof也是編譯時求值)。
#else命令的功能有點象C語言中的else;#else建立另一選擇(在#if失敗的情況下)。注意,#else屬於#if塊。
#elif命令意義與ELSE IF 相同,它形成一個if else-if階梯狀語句,可進行多種編譯選擇。#elif 後跟一個常量表達式。如果表達式為true,則編譯其後的代碼塊,不對其它#elif表達式進行測試。否則,順序測試下一塊。
#if expression
statement sequence
#elif expression1
statement sequence
#endif
在嵌套的條件編譯中#endif、#else或#elif與最近#if或#elif匹配。
# ifdef 和# ifndef
條件編譯的另一種方法是用#ifdef與#ifndef命令,它們分別表示"如果有定義"及"如果無定義"。# ifdef的一般形式是:
# ifdef macroname
statement sequence
#endif
#ifdef與#ifndef可以用於#if、#else,#elif語句中,但必須與一個#endif。
#define MAX 91
#include <iostream>
using namespace std;
int main()
{
#ifdef MAX
cout<<"hello,MAX!"<<endl;
#else
cout<<"where is MAX?"<<endl;
#endif
#ifndef LEO
cout<<"LEO is not defined"<<endl;
#endif
return 0;
}
命令#undef 取消其後那個前面已定義過有宏名定義。一般形式為:
#undef macroname
命令#line改變__LINE__與__FILE__的內容,它們是在編譯程序中預先定義的標識符。命令的基本形式如下:
#line number["filename"]
其中的數字為任何正整數,可選的文件名為任意有效文件標識符。行號為源程序中當前行號,文件名為源文件的名字。命令#line主要用於調試及其它特殊應用。注意:在#line後面的數字標識從下一行開始的數字標識。
#line 100 "jia"
cout<<"#line change line and filename!"<<endl; //line 100
cout<<__LINE__<<endl; //101
cout<<__FILE__<<endl; //jia
五 #pragma
命令#pragma 為實現時定義的命令,它允許向編譯程序傳送各種指令。
#pragma的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C++語言完全兼容的情況下,給出主機或操作系統專有的特徵。依據定義,編譯指示是機器或操作系統專有的,且對於每個編譯器都是不同的。
其格式一般為: #Pragma Para
1 message 參數。
Message 參數能夠在編譯信息輸出窗口中輸出相應的信息,這對於源代碼信息的控制是非常重要的。其使用方法為:
#pragma message(「消息文本」)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本列印出來。
當在程序中定義了許多宏來控制源代碼版本的時候,自己有可能都會忘記有沒有正確的設置這些宏,此時可以用這條指令在編譯的時候就進行檢查。假設希望判斷自己有沒有在源代碼的什麼地方定義了_X86這個宏可以用下面的方法
#ifdef _X86
#pragma message(「_X86 macro activated!」)
#endif
當定義了_X86這個宏以後,應用程序在編譯時就會在編譯輸出窗口裡顯示「_
X86 macro activated!」。就不會因為不記得自己定義的一些特定的宏而抓耳撓腮了。
2 code_seg 參數。
格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能夠設置程序中函數代碼存放的代碼段,當開發驅動程序的時候就會使用到它。
3 #pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次。這條指令實際上在VC6中就已經有了,但是考慮到兼容性並沒有太多的使用它。
4 #pragma hdrstop
表示預編譯頭文件到此為止,後面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能占太多磁碟空間,所以使用這個選項排除一些頭文件。
有時單元之間有依賴關系,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先順序,如果使用了#pragma package(smart_init) ,BCB就會根據優先順序的大小先後編譯。
5 #pragma resource "*.dfm"
表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體外觀的定義。
6 #pragma warning( disable : 4507 34; once : 4385; error : 164 )
等價於:
#pragma warning(disable:4507 34) /* 不顯示4507和34號警告信息。如果編譯時總是出現4507號警告和34號警告, 而認為肯定不會有錯誤,可以使用這條指令。*/
#pragma warning(once:4385) // 4385號警告信息僅報告一次
#pragma warning(error:164) // 把164號警告信息作為一個錯誤。
同時這個pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這里n代表一個警告等級(1---4)。
#pragma warning( push )保存所有警告信息的現有的警告狀態。
#pragma warning( push, n)保存所有警告信息的現有的警告狀態,並且把全局警告等級設定為n。
#pragma warning( pop )向棧中彈出最後一個警告信息,在入棧和出棧之間所作的一切改動取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop )
在這段代碼的最後,重新保存所有的警告信息(包括4705,4706和4707)。
7 pragma comment(...)
該指令將一個注釋記錄放入一個對象文件或可執行文件中。
常用的lib關鍵字,可以幫連入一個庫文件。
8 progma pack(n)
指定結構體對齊方式。#pragma pack(n)來設定變數以n位元組對齊方式。
n 位元組對齊就是說變數存放的起始地址的偏移量有兩種情況:
第一、如果n大於等於該變數所佔用的位元組數,那麼偏移量必須滿足默認的對齊方式,
第二、如果n小於該變數的類型所佔用的位元組數,那麼偏移量為n的倍數,不用滿足默認的對齊方式。
結構的總大小也有個約束條件,分下面兩種情況:如果n大於所有成員變數類型所佔用的位元組數,那麼結構的總大小必須為佔用空間最大的變數佔用的空間數的倍數; 否則必須為n的倍數。
下面舉例說明其用法。
#pragma pack(push) //保存對齊狀態
#pragma pack(4)//設定為4位元組對齊
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢復對齊狀態
為測試該功能,可以使用sizeof()測試結構體的長度!