文章:C++ Compilation Speed(C++的編譯速度)
日期:2010.08.17
作者:Walter Bright
作者的部落格:Walter Bright Home Page
作者簡介:
Walter Bright是位電腦程式設計師,是D語言的設計者,第一套C++原生編譯器的主要開發員,也就是Zortech C++(後來變成Symantec C++,現在是Digital Mars C++),在C++編譯器之前,他開發了Datalight C編譯器,先以Zorland C後以Zortech C之名販售。
C++的編譯速度
我常聽到有人抱怨說C++程式碼編譯速度很慢,有時候甚至要花上整夜的時間,編譯慢是exported templates這玩意的源由之一,甚至列在發展Go語言的理由清單上,這點確實是個問題;既然我身在C++編譯器的產業中,三不五時就會被問到這點。
為什麼C++編譯速度慢?一旦我們合理假設開發C++編譯器的人都擅長於寫出效能高的程式碼,那麼,一定有某個深植於C++語言本身的原因;的確,不同牌的C++編譯器速度快慢相差極大,但還沒完喔,其他語言通常能夠快上一整個等級,而且厲害的編譯器專家應該不會只為其他語言寫編譯器吧(!)。
我從1987就在寫C++編譯器了,對比今日想當年,電腦可是慢的不得了,所以我投注極大的精神讓編譯器能夠快一點,花上大把的時間做效能分析以及微調各個小地方來讓編譯器更快,我發現,語言本身的某些特性讓編譯速度快不了。
理由是:
1. 轉譯過程中有七個階段[1]。雖然有些可被合併處理,但在前端處理原始碼最少要有三個階段,至少我還沒找到降到三以下的法子。要快的話語言設計時就只能有一個階段,C++0x惡化了這點,居然要求trigraph轉換與行尾為\與下行接合這兩個功能要能夠支援string literals[2]。
2. 每個階段都相依於前一個階段,意思是,沒有可靠的方式可以做往前先看的動作,例如,往前先去找#include然後先去讀進檔案;編譯器沒辦法往前先看出是個string literal所以不要做trigraph轉換,必須先做trigraph轉換,但要做好回到上一步的準備;我從沒找出能夠平行編譯C++程式碼的方法,除了在make時加上-j參數這種很粗略的作法。
3. 因為#include是種文本逐字置入(textual insertion)的機制,而不是符號式(symbolic)的,當一個檔案被#include很多次,編譯器只能悲情地做白工地一再地處理,即使是被#ifndef包起來也一樣。(Kenneth Boyd跟我說,如果將標準文件讀的仔細一點,是有可能允許編譯器省略被#ifndef包起來的#include,但我不知道有哪一支編譯器利用了這點。)
4. 程式檔案中總是傾向於,通通#include進來就對了,當責任全部落在編譯器身上時,每一個.cpp檔通常都會連帶引出一拖拉庫的檔案要處理,在Ubuntu上,就算只把標準函式庫#include進來,居然需要處理74支檔案總共37,687行耶(不包括同支檔案被#include多次的情形);templates以及generic programming的興起更惡化了這點,而且,把更多的程式碼放進標頭檔(header files)中的壓力也逐漸升高,更是雪上加霜。
5. 語意上的與語法上的(不只是詞彙上的)處理單位依賴處在它之前的整個原始文本,意思是,沒有東西是上下文無關的;不把#include的東西先看一看,就不能正確地解析(parse)檔案,甚至是先做lex的動作也不行,標頭檔在第二次#include時可能含有不一樣的內容(事實上,確有標頭檔利用這點)。
譯註:語意上的 semantic,語法上的 syntactic,詞彙上的 lexical。
6. 因為第5點,編譯器在某個TU[3]所編譯的#include的結果,不能下一個TU共用,每個TU都必須從頭開始。
7. 因為不同的TU之間彼此不知道對方的存在,常用的templates在每個TU都會被產生出來,鏈結器(linker)會將重複的刪除,但當初所花的時間都白費了。
預先編譯標頭檔(precompiled headers)解決了一些問題,但那是對非標準的C++做出某些簡化後的假設,才可辦到,例如,標頭檔被#include還是會含有同樣的內容;所以你必須小心,不能違反這些假設。
想解決這些問題又要跟舊有的程式碼保持相容性,真是高難度的挑戰啊,我預期在C++0x之後,會有相當份量的心力花在這些問題上,但那至少是10年後了。
在那之前,並沒有哪個方法可稱得上是解答,exported templates被廢棄了,precompiled headers是不符合標準的,imports被踢出C++0x標準之外,以及往往你沒有選擇編譯器的權力,諸如此類的;現在來說,有效地使用make -j參數可算得上是最好的方法了。
我會再談談關於語言的設計,哪些特性能夠導致快速的編譯速度。
註解
[1] C++98標準文件的2.1章節, 七個階段是:
1. Trigraph與萬國碼轉換。
2. 行尾為\時接到下一行
3. 轉換成預先處理的標記(preprocessing tokens),標準文件註明說這是上下文相依的。
4. 預先處理的指令執行,展開巨集,#include的讀取以及再跑一次1到4。
5. 將原始碼中處在char與string literals的字元轉換成執行字元集(execution character set)。
6. string literal的接合。
7. 將預先處理的標記轉成C++的標記(C++ tokens)。
[2] 在C++0x標準文件中的例子在2.14.5-4:
const char *p = R"(a\
b
c)";
assert(std::strcmp(p, "a\\\nb\nc") == 0);
[3] 一個TU,也就是一個轉譯單位(Translation Unit),通常就是一支C++原始碼檔案,通常是以.cpp為副檔名,編譯一支TU會生出一個目的檔(object file),每個TU的編譯過程都與其他TU不相關,最後由鏈結器(linker)將目的檔整合成單一的執行檔。
感謝
感謝Andrei Alexandrescu、Jason House、Brad Roberts以及Eric Niebler給予這份文章草稿時的有用建議。
2010/08/22
翻譯:C++的編譯速度(C++ Compilation Speed) by Walter Bright
標籤: programming
2010/08/16
翻譯:Java簡史(A Brief History of Java) by Jeff Foster
文章:A Brief History of Java(Java簡史)
日期:2010.08.13
作者:Jeff Foster
作者的部落格:Fatvat : Exploring functional programming
Java簡史
話說當年1995之時,昇陽(Sun)公司釋出Java程式語言,做為整個Java平台策略的一部分,當初喊著“一次編寫,到處運行”的口號,要讓Java遍地開花,從腕錶到手機到筆電到超級電腦,隨處皆可運行。
剛開始時Java的接受度有高有低,當出現Java力有未逮之處,就會有福音天使出來保證這技術將會改變世界,你看,靠Java運作的烤吐司機就在不遠的角落了。
隨著時間演進,為了守住“一次編寫,到處運行”的承諾,Java也逐漸背上更多的負擔,標記為廢棄的方法(deprecated methods)到處可見,但昇陽必須留下這些功能來保持向後相容性;java.util.DateTime此套件變成了劣質設計的同義字,差勁的命名規則總是在修正中(叫它size好還是length好呢?)。
微軟(Microsoft) 的.NET開始蠶食Java的領地,微軟團隊導入delegates,一種有做型別確認的函式指標,使得事件處理(event handling)變得容易許多;Java要趕快反擊,所以在版本1.1時,Java生出了inner classes,能夠有類似的效用,但以一種較有限制與用起來較麻煩的方式出現,一份Java白皮書斷定地說『bound method references是不必要的東西....這玩意減損了Java語言的簡單性與一致性』,但與此同時,將bound method references放進Java裡的勢力從沒消失過。
當Java到了版本1.4時,為了對抗微軟的.NET,昇陽決定要有一套新的策略手段,思索良久之後,昇陽將版本1.4改成“5”,試圖一舉超前.NET的2.0。
同時間附帶了另一個決策,實作generics,一種能夠有額外的型別安全檢查的技術,不幸的是,隨型別安全而來的代價是要多打字,工程師引入generics後,每次又要寫一遍
List<Foo> foos = new ArrayList<Foo>
敘述時,就會常被聽到咒罵聲。大學很快地就接納了Java;學生們不再需要學習手動記憶體管理與指標的種種艱澀難懂的奧祕,反之,他們可以依賴Java來做這些苦功夫,集中精神在解決問題上,不幸的是,這也導致一批同等級的開發者,被稱作“設計模式使徒(Patternistas)”,手上只有鎚子且把所有問題都看做釘子去敲,在他們的領導下,Java命名規則越來越可笑,當這種類別名“RequestProcessorFactoryFactory”成為一種常態時,一些開發人員開始質疑無限制地構築抽象化高塔是否真為明智之舉。
當開發人員意識到他們終日所為僅是把數千行的程式碼搬來搬去而已,他們需要一個字眼來辯護他們的存在意義,那個字就是“重構(refactoring)”,所有的設計模式使徒欣喜不已;他們不但能運用工廠的工廠模式(factory factories)、獨身模式(singletons)與訪問者模式(visitors)來解決問題,而且還能朝三暮四改變心意,並用個時髦字眼來正當合理化!
整個產業的演進方向只是為了滿足設計模式使徒們而已,跟別人與別的語言比起來,老鳥們覺得Java越來越弱,所以新型開發環境出現了,IntelliJ與Eclipse的目標是將開發人員受的傷害最小化,靠著極佳的程式碼自動補足機制(code completion)與重構功能,只需按幾個鍵而不用輸入一長串的程式碼來達到那想要卻沒有的語言功能。
Java開始向企業端進攻,招攬了一些有領導地位的的架構大師,打造出一套會造成典範轉移的協同式架構方法來建置企業軟體,其結果不小於一場革命;爪哇豆(beans)現身了,Beans顯然是一種伺服端的元件架構,用來當企業應用軟體的建構基本模組。
使用generics所帶來的角括號障礙一旦減低之後,Java邁步向前跳上了XML的列車,藉由使用XML,開發人員就能將簡單明瞭的概念表達成一整個龐大囉嗦的角角惡夢,好處是XML檔案(不同於其他檔案類型)可以輕易地被電腦看懂處理,至於對人難以閱讀這種小代價是值得的,像是Ant和JBoss這些軟體都是由可執行的XML所建構出來的。
與此同時,在這些架構大師的宇宙之外,一群新種程式員認為將該死的工作完成遠比一整天都花在打字重要,這種想法誕生出一些框架,例如Rails,設計的重點在於不擋路讓你能解決真正的問題,其中心思路以“約定俗成先於設定(convetion over configuration)”之名廣為人知,Rails獲得正面的迴響,而且至少有一些Java信徒轉投向Ruby的陣營;Java不再能緊抓人心的第一個徵兆開始浮上檯面。
2006年8月,Java 7計畫啟動,很多開發人員都要求一個叫做lambda expressions的功能,毫無疑問地會簡化很多原先Java搞的很痛苦的撰碼作業,可惜,Java委員會四年後還在為這項功能的細微差別爭辯不休,而且有可能會被排除掉,Java 7的無作為引出了新一代更迷人的語言,例如Clojure和Scala,設計在Java環境中運行,但不需要Java語言。
2009年4月,甲骨文(Oracle)公司宣布計畫將收購昇陽,此乃敲下棺材釘的最後一鎚,由“黑暗王子拉瑞”所領軍的甲骨文是一部併購機器,擅長於企業軟體與賺進大筆鈔票,當甲骨文的律師們了解一份份的軟體專利文件,他們會挑個大目標然後引起戰鬥,而還有哪個目標比谷歌(Google)更大呢(一家網路廣告的領導品牌),所以甲骨文的律師猛撲而出,戰爭就此展開。
這會給Java帶來什麼後果呢?從15年前的小小開端起,在昇陽的領導下,Java爬上了最熱門的程式語言的頂端,而在甲骨文的控制下,不但到底會不會有下一版的Java情況不明,更別提開發人員渴望許久的功能了,這是Java的新開始還是結束呢?
標籤: programming
2010/08/12
翻譯:命名專案的十項提示(10 Tips for How To Name Your Project) by J Wynia
文章:10 Tips for How To Name Your Project(命名專案的十項提示)
日期:2006.06.22
作者:J Wynia
作者的部落格:The Glass is Too Big
命名專案的十項提示
這已經是數年前我寫的一系列文章中的一篇, 但我想要再次分享出來,考慮到有這麼多人開發的產品與服務的名字,居然需要落落長的解釋與說明,才能夠了解、念出來或是正確寫出來,我想這篇的內容仍然還是跟我當初寫的時候一樣重要。
命名你的專案的方法(與避免事項)
1. 如果你想到的名字是直接取自於某科幻或奇幻小說、電影、漫畫等等題材,放棄吧,在軟體業中這些都已經被用爛了,不僅想出有原創性的名字的可能性相當低,而且,科幻題材中大部分的角色與地點名稱都已經被註冊了,你有被告的風險。
2. 如果你是從希臘、羅馬或古北歐神話中取材,再嘗試別種作法看看,我們不知收到多少封談到以“墨丘利(Mercury)”命名的軟體的電子郵件。
3. 拜拜股溝(Google)大神,找找你想要取的名字,找到的項目越少越好,如果什麼也沒找到,恭喜你囉。
4. 如果你想要錯拼幾個字母來打造獨特的名字,別!把你嶄新的視窗膽案系統稱作Phat32,使用者在搜尋引擎只會看到關於“fat32”的結果,你覺得他們會高興嗎。
5. 如果你取的名不能在五零或六零年代的電視上說出來,你的方向大概是錯的,特別是當你想要產品在工作環境中被每一個人使用,如果單單念出產品名就有可能會被告性騷擾,那自然沒人會推薦給同僚。
6. 如果你的產品名連發聲念出來都是件不可能的任務,你就得不到口耳相傳帶來的好處,同樣的,如果沒人知道怎麼念,人們也就不會試著大聲說出來詢問相關疑惑。你怎麼念MySQL?PostgreSQL?GNU?幾乎所有地球上有人用的語言都是有某種子音/母音的音節系統,輪流使用子音與母音來拼名字是個相當好的方法,確保某人可以發音出來。
7. 跟女人的裙子一樣,越短越好。
8. 查看看.com的網域名稱是否可用,如果已經被申請走了,這是一項很好的指標,表示某人已經想到這個名字而且比你搶先要用它。做這個動作即使你不打算申請網域名。
9. 別把產品本身的限制條件加在名字上,把產品叫做LinProduct或是WinProduct的話,那將來要釋出跨平台版本怎麼辦。
10. 別把你自己的姓名用在開放原始碼計畫上,如果將來你不再參與,那這計畫就必須改名,或是你的名字將會以你不喜歡的方式被運用。
2010/08/11
翻譯:專案之名有什麼意涵?(What's in a Project Name?) by Jeff Atwood
命名,看似簡單卻絕不容易,啟動新專案要有個codename(內部專案代碼),寫程式要替變數、函式、類別取名,當專案完成變成產品,那也要有個又酷又炫的名號;取的好心情佳,取的不好整天就不順;看看這篇關於專案命名的文章吧。
文章:What's in a Project Name?(專案之名有什麼意涵?)
日期:2007.11.12
作者:Jeff Atwood
作者的部落格:Coding Horror
專案之名有什麼意涵?
從我進Vertigo公司以來,曾經參與過這些個專案:
- 米開朗基羅(Michelangelo)
- 納許(Nash)
- 威士忌小鎮(Whiskeytown)
- 超大硬糖(Gobstopper)
這些我們內部使用的專案代碼,是從一份放有各種“東西”的清單中按照字母順序選出來的,每個新專案都是這樣被命名的,我們從A開始用,當到Z用完時,我們會選出一組新的清單,作為命名專案的靈感來源,你可以猜出上述專案名的靈感清單從何而來嗎?別偷看啊!
關於專案命名,我們有以下這些鬆散的方針:
- 我們偏好一個單字的名字。
- 必須相當容易就可以念出名字來,拼出來也很簡單。
- 對客戶來說須是親切適宜的。
- 在整個公司內這名字必須是獨一無二的,沒有重複使用。
- 我們需要一個夠多的靈感清單可以選,照字母順序。
最後,嘗試將命名一個專案所帶來的利益加以數字化,就如同試著量化一個名字所能帶來的好處,可能兩者同樣都很眼光狹隘,對一個找到跟他們一拍即合的命名商的幸運客戶來說,其邊際效益遠超出單純的取個名字,有新的詞彙字可以學,好玩的遊戲可以參與;還有,以百猴(一家幫人命名的公司)的例子來說,無可懷疑的溫暖與關懷,『我們得到的遠超出單單一個名字』九十八點六(98point6)的羅賓巴爾這麼說,『我是說,我替我女兒找了個好名,我們一個資深董事對‘梅斯卡蘭札(Mescalanza)’有強烈的認同歸屬感,大家不再稱呼他為吉姆(Jim),他的名字就是梅斯卡蘭札。』與此同時,她還說,『我們網路開發部門的資深經理就莫名地愛上了‘果醬餅乾(Jamcracker)’,以致於,哈維(Harvey)會議已經改稱果醬餅乾會議,這家公司有三百個人將果醬餅乾視做哈維的代名詞。』
巴爾用手捂住嘴巴,『喔天啊,』 她說,『我忘了,我不應該向一個記者提到這些名字,技術上來說,我們沒有這些名字的所有權,果醬餅乾這名字仍然是百猴的資產。』
巴爾停頓了一下,好像在思考些什麼,然後她爆笑起來,『聽著,』她說,『我收回剛說的,你想寫啥就寫啥,如果外面真有人想要將他們的公司命名為果醬餅乾,願神保佑,也祝他們幸運。』
不論如何,困難點在於,如何生出一些新的靈感用來激發專案命名時的來源,我們先看看微軟的專案代碼以及蘋果的專案代碼,當做想法的出發點,
這裡是過去不同時間點時,我們考慮用來命名專案的各種想法:
食物種類 電玩遊戲(Atari 2600,大型機台, 等等) 啤酒品牌 羅馬帝王 卡通人物 / 節目 神話中的名字 / 神祇 汽車 GUIDs (個人最愛之一) 寶石 咖啡飲品的種類 州名 鄉村名 植物 希區考克的電影 | 狗的品種 顏色 著名的探險家 樹木 美國稅務表 英國君王 名人(例: 沙岡) 維基百科文章名 單一字母(包括萬國碼) 無線電通訊的字母系統 糖果品牌 恐龍 歷史地點 城市街道名 IKEA宜家家居產品名稱 | 扣件種類(螺帽,螺栓,鉚釘,等等) 滑雪度假村 國家公園 山之顛 第二次世界大戰時代的船隻 鳥類 海灘 橋 Web 2.0的命名 魔獸爭霸的王國名 起司 國家 穀類早餐品牌名 |
開新專案之時,挑選一個新名字總是有其樂趣所在,我很驚訝我們很快就用光一整個A-Z循環,從我進公司的2005年算來,我們已經經歷過4次循環了,那是我們的作法,那麼,你又是怎麼命名你的專案的呢?