第72章 模擬器更新(5.1k大章)

投票推薦 加入書籤 小說報錯

  第72章 模擬器更新(5.1k大章)

  斯坦威警長在FBI工作,從這警銜就能看出對方的地位,工薪在13萬到18萬美刀之間0

  實際上美利堅聯邦調查局只是稱警長,職銜正常來說,是屬於高級探員,至於主管和助理局長就都是文職一類,在美利堅複雜的官僚調度體系里難以琢磨,不過肉眼可見的也是地位很高。

  陳哲本來以為對方也是留學生,可是照現在這麼看,這傢伙反而是個土生土長的美國人!

  「我對此深感惋惜。」

  陳哲心中思緒起伏,表情沒什麼波折。

  「沒關係,也不是我第一次跟別人提起這件事了。」書蟲擺了擺手,聲音低沉。

  陳哲拍了拍對方的肩膀。

  他並不打算利用書蟲的身份做些什麼,和對方交談片刻之後,就整理好了自己腦中的所想,回到了聚會之中。

  聚會下半場,本端著咖啡回來的時候,氣氛已經松下來了。七八個人散坐在草坪上,有的靠著樹幹,有的盤腿坐在枯黃的草上,有的趴在野餐桌邊刷手機。陽光從雲層後面露出來,把整片草坪曬出一層薄薄的暖意。

  本沒坐下。他站在野餐桌旁邊,喝了一口咖啡,目光從人群里掃過,最後落在陳哲身上。

  「陳,」他忽然開口,「上次在綠點區跟你聊的時候,有個問題你沒答完。」

  陳哲抬起頭。

  本把咖啡放下,走到桌邊,從背包里翻出一支馬克筆,在白板——其實就是一張A3紙上寫了一行字。

  「Python的GIL,你怎麼看?」

  桌上幾個人抬起頭。提米的手指停在鍵盤上,萊拉把可樂罐放下,全民超人從樹幹上直起身子。

  這一句話的信息量極大。

  「又是出難題的時候了。」萊拉低聲說。

  「這次是針對陳一個人的?」

  「看來他倆線下已經單獨約見過了。」

  「群主對有潛力的新人真是上心————」

  席中短暫騷動隻言片語。

  陳哲想了想:「GIL是CPython的全局解釋器鎖,同一時刻只有一個線程執行Python字節碼。」

  本點點頭,等他繼續。

  「所以多線程CPU密集型任務在Python里是偽並行,」陳哲說,「只能跑在一個核心上。I0密集型可以用多線程,因為有阻塞等待,GIL會釋放。」

  本沒評價,又寫了一行字。

  「怎麼繞過GIL?」

  陳哲答:「多進程。用multiprocessing模塊,每個進程有自己的解釋器和GIL,能跑滿多核。或者用C擴展,把計算密集的部分用C寫,在C層面釋放GIL。還有asyncio,協程,適合I0密集型但不是CPU。

  本又寫了一行。

  「那你在實際項目里用過asyncio嗎?」

  陳哲想了想,搖頭:「用過一點。不過有一次寫異步爬蟲,aiohttp搭配asyncio,跑起來之後發現有些請求沒發出去。後來查了半天,發現是事件循環里有個地方忘了await。調了一天才找到。」

  提米在旁邊笑了一聲:「我懂。我第一次用asyncio的時候,在代碼里寫了個time.sleep,整個事件循環都卡住了。後來才知道要用asyncio.sleep。

  「7

  幾個人笑了。氣氛鬆快了一點。

  本沒笑。他看著陳哲,又寫了一行字。

  「那GIL在什麼情況下是真正的瓶頸?」

  陳哲的手指微微緊了一下。這個問題的方向和他預想的不太一樣——不是基礎題,是在問真實場景的邊界判斷。

  他想了想,開口:「如果是純CPU計算,單線程已經跑滿一個核心,GIL就是瓶頸。比如科學計算、圖像處理、機器學習訓練這些場景。但那種情況一般用numpy,它底層是C,不在Python層面算。」

  本等著他繼續。

  「如果是混合場景,」陳哲說,「計算加I0,比如Web服務,GIL的影響要看請求量和每個請求的計算占比。QPS不高的時候,GIL不是問題。QPS上去之後,每個請求的計算時間只要超過幾十毫秒,GIL就會開始卡。」


  他頓了頓。

  「具體閾值要看業務。一般估算的話,單核能撐的QPS上限除以CPU占比,再除以並發係數。比如單核能撐1000QPS純I0,如果每個請求有10%的CPU計算,那實際能撐的QPS大概在100左右。超過這個數就需要多進程或者換語言。」

  本聽完,沒說話。

  他低頭在白板上又寫了一行字。這次寫得很慢,像是在斟酌什麼。

  「你寫過多線程的生產級代碼嗎?」

  陳哲看著那行字,沉默了兩秒。

  然後他搖了搖頭。

  「沒有。我寫的都是單機腳本和Web後端。多線程只在demo里跑過,沒上過生產。」

  本的眉頭微微動了一下。

  他盯著陳哲看了兩秒,然後問了一個更細的問題:「如果讓你設計一個多線程的爬蟲系統,抓取一萬個網頁,你會怎麼處理線程池的大小?」

  陳哲想了想,開口:「先看瓶頸在哪。如果是I0阻塞,線程池大小可以設大一點,一般設100到200。但也要看目標網站的承受能力,不能把人家的伺服器打崩了。所以要用信號量限流,或者用隊列控制並發數。」

  「如果伺服器返回429呢?」

  「加退避。指數退避,第一次等1秒,第二次等2秒,第三次等4秒。如果連續失敗超過三次,就把這個URL丟回隊列,等後面再重試。」

  本又問:「那如果隊列滿了呢?」

  「滿了就阻塞生產者。或者用有界隊列,滿了之後生產者等待,等消費者空出位置。」

  本的眉頭沒有鬆開。

  他又寫了一行字:「你怎麼保證每個線程拿到的URL不會重複?」

  陳哲的手指在膝蓋上輕輕敲了一下。這個問題比他預想的深一不是問怎麼去重,是問分布式的去重。

  「用布隆過濾器。」他說,「每個線程拿URL之前先過一遍布隆過濾器,已經爬過的就跳過。布隆過濾器可以用Redis的bitmap實現,多個線程共享。誤差率可以通過哈希函數個數和位數組大小控制,一般能接受千分之一的誤判,少爬幾個頁面問題不大。」

  本點了點頭。但他沒停。

  「如果布隆過濾器誤判漏了一個重要頁面呢?」

  陳哲這次真的停住了。

  他沉默了兩秒,然後開口,聲音很平。

  「用確定性去重做備份。布隆過濾器只是第一層過濾,漏掉的頁面可以靠第二層校驗,比如把URL的哈希存在Redis的Set里,精確去重。但Set的內存占用太大,所以可以用布隆過濾器做預篩選,Set做兜底。」

  本盯著他,又問:「那如果Set也扛不住呢?」

  陳哲的手指停住了。

  他看著本的眼睛,沉默了三秒。

  然後他嘆了口氣,聲音裡帶著一點無奈,又好像只是很坦誠。

  「這個我不知道了。」

  他頓了頓,又說了一遍,語氣更輕,像是自言自語。

  「學藝不精。沒做過這麼大的量,沒碰到過這種級別的瓶頸。」

  本的目光在他臉上停了兩秒,像是想確認什麼。

  桌上安靜了片刻。

  提米在旁邊嘖了一聲,替陳哲解圍:「本,你這題出得也太偏了。分布式爬蟲的去重方案,那是架構師才需要考慮的問題。他一個剛入行的新人,能想到布隆過濾器加Set兜底已經很不錯了。」

  全民超人點頭:「就是。你問他GIL、問線程池、問退避策略,這些還算正常。問到分布式去重,那已經超出普通開發的範圍了。」

  萊拉也幫腔:「我工作三年了,也沒設計過這種量級的系統。平時業務里碰到這種需求,直接用現成的消息隊列和分布式框架,誰會自己造輪子?」

  本沒說話,只是看著陳哲。

  陳哲靠回椅背上,端起那杯涼透的咖啡,喝了一口。苦的。他的表情很坦然,像是一個真的不會的人。

  「確實不會。」他又說了一遍,聲音很穩,沒有辯解的意思。

  CodeMaster—US從桌子另一頭站起來,走到這邊,看了一眼白板上那行字。


  他的眉毛挑了一下,然後看著陳哲,開口說:「你能想到布隆過濾器加Set已經夠了。我面試別人的時候,大多數人連布隆過濾器都說不清楚。分布式去重本身就是個系統工程問題,不是一兩句話能說清的。」

  本聽到這句話,眉毛動了一下。

  他看了看CodeMaster—US,又看了看陳哲,嘴角動了動,想說什麼,但最後還是咽了回去。

  「行。」他說,把白板翻過去,「這題算我出難了。

  「7

  他端起咖啡喝了一口,目光在陳哲身上停了一秒,然後移開。

  陳哲嘴角微微上揚,隱去了最關鍵的一步。

  那一步,是只有在公司上過班的程式設計師才能得知的一些職業寫法,既然不應該出現在他這個大學生的身上,那他就不會說出口去。

  聚會散場的時候,天已經暗下來了。

  陳哲站在公園門口,看著那群人三三兩兩地消失在街角。麥克拍了拍他的肩膀,說了句「改天約酒」,然後往地鐵站的方向走了。提米和萊拉湊在一起商量去哪兒吃飯,邊走邊爭論哪家店的披薩正宗。全民超人一個人往河邊走,耳機塞在耳朵里,步子很慢。書蟲不知道什麼時候已經走了,陳哲回頭找他的時候,只看見一個黑色的背影消失在街角。

  本最後走的。他站在野餐桌旁邊,把那塊白板收起來,塞進背包里。看見陳哲還站在門口,走過來,在他面前站定。

  「你今天答得不錯。」他說,「尤其是分布式去重那部分。能想到布隆過濾器加Set

  兜底,說明你確實有系統設計的意識。至於再往上,那是經驗問題,急不來。」

  陳哲點點頭:「我知道。」

  本盯著他看了兩秒,目光從他臉上掃過,忽然問了一句:「你剛才說的那些線程池、退避策略、布隆過濾器都是自己在網上學的?」

  「對。」陳哲說,「看書,看博客,看開源項目。」

  本沒說話,只是點了點頭,轉身往街邊走。走了幾步,又停下來,回頭看著他。

  「你那個YouTube頻道,」他說,「我看過了。」

  陳哲的手指微微緊了一下。

  「第六期那集文件操作講得不錯。」本說,語氣很平,「付費視頻我也買了。質量對得起價格。」

  他頓了頓,目光在陳哲臉上停了一秒。

  「繼續做。別停。」

  說完,他轉身走了。

  陳哲站在原地,看著他的背影消失在街角,然後轉身往公交站走。

  回到公寓的時候,傑姆尼正躺在沙發上看手機。電視開著,放的是某個他叫不上名字的cult血漿片,聲音開得很小。他看見陳哲進來,把手機放下,從沙發上坐起來。

  ——

  「回來了?今天的事怎麼樣?」

  「還行。」陳哲把背包扔在沙發上,在另一邊坐下。

  傑姆尼盯著他看了兩秒:「你看起來有點累。」

  「有點。」

  傑姆尼沒再問,從茶几下面摸出兩個啞鈴,開始做彎舉。他的動作比前幾天標準了一點,但還是很生硬,每次舉起來的時候肩膀都會不自覺地往上聳。陳哲在旁邊看了一會兒,忽然開口:「肩膀放鬆。不要用慣性,用肌肉控制。」

  傑姆尼愣了一下,調整了一下姿勢。這次好了一點,但還是不太對。陳哲站起來,走到他旁邊,伸手按了按他的肩膀:「沉下去。不要聳肩。肘關節夾在身體兩側,不要往外撇。」

  傑姆尼照做了。這次動作明顯順了很多,二頭肌鼓起來,手臂上的血管凸出皮膚表面。他做了十五個,把啞鈴放下,活動了一下肩膀。

  「舒服多了。」他說,「陳,你怎麼這麼懂?」

  「但凡有一點鍛鍊經驗都知道的吧,你不會上網查麼?」

  傑姆尼嘖了一聲,從沙發上站起來,開始做伏地挺身。這次他沒等陳哲糾正,自己調整了一下手的位置和背的角度。做了二十個,站起來,拍了拍手上的灰。

  「陳,」他說,「忘了說————你那個槍,今天打完之後我擦過了。彈殼也撿了,扔在河邊的垃圾桶里。放心。」

  陳哲點點頭:「謝了。」


  傑姆尼該說的話也說完了,也就搖頭晃腦地回了房間。

  客廳安靜下來。陳哲靠在沙發上,盯著天花板。

  休息了一會兒,陳哲站起來,走到桌邊,打開那台二手ThinkPad。屏幕亮起來的時候,他習慣性地先看了一眼右下角,模擬器的圖標閃爍。

  陳哲盯著軟體圖標看了幾秒,手指在觸控板上懸著,沒有點下去。窗外傳來警笛聲,由遠及近,又由近及遠,消失在不知道哪個街區。

  他收回目光,關掉模擬器窗口,打開群聊。

  【布魯克林區程式設計師交流群】

  【小王子:@陳,今天聚會沒聊夠啊,本那個分布式去重的題你後面想明白了嗎?】

  【全民超人:那題本來就是架構師面試用的,他能想到布隆過濾器加Set已經很不錯了。】

  【愛來自冒險家協會:+1。我工作三年了,也沒設計過那種量級的系統。】

  【麥克:@陳,別放在心上,本就是那種人,對新人特別嚴格。】

  陳哲靠在椅背上,開始打字。

  【陳:沒事。本上次單獨約我聊過一次,可能對我期望比較高。】

  群里安靜了一秒。

  【小王子:???】

  【愛來自冒險家協會:單獨約聊?什麼時候的事?】

  【陳:上周末。在綠點區,他家裡。】

  【全民超人:————】

  【麥克:臥槽,本一般不單獨見人。】

  【愛來自冒險家協會:他跟你聊了什麼?】

  陳哲想了想,打字。

  【陳:聊了聊技術方向。看了我一些代碼。喝了杯咖啡。】

  【小王子:就這些?】

  【陳:就這些。】

  【全民超人:本這個人眼光很高的。他願意單獨約你,說明他覺得你有東西。】

  【麥克:+1。我跟他認識三年了,他從來沒單獨約過我。】

  【小王子:+1】

  【愛來自冒險家協會:+1】

  一如既往的震驚。

  陳哲盯著屏幕,嘴角動了一下,大概過一會兒就能看到本在群里的認證了,反正這事在聚會裡已經暴露過了,那麼證實一下他們的揣測,反而是對自己百利無一害的,是增加聲望的行為。

  他關掉群聊,靠在椅背上。窗外的天已經完全黑了,只有對面那棟老公寓樓上還亮著幾扇窗,橘黃色的光透出來,在夜色里暈成模糊的一團。

  傑姆尼房間裡傳來他鍛鍊的聲音,呼哧呼哧的喘息,夾雜著計數聲。

  陳哲聽著那個聲音,忽然站起來,走到客廳中間,趴下去,開始做伏地挺身。

  一個,兩個,三個。動作很慢,下去的時候胸口幾乎貼著地板,起來的時候手臂完全伸直。他做了二十個,呼吸開始變重。做到四十個的時候,汗從額頭滴下來,砸在地板上,發出輕微的啪嗒聲。做到六十個的時候,手臂開始發抖。

  八十個。他咬著牙,臉上的肌肉繃緊,額頭上青筋暴起。一百個。他撐在地上,胸口離地板只剩一拳的距離,起不來了。

  他趴在地板上,大口喘氣。汗從下巴滴在地上,在灰白色的地板上洇出深色的一小片。

  一百個伏地挺身,這就不是神經募集能力能涉足的領域了,是純粹的肌肉耐性問題。

  陳哲能感覺到自己一開始做得輕鬆,但是後面倒也沒有多少疲憊的時候就開始喘息,可見還是身體的硬體沒能跟上多少。

  而最為重點的,是陳哲已經許久沒有和互助會聯絡,那個作家弗蘭克也沒有在私人聊天的界面里和他有多少交談。

  陳哲想了想,滑動觸控板,還是打開人生重開模擬器。

  只是出乎他意料的是,呈現在眼前的,並不是新增加的模擬次數,而是模擬器更新了版本。

章節目錄