兩段實習經歷
學校又要評個什麽獎,要寫個人情況的材料(從小到大的那種,我擦)。一看要寫材料我就不想參加了,但有老師說學校行政部門看了我的材料可能會幹那件事,那我就寫一個吧。而且把從小到大的事寫出來挺有意思的,也發給大家康康。
下面是正文的一部分,講了我的兩段實習經歷,很好玩:
在初二的春天,一個群友說學校要做一個和程序語言有關的項目,他是參與者之一,來和大家討論討論想法。他說這個項目是“並行編程”,我想並行編程跟程序語言有啥關系,難道要把並發控制集成在語言特性裏-_-b?後來他一解釋,居然是研究一種高效地把串行程序通過分析自動轉換成並行程序的算法,也就是“自動並行化”。我知道折騰互斥鎖和信號量的痛苦,如果實現了這種技術,難道可以一勞永逸?這讓我不禁躍躍欲試。群裏的其他同學也是一樣,大家七嘴八舌地提出各種想法,但不少卻都被他否決,他說他們做的是“靜態分析”,要討論這個問題,你得知道靜態分析的基本原理。隨後在群裏發了幾篇充斥著各種詭異符號的論文。我一看頓時心裏打怵,雖然以前不是沒看過英文材料,但這好像確實超出了我的水平。
對於學習英語,有點有趣的事。小學時,爹媽怕我“輸在起跑線上”,不顧我的強烈反對給我報了英語班。這個英語班我一直上到初中畢業,是一個本地的“名師”在一二百人的大教室裏講課。這個“名師”是真的名師,授課的內容和水平都可以說相當高,課堂氣氛極其活躍。但這好像跟我沒啥關系,可能是因為不想學,也可能是別的什麽原因,他講的課我是真的聽不進去,就像個傻子一樣坐在那。每次考試在全班幾百人裏穩居倒數幾名-_-b。這樣時間久了,對人的打擊是可想而知的——我覺得我英語水平不行,甚至對英語學科產生了一些抵觸。
可是面對這麽有趣的項目,我實在不想放棄。只好硬著頭皮、用百度翻譯查詞去看那些鬼畫符論文,然後到google和wiki搜索裏面的各種名詞……這些技術有著非常廣博的前置知識——抽象代數、範疇論……雖然不用真的像數學系學生一樣把一本本專業書都啃會,但起碼得學很多東西才能知道這論文到底在說什麽。可想而知,能接受這種“苦行”的人沒幾個。之前在群裏一起激烈討論的小夥伴大多都忘了這件事了。但我一直在研究解決這個問題的思路。知道自己在做什麽是很重要的事,這就是研究——你在身邊發現一個問題,想知道為什麽。然後你就想去獲得解決這個問題的知識。你去看書,你去問專家,你上網去搜索。如果沒有發現答案,那麽好啦,你就可以自己試圖去發現為什麽,這是最有趣的部分。知道了為什麽,就想讓這個東西有用處,對人們的生活產生好處。這就是研究。我知道我要處理怎樣的代碼使其“並行化”,這樣就能按圖索驥,去找辦法解決處理這些程序中遇到的問題。
這個工作涉及到三個關鍵技術——數據流分析、抽象值執行(符號執行)和指針分析。數據流分析是生成並行代碼的基礎,它靠分析程序中變量之間的關系來生成一張“變量依賴圖”,顯然,不互相依賴的變量之間不存在數據競爭,是天然可以並行的。直接把它們分別抽取出來生成不同的block就行了。不過這裏面涉及到一些分析的中間結構,需要把原來的程序變換成另一種“形狀”才好處理。根據之前對函數式語言的學習,我參照CPS變換的思想,設計了一種結構,消除了中間變量,把操作(函數調用)串在了一起,相當於顯式地表達了“數據流圖”。這種中間結構有助於生成更高效的並行代碼。
數據流分析可以分析出“天然可並行的代碼”,那麽看起來似乎存在數據競爭的代碼能不能想辦法知道它們在什麽時候可以並行執行呢?這就涉及到符號執行技術,不過我更喜歡“抽象值”這個詞,因為它明確的表達出了算法在做什麽——即使兩段代碼依賴同一個變量,也不代表它們完全不能抽象成兩個block。比如當變量a為1時執行代碼塊A,a為2時執行代碼塊BC,其中BC沒有數據依賴關系。那麽其實,當a為2時,BC是可並行的(盡管它們從數據流圖中看起來都依賴於a)。想要完成這種優化,你需要知道在什麽情況下a的值會為2。這並不簡單,因為a可能從一個復雜的執行路徑裏被計算。靜態分析不能真正去精確執行這個路徑,只能使用抽象值來判斷一類情況——即,在“某類”輸入的情況下,這個路徑“一定”會往哪走,最終使得a的值“一定”為2。然後在這種情況下,進行BC並行的優化。這是靜態分析領域的前沿技術。
了解了這些之後,我知道應該怎麽做了,並設計了合理的算法。那個同學看我這麽短時間就把這個算法設計到了這種程度,十分驚訝。他說他這段時間一直在上課-_-b。看我已經做成這樣了,他邀請我加入他們的團隊,我們一起完成這個項目。這時我才知道,這個項目來自計算機科學的重鎮,華盛頓大學:P
然後我來到了華盛頓大學的PLSE(程序語言與軟件工程)實驗室。顯然,作為跟學校完全無關的人,我拿的是一個類似科研助理(RA)的offer。但因為我可能是目前團隊裏對這個問題最懂的(不知道學校老師都去幹嘛了-_-b)所以這個項目由我挑大梁。學校的同學拿給我了實驗室做算法實驗用的一個mini命令式語言的parser和基礎設施,我將在這個語言上實現我的算法。首先我先把我的算法針對值類型實現,然後再分析引入指針的情況。我認為指針的情況只是對值類型生成的數據流圖進行一些修改,使得圖上可以反應指針指向的變更而已。整體大框架是一樣的。因此我寫了一個接口,可以引入指針分析的結果,然後學校的一個PhD學生完成了指針分析部分(主要是我不會指針分析-_-b)。
這套算法可以把串行代碼生成為並行block,並將這些block(以及調度它們的connection)生成到PLSE開發的IR。其實這套系統還缺個後端,來把這些並行block生成為實際的CUDA/openCL代碼。不過沒人想寫這個後端(雖然如果有了這個後端,其它用這個IR的項目也會受益。但大家都忙著鼓搗論文,我也懶得學CUDA-_-b),所以沒法真的去跑個實驗來測試它的性能。但看拆分出的block,可以說相當不錯。而且我設置了可調的分割粒度,因此可以通過調節這個粒度參數來解決block拆分太細/符號執行優化太多導致的並行開銷問題。
這個項目到這裏就結束了。當年開工之前,忘了有誰畫餅,說這個項目能發POPL。最後顯然是沒發,我也只得到了1000$的part-time報酬(兩個月麥當勞端盤子掙的都比這多-_-b)。它真正發揮威力還要等到2019年,有個同學告訴我,當年的這個東西現在已經改頭換面了,算法被集成到了一個函數式語言、DSL。成為了愛丁堡大學主導,大力宣傳的“並行優化語言”,我雖然覺得這個東西不錯,不過也看不到它值得這麽宣傳的意義-_-b。這個語言倒是發文章了,不過作者沒有我,也沒有當年我們團隊的任何一個人(因為我們的項目相當於給到UOE了,只是他們那個語言項目的一部分),不過似乎是有不少大牛。發的是什麽我也沒太關心,不過看那架勢大概是個頂會……讓我掛個名也好啊,雖然我根本不想關心這個語言-_-b
這段時間給我最大的意義是,我感覺我的心智大門被開啟了。我開始嘗試從來沒有做過的事情,以及從來不認為我能做好的事情。我曾經以為我不擅長英語,理解不了復雜的數學概念。但其實我能做到。原來只要有了問題和探索的精神,就會有動力去獲得解決它所需要的知識,最後將問題解決。另外我還感受到了許多其它的知識——PLSE的大多數團隊在研究函數式語言,通過了解函數式語言的歷史,我發現了很多和以前認識不一樣的東西,比如Prolog——一種語言可以不是用來“編程”的,而是用來描述關系、推理關系、實現“人工智能”的。它是怎麽做到這一點的?它的實現和正常的語言有什麽區別?我開始思考這些問題。還接觸到了“柯裏-霍華德同構”,知道了對程序語言的類型檢查等價於一個邏輯證明,而這種邏輯的表達能力取決於程序語言的類型系統——這不得不說很有意思,每天都在用的類型檢查居然可以和那些玄妙的東西有著同樣的原理,這就是理論研究的神奇之處了。我發現我所感興趣的不只是能實在作用於社會的東西,理論工作也有著相應的美感。笛卡爾說“要掌握科學就要掌握它的全部”,這句話真合我心意,我就是想做一個懂很多東西的人啊。
過了一段時間,又一個群裏的朋友聯系我,說他現在在一個量化基金公司當技術兼HR,現在在招C++,問我有沒有興趣去他們的公司做高頻交易工程師。我悄悄地告訴他,其實我是個中學生-_-b。他楞了一會,說那就當假期實習吧,現在好的C++工程師真的難招……呃,原來是因為招不到人才找我嗎-_-b……不過我還是同意了。量化交易也是我心中的一個傳說,盡管已經脫離了把股票市場當提款機那個級別的認知,但用一種程序自動交易,居然能賺到錢,確實還是挺神奇個事。
進了團隊之後先被鈔能力震驚——高頻交易不是誰都能做的,公司的核心通信機組建在離上交所很近的地方(和其它公司共用機房),而且用的是價值不菲的微波通信。高頻交易對速度要求的恐怖可見一斑。然後我發現,給我所謂的“高頻交易工程師”其實是“高頻交易系統工程師”——我做的是交易系統架構。之前交易系統的時延無法滿足需求,因此現在他們把linux魔改成了另一個樣子,拆掉了linux內核的調度模塊,自己搞了一個一個進程獨占一個核的簡單粗暴但有用的架構——沒有上下文切換顯然減少了時延,而且提高了cache命中率。不過既然改了系統,中間層也得做相應變更——不想要上下文切換開銷不代表永遠就跑那幾個進程不調度了。一些時候需要緊急更改策略,比如市場出現了某種異象,再或者就是到時間了,要平倉了,反正需要另一個進程中斷進來。我就負責編寫這個“超輕量調度模塊”,這東西說原理簡單,但要真的編出來需要知道怎麽在linux底層進行各種彎彎繞,還得對接之前團隊魔改過的部分。我對linux系統編程可以說一竅不通,當時東問西問好不容易交差。
不過接受這份工作,我想了解的是量化交易的核心知識,不是怎麽實現低時延系統,而是到底用什麽策略進行交易。於是我在團隊裏開展各種偵探工作,首先知道的是高頻交易雖然看起來最貴,但從算法的角度來說,其實是技術含量最低的算法——原因你可以猜到。然後要偵探的是中低頻算法,這裏就涉及到正兒八經的金融工程知識了。我又開始了一輪補課,最終順利理解了一部分。這些知識糾正了我的一個誤解——我一直以為量化交易的核心是建模博弈,其實並不是,建模博弈需要很強的假設。市場的噪音也不允許這種建模。量化交易是對統計規律建模,這大概是計量經濟學的基本知識,不過當時確實是糾正了我長久以來的錯覺。
統計規律也不能胡亂建模,需要註意社會問題的復雜性。否則看起來把數據擬合的很好,但得到的都是垃圾。計量經濟學和量化交易建模的基本原則就是:用“強但穩健”的假設,顯然,假設強了,在市場那麽復雜的環境中,就會影響擬合精度,影響擬合精度還要穩健,就要求不能直接用“純數值量”做出決策,而要用“狀態量”。這也是實盤量化交易模型很少去直接預測股價的原因,如果你直接根據你預測的股價交易,那很多時候就會………………當然了,可以使用各種受過檢驗的經濟學定價模型,不過那並不是交易系統的全部。構造合理的假設需要什麽呢?負責策略的同事大概是這麽表達的(借用一下教科書上的段落):“金融市場,本質上是實體經濟的衍生,長期上,受經濟基本面、公司財務狀況、國際貿易和技術進步影響,短期內,受證券的供需關系、交易成本、法規監管、市場結構限制。理解金融市場的運作原理,理解經濟學基本模型和理論,應該是投資和交易的根基。缺乏經濟學常識、只懂回歸分析、隨機偏微分方程等等,或許能一時成功,然而,在真正危機的時候,把所有收益全都吐出來……”
我另外的學到的一個重要的東西就是——信任你的策略。為什麽要衡量策略的“最大回撤”?這不僅提防的是策略的風險,某些方面更重要的一點是在提放人心。人們看著自己的策略虧錢的時候,其實是很脆弱的,甚至總是管不住手去幹預機器進行交易。但是這個時候基本越幹預越虧錢,因為能上實盤的策略都是經過足夠測試的,而人的“脆弱”並沒有測試,在某種程度上,人並沒有機器穩定。因此真正的策略工程師極少去幹預策略,甚至在基金的匯率換算協議裏寫明“所有交易動作都由系統自主發出”。學到了這個道理,日後我再接觸股票基金,就很少去“研究局勢”,因為我沒有時間,而且研究也研究不過別人,毫無意義。索性就使用最簡單最穩健的策略——MA、甚至均勻采樣。就算策略再簡單再菜,只要不極致反向操作,從基本面上來講都是賺的。所以就讓程序自己幫我弄得了,我連看都不看,省時省力。這可能也是我一直都沒虧的原因;-P。
因為真正的交易策略是公司的機密(而且我也不知道具體的策略-_-b),所以只能說點這種形而上的,這個話題就到這裏吧。