美劇《硅谷》用實例告訴你,什么叫不作死就不會死
沒錯,軟件行業(yè)向來崇尚年輕化。如果大家已經擁有了自己的家庭,那么在編程領域已經算是個老年人了。而如果大家已經年過廿五甚至已過而立,那么各位未來的技術之路可能只會一路走低。
唉,自以為是的家伙并不總能完美地解決編程的技術難題。盡管他們的大腦當中塞滿了關于最新、最流行的各類架構、框架以及堆棧的技術細節(jié),但他們并不具備對軟件在本質層面為何能夠確切起效或者發(fā)生故障的實際認知。這些經驗只會在我們連續(xù)數(shù)周遭遇怪異甚至莫名其妙的錯誤之后慢慢積累并建立起來。
正如《硅谷》的觀眾們心滿意足地看著第一季第六集結尾那位少年天才最終搞砸了一切,我們這些編程界的“老者”們也同樣樂于欣賞那群視我們?yōu)?ldquo;過時之人”、但卻由于不聽前輩勸告而陷入困難的后起之秀們的沮喪表情。
本著共享精神的考量、或者也算是給年輕人的一點教訓,在這里我們總結出了一些遠不是憑借聰明的頭腦加上幾周的實踐就能掌握的寶貴經驗。順帶一提,其中很多財富只有那些需要靠兩位十六進制數(shù)字才能寫出自己年紀的老者們方可掌握。
內存很重要
就在不久之前,我們計算機設備上的內存容量還在以MB為計量單位而非GB。當我組裝起自己的第一臺計算機(一臺Sol-20)時,其內存甚至只有可 憐的KB級別。這臺設備的主板上接有約64塊內存芯片,每一塊大約配備18個插針。我記不清楚具體數(shù)目了,但我可以肯定每個接點都是由自己親手焊接完成。 當焊接任務結束后,我還得重新處理那些無法通過內存測試的插針。
如果大家像我一樣經歷過那段內存是金的往昔歲月,就會意識到這一點點資源有多么寶貴。如今的年輕人們則不會那么嚴謹,而更傾向于“差不多得了”的態(tài)度打理內存資源。他們會把指針搖來晃去,從不清理自己的數(shù)據(jù)結構,這一切都是因為內存成本如今已經非常低廉。他們只需要點擊一個按鈕,就能為自己的云實例添加16GB內存容量。如果每個人都能如此輕松地從Amazon手中租到配備244GB巨量內存的實例,鬼才會在編程當中認真考慮內存的分配問題。
然而垃圾收集機制的工作效果總會有局限,正如家長不可能無限度地為小朋友們打掃房間。大家可以分配規(guī)模龐大的堆,但最終我們仍然需要對內存加以清 理。如果各位習慣了任意揮霍資源并在內存當中如流感一般來回穿梭,垃圾收集機制很可能出現(xiàn)體積膨脹的狀況——并最終塞滿看似充裕的224GB空間。
除此之外,虛擬內存的興盛同樣帶來值得重視的隱患。如果我們的計算機由于內存不足而轉向利用磁盤進行數(shù)據(jù)交換,那么軟件的運行速度將發(fā)生成百倍甚至上千倍的速度遞減。虛擬內存從理論層面講確實大有可為,但在實際效果角度看卻太過緩慢。程序員們需要清醒地意識到,內存資源在物質極大豐富的今天仍然非常珍貴。如果缺乏這種科學的觀念,那么原本在開發(fā)階段運行速度理想的軟件很可能在投付實踐之后遭遇速度下滑。換言之,大家的工作成果根本無法實現(xiàn)規(guī)?;卣?。近年以來,可拓展能力已經成為一切技術方案的必要前提。因此請大家注意,在軟件或者服務遭遇瓶頸之前打理好內存資源。
計算機網(wǎng)絡速度緩慢
負責市場營銷的員工們一直將云服務包裝成類似于計算業(yè)務領域的萬靈藥,在這里數(shù)據(jù)總能夠順暢無阻地往來遷移。如果大家希望在云端保存自己的數(shù)據(jù),他 們還準備好了能夠提供永久存儲、備份以及其它各類功能的簡單Web服務產品——總而言之一句話,事情交給服務供應商、您就放心吧。
在萬事拜托這方面、營銷人員的宣傳內容的確屬實,但還有一點他們沒提——客戶需要等,長久地、不懈地等。進入與傳出計算機的全部流量都需要耗費時間。相較于CPU與本地磁盤驅動器之間的傳輸速度,計算機網(wǎng)絡一直扮演著緩慢小烏龜?shù)慕巧?/p>
編程界的前輩們可謂“生在新中國,長在紅旗下”,在那艱苦的歲月里互聯(lián)網(wǎng)還根本連雛形都沒有。FidoNet會以對話方式將我們的數(shù)據(jù)路由至與可能接近目的地的其它計算機處。要想跨越國境線,大家的數(shù)據(jù)可能走得比人還慢——花費幾天時間穿越無數(shù)吱吱作響的調制解調器。
這種痛苦的經歷告訴他們,正確的處理方式應該是盡可能多地以本地方式處理計算任務,并最大程度保證遠方的Web服務只需要處理規(guī)模較小的最終結果。今天的程序員們很可能無法體會這些由老一輩無產階級開發(fā)者們從實踐中辛苦積累而來的教訓,事實上云存儲給出的承諾并不可靠,而且直到最后幾毫秒內才可以放心將任務交給云服務。
編譯器中存在漏洞
當我們遭遇故障之時,真正導致問題發(fā)生的往往并不是我們編寫出的代碼本身。我們也許忘記了對某些項目進行初始化,或者沒能及時檢查某個null指針。無論實際原因是什么,每一位程序員都明白當軟件出現(xiàn)故障時,責任必須由我們自己承諾——句號。
事實上,最令人頭痛的并不是我們自己的編程失誤。有時候責任源自編譯器或者解釋器。盡管目前的編譯器與解釋器在穩(wěn)定性方面相對可靠,但其距離完美仍有很長一段道路要走。必須承認,無數(shù)技術人員耗費大量心血才讓今天的編譯器與解釋器擁有當下的穩(wěn)定性水平,但將這種穩(wěn)定性認定為理所當然仍然不夠明智。
需要提醒大家的是,編譯器與解釋器同樣有可能發(fā)生故障,我們也應當在遭遇問題時將針對二者的調試工作考慮入其中。如果大家并不清楚編譯器為什么出現(xiàn) 問題,那么追尋答案的過程很可能耗時數(shù)天甚至是數(shù)周。
早在很久之前,程序員們前輩們就意識到有時候最理想的問題調試途徑并不是測試自己的代碼成果,而是將注意力集中在工具身上。如果大家習慣性地認為編譯器本身不會出問題,而且不假思索地把責任歸咎于代碼的渲染計算過程,那么往往耗費數(shù)天甚至數(shù)月也無法從工作中找到根本不存在的問題根源。年輕人啊,相信你們很快就能在實踐中成長起來。
速度對于用戶而言極為重要
很久以前,我曾經聽說IBM公司就可用性議題開展過一次調研,并發(fā)現(xiàn)人們的意識會在響應時間超過100毫秒之后出現(xiàn)波動。這一結論到底是真是假?我曾經就此求證于搜索引擎,但互聯(lián)網(wǎng)掛掉了……而且我之后也忘記了再試一次。
經歷了IBM大型機上那古董級綠屏應用時代的朋友們肯定知道,IBM公司將100毫秒這一導致人腦意識渙散的時間分水嶺設為響應速度閾值。有鑒于此,他們在I/O線路方面投入了大量精力。在銷售其大型機產品時,藍色巨人的工作人員們會以詳盡的規(guī)格列表指明設備當中的I/O通道數(shù)量,這種作法與汽車制造商介紹自家發(fā)動機參數(shù)時如出一轍。誠然,這些設備也會如現(xiàn)代產品一樣發(fā)生崩潰,但當它們處于正常運轉狀態(tài)時,這些數(shù)據(jù)總能通過預設通道順暢地流向終端用戶。
我曾親眼目睹過不少一位編程人員絞盡腦汁地調整其由于大量JavaScript庫以及數(shù)量總量流向瀏覽器而導致崩潰的AJAX重量級項目。他們往往抱怨稱,將其陷入泥潭的緩慢創(chuàng)新成果與作為替代對象的陳舊綠屏終端相比較并不公平。
但企業(yè)中的其它部門卻應該為此而慶幸。畢竟如今我們迎來了更美觀的圖形顯示效果并在應用程序中包含更多色彩表現(xiàn)。毫無疑問,CSS讓一切變得更酷、更漂亮,令用戶不滿的僅僅是其緩慢的響應速度。
真正的Web永遠不可能像辦公網(wǎng)絡那樣迅捷
現(xiàn)代網(wǎng)站往往像是一只用時間壘砌而成的小豬。其往往需要數(shù)秒鐘時間將MB級別的數(shù)據(jù)從JavaScript庫當中交付給瀏覽器。接下來,瀏覽器需要將這些多層MB數(shù)據(jù)推向JIT編譯器。如果我們能夠將世界范圍內全部jQuery重新編譯帶來的時耗加以累積,其總長很可能達到數(shù)萬甚至上百萬年。
樂于使用基于瀏覽器的各類工具的程序員們往往會犯下一類常見錯誤——以無處不在的方式肆意濫用AJAX。這一切在辦公環(huán)境的演示過程中都能順利完成,畢竟在這類條件下服務器本身就位于桌子后面的柜子上。
有時候“服務器”也會運行在本地主機當中。當然,文件能夠在彈指一揮間到達指定位置,運行的整個過程都非常順暢、甚至老板在屋角進行測試時也能應對自如。
不過當用戶身處DSL連接環(huán)境下或者需要通過一座已然過載的信號塔以蜂窩網(wǎng)絡進行路由時,結果又會如何?他們需要耗費大量時間等等庫內信息的交付。如果無法在數(shù)毫秒當中順利抵達,他們往往會憤而在TMZ上發(fā)表文章大發(fā)牢騷。
算法的復雜程度至關重要
在某個項目當中,我遇到了與《硅谷》居住中Richard面臨的同一個難題,而我也與他一樣、把求助的目光投向了一位尚未到飲酒年齡但已經對 Greasemonkey的前世今生極為熟悉的朋友身上。他對我的代碼進行了重寫,并把結果發(fā)還給我。在閱讀了各項變更之后,我意識到他僅僅是將代碼內容 變得更加精致,但同時也把算法的復雜程度由0(n)提升到0(n^2)。他堅持將數(shù)據(jù)放在列表當中以實現(xiàn)匹配,這么做看起來確實很漂亮、但卻會隨著n值的 提升而導致代碼運行速度越來越慢。
算法復雜性議題在高校的計算機科學課程當中已經得到了詳盡的解讀。然而,很多出身科班、憑借著聰明才智在周末自學掌握Ruby或者CoffeeScript的年輕人們根本沒能深入領會其精神。
復雜性分析可能看似一種極為深奧的理論性事務,但其會隨著項目規(guī)模的變化而展現(xiàn)出完全不同的面貌。當n值較小時,一切都能夠輕松實現(xiàn)。特別是在內存容量充裕時,代碼將以令人滿意的速度得以執(zhí)行,這時即使糟糕的算法也能夠在測試中得以通過。然而當 用戶倍增再倍增時,那些包含有0(n^2)甚至0(n^3)的算法會讓使用者陷入無盡等待的噩夢。
當我詢問這位天才少年,能否把匹配流程轉化為一條二次算法時,他撓了撓頭。他根本不知道我在說些什么。后來,我利用一套散列表取代了他的清單,一切也就此回歸順暢。不過時至今日,我想他一定已經老到到能夠理解這個話題的程度了。
庫有可能糟糕透頂
那些編寫庫的技術人員并不總是關注我們這些普通使用者的利益與訴求。他們確實在努力幫忙,但其關注重點往往集中在為整個世界作出貢獻身上——而非我 們日常面臨的小小難題。他們最終打造出的往往是一把能夠解決多種不同版本問題的瑞士軍刀,而非針對當前問題作出深度優(yōu)化的解決方案。庫項目的工程技術與編碼水平毋庸置疑,但運行速度卻可能不堪恭維。
如果大家不在這方面多加注意,那么庫本身很可能把我們的代碼成果拖進速度緩慢的泥潭,而各位對此甚至一無所覺。我曾經請一位年輕的程序員幫我調整代碼成果,因為我寫了十行代碼來從字符串中提取字符。
“我可以用一條正則表達式與一行代碼完成同樣的任務,”他自信地表示。“從十行到一行,這就是看得見、摸得著的改進。”但他并沒有意識到,他這一行代碼在每一次進行正則表達式調用時,都需要經歷解析與重新解析的過程。他單純認為自己編寫的是一行代碼,而我這是十行代碼,因此他比我水平高到不知道哪里去了。
庫與API在適當運用的前提下能夠發(fā)揮巨大的作用。但如果以內部循環(huán)的方式加以使用,那么他們完全可能對速度產生破壞性的影響——而當事人往往還完全摸不著頭腦。
文章轉載自微信公眾號“一斑”(ID: yiban51CTO)