黑客馬拉松(Hackathon,Hacker + Marathon)就是一個鼓勵「搭積木」而非「造輪子」的活動。你能夠在固定的時間(如今多爲一天)內爲所欲爲,去作本身想作的產品。固定的時間就限定了你能作的事情 —— 「造輪子」這種基礎性的工做,一天下來可能想還沒想清楚呢就過去了,因此最終比拼的是「搭積木」的能力 —— 誰搭出來了,誰搭得漂亮。javascript
Juniper內部的hackathon起源於一年前,如今已是第三屆了。老闆們知道「老」程序員們(Juniper員工年齡都偏大)熬夜編程是個挑戰,因而慷慨地給了一天開發時間,半天準備presentation的時間,和半天的pitch時間。今天我就結合本身此次hackathon的經歷,講講在hackathon中如何選題,如何利用好一天的時間鏖戰(包括pivot),以及如何pitch。html
選題
既然是爲所欲爲,選題就要選本身想作的東西。若是能往公司的產品上靠(內部hackathon),或者往大會的主旨上靠(外部hackathon)最好;若是不能,也不要緊。hackathon的精神是交流學習及努力交付。若是你找不到什麼題材,又想參加hackathon,不妨往這幾個方向去想:前端
- 提高可用性(availability)
- 提高效率(efficiency)
- 提高可測性(debugability)
- 提高性能(performance)
- 提高娛樂性(recreationability)
找到你想作的點以後,要把產品的範圍縮小縮小再縮小。千萬不要高估你一天之中能作出來的東西。若是你聚精會神毫無打擾地寫一天能寫1000行代碼,那hackathon你的代碼量要控制在500行內。若是一個team好幾我的作開發,假設三人,那麼總代碼量最好乘以一個減去溝通成本的有效編碼的係數,如0.7,也就是三我的一塊兒寫,總代碼量控制在1000行。這裏舉的數字都是估值,具體要看你本身對本身和團隊的估計。java
產品的範圍集中在一個小點上的好處是容易交付。沒有什麼比作了一天沒有任何可交付的軟件讓人痛心疾首的了。若是很不幸你低估了本身的能力,很快就作出來可交付的產品,那麼空閒的時間能夠繼續完善產品(而不是擴大產品的範疇),讓核心功能更突出。python
此次hackathon個人選題和visualization相關。我就說說其中和Juniper產品無關的部分吧。在過去的數年裏,咱們漸漸知道data visualization的巨大威力 —— 下圖是當本拉登被擊斃後,twitter上的傳播路徑 —— 複雜的數據流動,經過一張簡單的圖就說得無比清楚:linux
其實相似的路徑在代碼中也存在。一個公司的代碼庫裏面有數百萬,數千萬行代碼,有誰能把它捋清楚,有誰能在很短的時間內瞭解其中的細節呢?可不能夠將代碼庫可視化,讓其可以本身告訴你其中的各類邏輯/調用關係?c++
這就是我想嘗試的。程序員
開工
開工首要解決的問題就是原料,也就是尋找能夠用來「搭積木」的「輪子」。在hackathon前備好料最好不過,當天備料也何嘗不可。web
備料是個很重要的過程,你使用的「輪子」決定了你「搭積木」的高度。不少時候一個產品都少不了和數據打交道,若是使用django的admin(加south),處理數據,存儲數據,撰寫基礎數據都易如反掌 —— 並且你只須要寥寥數行代碼(不包括model的代碼)就能獲得大部分功能。別人也許還在吭哧吭哧地作CRUD,這廂你已經開始聚焦你要解決的核心問題了。chrome
作hackathon常常碰見的問題是作了半天發現手頭的產品礙於主觀或客觀緣由沒法繼續下去,走進了死衚衕。這時要果斷pivot —— 注意不是另起爐竈。pivot這詞很差翻譯成中文,借用Tao神的形象解釋(若是你看了這幾期『途客們地旅行夢』,你應該對他有點印象)—— pivot在籃球場上就是指一隻腳做爲軸,另外一隻腳來回探索尋找投籃或傳球的機會。當產品進入死衚衕,不要當即否認,退後一點作作其它方向上的嘗試,也許能找到突破口。記住努力交付是hackathon的精神之一,pivot的目的就是爲了交付。
回到個人項目。咱們知道,對代碼進行profile分爲靜態和動態兩種。我想達到的目標是可以可視化代碼中運行時的路徑(這個足夠cool!)。好比說對linux kernel的代碼作研究(舉個例子,我真正作的不是這個),我想知道一個個數據包在kernel裏走過的所有流程,而後以此繪製熱點圖,Petri Net等等。
對這個需求點,我須要的「輪子」有:
(1) systemtap,一個linux下媲美DTrace的probing工具。systemtap能夠生成kernel module將你須要的代碼注入到被監測的點(經過TRAP),達到不改變已有系統的代碼,runtime獲取信息的效果(你能夠把它想象成病毒 - 獲取程序的控制權,執行病毒代碼,而後恢復原有程序的執行)。
(2) jointjs(DavidDurman/joint)。得到了運行時的數據後,我須要將其可視化,而jointjs就是我找到的一個最稱手的工具(但不完美)。我原本是想用d3.js或者sigma.js來作這事的,但前者太基礎,須要寫不少代碼,後者表述的圖形種類太少,因此我最終選定了jointjs。
(3) websocketd。這是一個能夠把stdout輸出轉化爲websocket數據傳遞給browser進行展現的好工具。好比下圖是我作得一個對vmstat的可視化(實時變化的):
找齊了「輪子」後,我打算這麼「搭積木」:使用websocketd來運行systemtap抓取數據(systemtap把數據輸出到stdout,websocketd將其發到瀏覽器),而後前端生成jointjs的繪圖代碼,生成實施的數據流圖。代碼量估計不會很大,就幾百行的樣子(systemtap腳本+javascript)。
惋惜人算不如天算,我沒鬥得過Murphy's Law(凡事可能發生,就必然發生),我工做的幾臺vm都罷工了,要麼業務跑不通,要麼vm自己沒法訪問。下面的systemtap腳本運行也出了很多問題(示例而已,已經大幅裁剪和移除和工做相關的內容):
global ignoredglobal start
probe begin {
printf ("Start probling...\n")
start = 0}probe process("mydaemon").function("*").call {
if (!start) {
ignored[probefunc()] = 1
} else {
if ([probefunc()] in ignored) {
// do nothing
} else {
printf ("%s -> %s\n", thread_indent(1), probefunc())
}
}}
probe process("mydaemon").function("*").return {
if (start) {
if ([probefunc()] in ignored) {
} else {
printf ("%s <- %s\n", thread_indent(-1), probefunc())
}
}}
probe timer.s(5) {
start = 1
printf("Function call probing started.\n")}
probe end {
printf ("probing stopped.\n")}
這個腳本監控某個process,任其運行5s,把全部遇到的function call都存入ignored中,5s以後的函數調用關係會被打印出來。之因此選擇5s,是想屏蔽系統的噪音 - 初始化代碼,各類scheduling,time event代碼,打印真正的業務。因爲我工做的vm不穩定,業務跑不通,因此沒法抓到有效的數據。
整整試了一個早上(大概8:00-11:30),最終我放棄和系統抗爭。動態profile走不通,我決定pivot到不那麼sexy的靜態profile。靜態profile就是生成代碼的調用關係 —— 我最初打算作一個python腳本,用戶輸入一個函數名,我爲她生成兩張圖,一張是往前回溯,展現全部調用這個函數的完整路徑;另外一張是日後追溯,展現全部以它爲根的整個調用路徑。初見成效後,我便用django將生成的結果管理起來了。
要得到整個代碼庫的調用關係能夠寫yacc/lex進行語法分析,但單單作這一件事就要耗去不止一天時間。我想了不少其它方法,也在stackoverflow上處處尋找幫助,但都無疾而終。人在緊急狀況下會產生「急智」,就好像忽然開竅通常,我忽然想到了用cscope生成的索引文件cscope.out。非c/c++,或者非unix平臺下工做的程序員可能不知道cscope —— 其實只要你使用IDE,IDE就會生成代碼庫的索引,跟cscope原理基本同樣。我開始打算寫個python腳本分析cscope.out裏的內容,去尋找函數間的關係,但我沒能在cscope的官網上找到相關的文檔告訴我該怎麼作,也沒有找到操做它的API。退而求其次,我只好求助於cscope命令自己。使用過cscope的人大可能是用vim/emacs或者直接"cscope -d"使用,我想99%的人不知道cscope還能這麼用:
$ cscope -d -L2 <function-name> # print all callee symbols to stdout$ cscope -d -L3 <function-name> # print all caller symbols to stdout
這下我只須要寫個python腳本分析這兩個命令的輸出就OK了。經過分析輸出並不斷遞歸這個過程直到stdout沒有輸出(到達根節點,沒有調用關係了),我就能獲取某個函數的整個調用關係。固然,這麼寫代碼是權宜之計(workaround),效率很低(由於不斷地和cscope進程交互),因此真正高效的作法是找到合適的api,或者本身寫yacc/lex。
使用cscope.out的另一個好處是對代碼的分析能夠脫離代碼自己,任何一個裝有cscope的環境就能夠進行分析。
靜態profile我須要的其它「輪子」還包括graphviz(繪圖),django(存儲所生成的graph,提供web訪問方式)。其中一個function的callee可視化後是這個樣子(不用費心啦,你看不清我長什麼樣子滴^_^):
挺嚇人的吧 - 這原圖三十多兆,以我高大上的15" new mbp還繪製了半個小時。最終這些圖表可以在django作的website中展現出來,還能查詢。
前天晚上我寫了個多線程的腳本,8個核打滿,一夜直到把個人mbp電池耗盡才繪製了400多個函數的caller graph和callee graph。繪圖的效率過低,這performance也就頂多給hackathon作個演示。昨天我原本想修改一下代碼,先將中間結果保存在圖形數據庫neo4j裏面(使用neo4django),而後再考慮繪圖的事(或直接用jointjs展現到前端),惋惜時間不夠就放棄了,我還須要寫slides作pitch呢。
pitch
pitch是整個hackathon中最耀眼的環節,但又是最很差玩的環節。產品最終是要被展現出來的,這是我喜歡pitch的地方;但pitch的功利性太強,反而沖淡了hack一天帶來的愉悅感。
一位作產品的前輩曾跟我說:「作出來的產品要麼很受歡迎(大把粉絲),要麼有不少爭議(不少仇家)。無論怎樣,千萬不要作出來後你們以爲可有可無」。我雖然對此論調持保留意見,但我以爲這話用在pitch上比較對路。無論怎麼說,一個你們不聞不問的pitch不是好pitch。到了這一步,idea不酷不重要,demo不豐滿不重要,重要的是pitch要夠吸引人。這很悲哀,但也很現實 —— 和忽悠投資人是一個道理。
因此hackathon最後要多花些時間打磨pitch。我參加過的在北京舉行的techrunch hackathon最後team就敗在了pitch環節 —— 咱們頭腦發熱作了個情景舞臺劇模擬用戶的使用場景 —— 天知道當時你們是怎麼想的。因此儘管你作出了很cool的產品,若是pitch很差,吸引力仍是會大打折扣的。
我如今作pitch除非公司強制,不然絕對不會用powerpoint/keynote —— 並非這兩個工具很差,而是它們已經跟不上互聯網的思惟了 —— 演示文檔要隨時隨地可達,而且能夠嵌入任何內容。我本身通常使用reveal.js和impress.js。固然不是直接用,而是使用我本身結合wintersmith作的一套生成工具。這樣我能夠用markdown,jade,yml等格式撰寫slides,而後編譯成html,在chrome裏播放。此次pitch我使用的是impress.js。類prezi的炫麗展現效果加上裏面引用data visualization的一些高大上的圖(包括ben laden的這張開山鼻祖圖),足夠吸引觀衆的眼球。
結語
其實參加hackathon最有意思的事情是挑戰本身的能力極限,逼出本身的潛能 —— 這是hackathon向marathon致敬的地方。我我的感受重要的不是受到讚許或者別人注目的快樂,衝線那一刻的快樂,獲獎的快樂等等,這些快樂都沒法持續好久,也許次日起牀後你就忘記了;真正重要的是沉浸於其中,專一地圍繞着一個目標不斷學習不斷改進,最終交付的整個過程當中得到的快樂。我在參加此次hackathon以前從未用過 graphviz(pygraphviz),jointjs,neo4django等等工具,都是現看文檔現學習,很受用,很享受learning by doing的快樂。
題圖是baltimore hackathon的宣傳海報,我以爲它很好地反應了hackathon的精髓。
若是你對本若是你對本文感興趣,歡迎訂閱公衆號『程序人生』(搜索微信號 programmer_life)。每篇文章都力求原汁原味,早8點與您相會。