大學(xué)的課程,依舊是那副雷打不動的樣子,按部就班。
對黎陽而言,白天的時光,除了必須應(yīng)付的課堂,他就像一顆釘子,牢牢釘在了圖書館。
一來,維持“學(xué)霸”人設(shè)不能崩,得讓同學(xué)們覺得他熱愛學(xué)習(xí)、天天向上。
二來嘛,圖書館確實安靜,沒人打擾,正好方便他梳理腦子里那些來自未來的技術(shù)碎片,以及“靈犀”下一步該怎么走的開發(fā)思路。
當(dāng)然,他真正的主戰(zhàn)場,永遠(yuǎn)屬于宿舍熄燈后,那片深邃的、只屬于他一個人的黑夜!
又經(jīng)過幾個晚上的秘密奮戰(zhàn),“靈犀”App的客戶端原型,已經(jīng)不再是當(dāng)初那個簡陋的架子了。
核心聊天界面?有了。 聯(lián)系人列表?雖然現(xiàn)在只有一個孤零零的AI“靈犀”,但也像模像樣了。 簡單的設(shè)置頁面?也搞定了。
一個App該有的基本骨架,算是搭起來了。
但黎陽怎么可能滿足于此?
他太清楚了!
聊天App,什么最重要? 用戶體驗! 而聊天記錄的存儲和加載性能,就是用戶體驗的命脈! 沒人會喜歡一個每次打開聊天都要卡半天、轉(zhuǎn)圈圈的垃圾App! 尤其是在2015年! 這個安卓生態(tài)還比較混亂,低端機(jī)性能普遍拉胯的年代,數(shù)據(jù)庫I/O簡直是性能重災(zāi)區(qū)!
這不,今晚,黎陽就一頭撞上了這塊硬骨頭。
他隨手寫了個腳本,生成了幾千條模擬的聊天記錄數(shù)據(jù),塞進(jìn)了測試數(shù)據(jù)庫。
然后,在App里,他輕輕一點(diǎn),嘗試進(jìn)入那個塞滿了數(shù)據(jù)的聊天界面。
結(jié)果…… 慘不忍睹! 他那臺屏幕都快包漿的老舊安卓測試機(jī),仿佛瞬間被注入了水泥! 屏幕卡頓! 滑動遲滯! 操作響應(yīng)慢得像得了帕金森!
足足過了好幾秒鐘,仿佛一個世紀(jì)那么漫長,聊天記錄才如同擠牙膏一般,慢吞吞、一行一行地“吐”了出來。
“草!果然還是這么拉胯!” 黎陽皺緊了眉頭,低聲罵了一句。
他用的,是最基礎(chǔ)、最簡單、也是大學(xué)教材里唯一會教的那種SQLite操作方式——一條樸實無華的SELECT *,沒有任何優(yōu)化技巧可言。
如果是這個時代一個真正的大一新生,碰到這種情況? 多半直接懵逼! 要么束手無策,要么怪手機(jī)太破,要么干脆擺爛,覺得“就這樣吧”。
但! 他是黎陽! 一個擁有未來十年大廠頂尖開發(fā)經(jīng)驗的老鳥! 這個問題,在他眼里,簡直就是送分題! 腦海中,無數(shù)個前世踩過的坑、用過的優(yōu)化方案,瞬間如同彈幕般刷過!
“哼,SELECT *?蠢貨才這么寫!必須只查需要的字段!”
“message_id和timestamp,這兩個查詢最頻繁的字段,必須加索引!這都不懂還寫個屁的代碼!”
“幾千條數(shù)據(jù)一次性懟進(jìn)內(nèi)存?手機(jī)不卡死才怪!分頁加載!必須分頁!每次只加載一屏!”
“二次打開速度也不能慢!內(nèi)存緩存!搞個LRU緩存最近聯(lián)系人的消息!”
“序列化方式……嗯,這個暫時可以放放,先把前面幾個搞定。”
一個個優(yōu)化點(diǎn),清晰無比,如同黑夜里的燈塔,瞬間照亮了解決問題的康莊大道! 這些知識,對2015年的學(xué)生來說,簡直就是降維打擊!是另一個次元的技術(shù)!
沒有絲毫猶豫! 干就完了! 黎陽立刻動手,手指在鍵盤上化作幻影!
改數(shù)據(jù)庫表結(jié)構(gòu),加索引。 寫分頁查詢邏輯,LIMIT、OFFSET,安排上。 實現(xiàn)簡單的內(nèi)存LRU緩存機(jī)制。小菜一碟!
代碼如同決堤的洪水,從他指尖洶涌而出,行云流水,一氣呵成! 偶爾遇到某個API的細(xì)節(jié)用法有點(diǎn)模糊(畢竟是幾年前的老版本API),他甚至懶得去翻墻Google,直接打開提前下載到本地的離線Android開發(fā)者文檔,或者在后臺運(yùn)行的藍(lán)鯨AI控制臺里飛快敲下一行:
> SQLite Android API 21, 索引創(chuàng)建最佳實踐語法確認(rèn)。
AI幾乎是毫秒級響應(yīng),給出了標(biāo)準(zhǔn)的代碼片段。
黎陽掃了一眼,確認(rèn)無誤,然后無縫銜接,繼續(xù)編碼。
整個過程,專注、高效,充滿了對技術(shù)的絕對掌控感!
就在黎陽全神貫注,調(diào)試著優(yōu)化后的數(shù)據(jù)庫查詢代碼,測試分頁加載的流暢度時——
“窸窸窣窣……”
上鋪突然傳來一陣輕微的響動。
緊接著,一個睡眼惺忪的腦袋探了下來。
是陳東。 他似乎是被鍵盤聲或者椅子挪動的聲音吵醒了,揉著眼睛,迷迷糊糊地問: “唔……黎陽?你還沒睡?。俊@都快兩點(diǎn)了,你在搞啥呢?”
黎陽手上動作不停,頭也沒抬,只是笑了笑: “哦,睡不著,隨便寫點(diǎn)東西。吵到你了?”
“沒,沒有……”陳東打了個哈欠,但好奇心顯然被勾起來了,“我就是醒了,看你這燈還亮著……”
說著,他干脆撐起身子,目光不由自主地落在了黎陽那臺老舊筆記本的屏幕上。
屏幕上,是密密麻麻的、閃爍著各種顏色的代碼。 其中夾雜著一些他勉強(qiáng)認(rèn)識,但更多是完全看不懂的單詞:SQLite, Index, Cache, LIMIT, OFFSET……
“臥槽?” 陳東的睡意瞬間消散了一半,驚訝地瞪大了眼睛,“你…你這是在搞數(shù)據(jù)庫?!” 他忍不住好奇,直接從上鋪爬了下來,湊到黎陽電腦旁邊。
要知道,他們的C語言課才剛剛講到指針,數(shù)據(jù)庫這玩意兒,對他們這些大一新生來說,基本還停留在“聽說過”的層面,遙遠(yuǎn)得不行。
“嗯?!崩桕柦K于停下了手,抬起頭,露出一絲恰到好處的“疲憊”笑容,“是啊,優(yōu)化一下聊天記錄的加載速度,之前寫的太爛了,卡得要死?!?/p>
來了! 黎陽心中暗道。 這是一個完美的,在陳東面前“不經(jīng)意”地展露冰山一角的機(jī)會!
他故意放慢了操作速度,一邊看似在檢查代碼,一邊用一種“分享經(jīng)驗”的語氣,對旁邊的陳東“科普”起來:
“你看啊,陳東,之前我傻乎乎地直接SELECT ,把所有聊天記錄一次性全查出來,數(shù)據(jù)少還行,一旦多了,幾千條記錄,手機(jī)內(nèi)存直接爆炸,CPU也跟著干爆,不卡才怪?!?/p>
“所以呢,首先,查詢的時候,絕對不能偷懶用,要明確指定你需要的字段,比如message_id, sender, content, timestamp這幾個,減少數(shù)據(jù)傳輸量?!?“然后,你看這里,”
他指著屏幕上剛加的CREATE INDEX語句,“要給經(jīng)常用于查詢條件的字段,比如這個message_id和timestamp,加上索引。這玩意兒就像書的目錄,數(shù)據(jù)庫能通過索引,唰一下就定位到你要的數(shù)據(jù),而不是傻乎乎地一頁一頁翻。”
“還有這里,” 他切換到另一個Java文件,展示著帶有LIMIT和OFFSET子句的查詢方法,
“一次加載幾千條,手機(jī)肯定扛不住。所以要做分頁加載,比如用戶滾動屏幕,快到底部的時候,再加載后面20條、30條,這樣壓力就小多了?!?/p>
陳東站在旁邊,聽得是云里霧里,似懂非懂。 但他敏銳地捕捉到了幾個關(guān)鍵詞:“內(nèi)存爆炸”、“CPU干爆”、“索引”、“分頁加載”……
這些詞匯,聽起來就……就很牛逼! 而且,黎陽講解時那種深入淺出、信手拈來的自信,以及他敲代碼時那種行云流水般的熟練度……
這特么……這特么真的是一個剛學(xué)編程幾個月的大一新生?! 陳東的世界觀受到了億點(diǎn)點(diǎn)沖擊。
就在這時,黎陽“恰到好處”地停了下來,眉頭微微皺起,盯著屏幕上的一段查詢代碼,開始喃喃自語,聲音不大不小,正好能讓陳東聽見:
“咦?奇怪了……按理說,加了索引,也做了分頁,這里的查詢速度應(yīng)該起飛了才對啊……怎么模擬器跑起來,感覺還是……還是有點(diǎn)慢?沒達(dá)到想象中的效果……”
他裝出一副遇到了技術(shù)瓶頸的樣子,開始煞有介事地進(jìn)行“調(diào)試”。
先是在代碼里加了幾行打印日志的代碼,看看查詢到底耗時多少毫秒。 然后又仔細(xì)檢查SQL語句的拼寫,確認(rèn)WHERE子句的邏輯沒問題。
接著,又切換到剛剛寫的那個簡陋的內(nèi)存緩存代碼,“猜測”道:“難道是緩存命中率太低?還是緩存的key設(shè)計有問題?”
陳東在一旁看著黎陽眉頭緊鎖、手指在鍵盤上飛快敲打的樣子,也跟著緊張起來。
雖然他完全看不懂,也幫不上任何忙,但還是忍不住湊近了些,憑著自己僅有的一點(diǎn)點(diǎn)C語言經(jīng)驗,小聲地、試探性地提議: “呃……黎陽,會不會是……哪個變量名寫錯了?或者……或者少了個分號?”
這是他調(diào)試C語言代碼時最常用的“玄學(xué)”技巧。
“或者……要不,你重啟一下模擬器試試?有時候重啟就好了……”
黎陽回頭看了他一眼,嘴角勾起一絲微不可查的笑意,但臉上還是那副“苦苦思索”的表情,搖了搖頭: “語法編譯器都檢查過了,應(yīng)該沒拼錯。重啟大法……治標(biāo)不治本,這肯定是哪個更深層次的邏輯,或者索引應(yīng)用上的細(xì)節(jié)出了問題?!?/p>
他又埋頭“苦思冥想”了十幾秒,手指在鍵盤上象征性地敲了幾下,似乎在嘗試不同的查詢條件。
同時,他飛快地瞥了一眼一直開著的藍(lán)鯨AI控制臺——其實他早就知道問題所在,但戲要做全套。
他假裝剛剛在AI那里問了個問題(實際上可能只是敲了個回車),然后猛地“眼睛一亮”,仿佛醍醐灌頂,輕輕一拍大腿:
“臥槽!我知道了!” 聲音不大,但在寂靜的宿舍里卻格外清晰。
陳東被他嚇了一跳,連忙問:“怎么了?找到問題了?”
“嗯!” 黎陽臉上露出“恍然大悟”的表情,指著一行代碼,快速說道:“問題出在這個查詢條件,user_id。
我在代碼里傳進(jìn)來的是字符串類型,但是數(shù)據(jù)庫表里定義的字段是整型(INTEGER)!”
“數(shù)據(jù)庫在比較的時候,發(fā)現(xiàn)類型不匹配,它可能需要進(jìn)行內(nèi)部的隱式類型轉(zhuǎn)換,這么一搞,我辛辛苦苦給user_id建的索引,直接就廢了!數(shù)據(jù)庫又變成全表掃描了!怪不得慢!”
他一邊說著,一邊飛快地修改了代碼,在傳入?yún)?shù)前加了一個明確的類型轉(zhuǎn)換,確保傳入數(shù)據(jù)庫的是整型。
修改完畢。 重新編譯。 運(yùn)行App。
這一次,他再次點(diǎn)擊那個擁有幾千條模擬聊天記錄的聯(lián)系人。
奇跡,發(fā)生了! 就在手指觸碰到屏幕的那一瞬間! 唰! 之前那種卡頓、遲滯、如同老牛拉破車般的加載過程,徹底消失得無影無蹤!
取而代之的,是如絲般順滑的體驗! 聊天界面幾乎是零延遲、瞬間就完整地呈現(xiàn)在眼前!
幾千條聊天記錄,仿佛被施加了魔法,眨眼間就鋪滿了整個屏幕!
手指在屏幕上快速滑動列表,上下翻飛,毫無任何卡頓和掉幀!
流暢得簡直不像是在這臺破爛不堪的老舊測試機(jī)上運(yùn)行! 甚至比市面上很多主流的聊天App還要流暢!
“搞定!” 黎陽臉上露出如釋重負(fù)、帶著強(qiáng)大自信的笑容,他轉(zhuǎn)過頭,看向旁邊已經(jīng)目瞪口呆、仿佛石化了一般的陳東,輕松地說道:
“你看,就是這么一個小小的類型匹配細(xì)節(jié),平時根本沒人注意,但性能可能就差了幾十倍甚至上百倍。數(shù)據(jù)庫優(yōu)化這玩意兒,細(xì)節(jié)是魔鬼啊。”
陳東:“……” 他張大了嘴巴,下巴都快掉到地上了,眼睛瞪得像銅鈴,死死地盯著黎陽的屏幕,又看看黎陽那張云淡風(fēng)輕的臉。 半晌,他才艱難地合上嘴,從喉嚨里擠出兩個字: “臥……槽……”
震驚! 難以置信! 還有……濃濃的敬佩!甚至可以說是……膜拜!
從發(fā)現(xiàn)問題,到條理清晰地分析原因,再到精準(zhǔn)定位并解決Bug,最后是這立竿見影、堪稱恐怖的效果對比……
整個過程,行云流水,一氣呵成! 充滿了技術(shù)大佬對低級Bug的無情碾壓和絕對掌控力!
這尼瑪…… 這真的是那個上課會問指針基礎(chǔ)問題、下課會吐槽食堂飯菜的黎陽?!
“黎陽,你……你老實告訴我!” 陳東終于緩過神來,一把抓住黎陽的胳膊,語氣激動,眼神里充滿了探究和狂熱,
“你特么……絕對不是新手!你以前是不是偷偷學(xué)了很久?!甚至……你是不是哪個編程大佬的小號?!這數(shù)據(jù)庫優(yōu)化思路,這調(diào)試Bug的手段,還有這解決問題的速度……太TM離譜了!太牛逼了!”
他現(xiàn)在看黎陽的眼神,已經(jīng)完全變了! 這哪里是什么同級的普通學(xué)生! 這分明就是一個隱藏在宿舍里的骨灰級編程巨佬!人形自走代碼庫!
“哈哈,沒有沒有,你想多了?!?黎陽謙虛地擺擺手,心中卻是一片篤定——成了!自己在陳東心中的“技術(shù)大神”人設(shè),經(jīng)過今晚這一出,算是徹底立住了!而且是鋼印級別的!
他不動聲色地抽回胳膊,用早就準(zhǔn)備好的說辭解釋道: “真不是什么大佬。就是高中時候?qū)τ嬎銠C(jī)特別感興趣,自己瞎看了不少書,也喜歡泡在國外的技術(shù)論壇和博客上看大神們吹牛逼,耳濡目染,了解了點(diǎn)皮毛而已。很多東西也是現(xiàn)學(xué)現(xiàn)賣,邊做邊試。剛才那個類型問題,說實話,也是運(yùn)氣好,瞎貓碰上死耗子,突然想到的。”
這個解釋,半真半假,虛虛實實。 既承認(rèn)了自己有“底子”,又把關(guān)鍵的突破歸結(jié)于“運(yùn)氣”,顯得既牛逼,又不過分夸張,符合一個“天賦異稟又熱愛自學(xué)的技術(shù)宅”形象。
陳東顯然不完全相信黎陽的說辭,覺得他肯定是在謙虛,是在隱藏實力。 但他也沒有證據(jù),只能將信將疑地接受了這個說法。
不過,這并不妨礙他對黎陽的敬佩又拔高了幾個層級??聪蚶桕柕难凵窭?,除了原有的敬佩,更是多了幾分狂熱的好奇和探究。
“那你現(xiàn)在……費(fèi)這么大勁搞的這個App,又是數(shù)據(jù)庫優(yōu)化,又是自定義聊天氣泡的,到底是個啥玩意兒?。俊?陳東的好奇心徹底被點(diǎn)燃了,追問道。
黎陽神秘地笑了笑,知道火候差不多了,是時候拋出一點(diǎn)誘餌了。
“就是一個……我自己瞎鼓搗的小玩具?!?他斟酌著詞句,緩緩說道,“想試試看,能不能做一個……稍微智能一點(diǎn)的聊天機(jī)器人。嗯,比現(xiàn)在市面上那些只會復(fù)讀和簡單應(yīng)答的‘人工智障’,稍微聰明那么一點(diǎn)點(diǎn)。”
他頓了頓,看著陳東瞬間亮起來的眼睛,繼續(xù)吊胃口: “現(xiàn)在還只是個非常粗糙的原型,很多地方都沒弄好。等以后做得稍微像樣點(diǎn)了,第一個拿給你這個技術(shù)宅體驗體驗,幫我找找Bug。”
現(xiàn)在還不是徹底攤牌,邀請陳東入伙的時候。 但可以先畫個餅,透露一點(diǎn)“智能”、“超越時代”的方向,把他的胃口徹底吊起來!
“智能聊天機(jī)器人?!” 果然,陳東的眼睛瞬間就亮了,呼吸都有些急促起來! 作為也算半個技術(shù)愛好者,他當(dāng)然知道這意味著什么!
“是像小黃雞那種嗎?不對……你這個還能存聊天記錄,界面還做得這么流暢……聽起來……好像比小黃雞牛逼多了?。 ?陳東興奮地搓了搓手。
“呵呵,希望能比小黃雞聰明那么億點(diǎn)點(diǎn)吧?!?黎陽模棱兩可地回答,笑容高深莫測。
“臥槽!行!那我可等著了!你做好了可一定得第一個給我玩!” 陳東激動地說道。雖然心里還有無數(shù)疑問,但他知道現(xiàn)在不是刨根問底的時候,只能強(qiáng)壓下好奇,重新爬回床上躺下。
只是,他翻來覆去,腦子里全是剛才黎陽那番行云流水的“神操作”,以及那個聽起來就酷斃了的“智能聊天機(jī)器人”,激動得久久無法入睡。
黎陽看著陳東重新躺下,嘴角微微上揚(yáng),露出一抹計劃通的笑容。
搞定數(shù)據(jù)庫性能這個硬骨頭,App的核心功能算是基本盤穩(wěn)固了。
雖然UI還很粗糙。
雖然AI的集成還只是最基礎(chǔ)的命令行調(diào)用本地模型。
雖然服務(wù)器端還是一片空白。
但他手里,已經(jīng)有了一個可以拿出來演示的基礎(chǔ)版本! 一個在核心體驗上,足以碾壓這個時代絕大多數(shù)同類產(chǎn)品的版本!
更重要的是! 他成功地,在陳東——這位他計劃中未來最重要的技術(shù)合伙人心中,將自己“編程天才”、“技術(shù)大神”、“未來科技引領(lǐng)者”(霧)的光輝形象,深深地烙印了下去!
這為他后續(xù)“忽悠”……啊不,是“邀請”陳東上船,打下了無比堅實的基礎(chǔ)!
萬事俱備,只欠東風(fēng)……和啟動資金了。
黎陽下意識地摸了摸口袋,里面只有幾張皺巴巴的零錢和一張余額沒有多少的銀行卡。
現(xiàn)實的寒風(fēng),瞬間吹散了剛才裝逼成功的些許得意。
技術(shù)再牛逼,原型再驚艷,沒有錢,都是空中樓閣! 服務(wù)器要錢! 域名要錢!
后續(xù)開發(fā)可能需要外包部分非核心功能,比如UI設(shè)計,要錢!
更別提,想要讓“靈犀”真正變得智能,擺脫現(xiàn)在這個1.5B參數(shù)的“玩具模型”,購買昂貴的GPU服務(wù)器進(jìn)行模型訓(xùn)練和升級,那更是需要天文數(shù)字的錢!錢!錢! 還有母親的病……也需要錢!
必須!立刻!馬上!搞到第一桶金! 黎陽的目光,再次投向窗外的沉沉夜色,眼神變得無比銳利。
大腦開始以前所未有的速度運(yùn)轉(zhuǎn)起來,瘋狂搜索著前世的記憶碎片——
2015年底……這個時間點(diǎn)…… 到底有什么機(jī)會,能讓一個身無分文的大學(xué)生,快速地、合法地,賺到一筆足以啟動夢想的資金?! 時間,不多了!