編者按:本文系亞馬遜的潘陽講師,在掘金技術社區主辦的《中美技術人才硅谷大講堂 | JTalk 掘金線下活動第六期》 活動上的分享整理。掘金 JTalk 目前已舉辦6期,每期 JTalk 會邀請垂直行業的優秀工程師來分享優秀的實踐經驗,技巧方法。旨在爲開發者提供線下技術交流互動機會,幫助開發者成長。前端
潘陽,目前在亞馬遜 Alexa Mobile 部門工做,負責 Alexa app 的 React Native 及 iOS 架構與設計。其曾在 Apple HomeKit 團隊工做,參與 HomePod 及其餘多個 HomeKit 項目開發。此前他畢業於卡耐基梅隆大學並在Google實習。算法
相信你們能來這個講座,想必日常也在開源技術社區活躍,對新技術的熱情應該蠻高。那麼在工做中遇到問題時,你會如何解決?假若有不少種解決方法,你會如何分析?其實這是一個亙古不變的話題,每一個人會有每一個人不一樣的方法去取捨本身使用的工具,下面我來分享一下我以前在我相關工做中的經歷。數據庫
咱們先從 HomeKit 講起,HomeKit 出於隱私的考慮,不像 Alexa 和 Google Home 把全部的邏輯、數據放在服務器,你的手機端只是做爲展現端。HomeKit 認爲應該把全部的計算和邏輯都放在你的設備上,包括你的手機、手錶、iPad、電視。那麼實際中存在雲端的東西只有同樣,就是你的數據。這就致使了一個很經典的分佈式問題,我有多少臺設備,我同時在共享、讀取、修改同一份數據,那麼咱們該如何解決多設備之間同步的衝突? 這個問題其實很難,在 HomeKit 最開始創建之初,咱們並無花太多的時間精力去嘗試解決這個問題,由於在最開始咱們的業務邏輯和任務至關簡單。雖然理論上數據同步會有衝突,但遇到的狀況不會太多,並且它的衝突在當時簡單的業務邏輯下用戶通常感知不到,因此咱們一開始也沒花費太多時間在這上面。可是最近這一兩年,HomeKit 業務增加十分迅速,業務邏輯變得很是複雜。如今纔是一個須要咱們花時間和精力去解決問題的時機。後端
如今的分佈式計算領域裏有個解決方案叫作「CRDT」,用於解決多設備之間的數據共享衝突。但它如今沒有很是成熟的商業解決方案。若要採起該方案,需本身從頭開始解決實現。因爲這套系統基本上是一個完善的分佈式數據庫,其總體實現並不簡單。此外若是要用到 CRDT,HomeKit 的全部數據模型都要重寫。不只如此,咱們還須要與 iCloud 團隊進行合做。由於 HomeKit 是借用 iCloud 來存儲雲端數據的。有大公司工做經驗的人都知道,跨部門協同工做很是惱人,不只是技術層面,你還須要不少管理層層面的溝通與進度協調。全部這些問題加起來,致使這個解決方案的成本很是高。服務器
既然解決成本這麼高,上 CRDT 真的值得嗎?咱們來作一個收益-成本分析。成本咱們剛纔已經分析完了,下面來看收益。咱們根據一些反饋數據得知,數據丟失、同步衝突等現象發生愈來愈頻繁,數據同步時間也愈來愈長,總體用戶體驗愈來愈差。因此這個收益-成本圖應該是這樣。網絡
幸運的是,Apple 是一個很是在乎用戶體驗的公司。這麼高的用戶體驗的收益使得咱們願意付出這麼高的成本。最終管理層仍是決定上馬項目。從這個例子中咱們學到的一點就是,在上馬項目以前要作好收益-成本分析,咱們才能作出最符合利益的決定。架構
可是呢,這其中會有一個潛在的問題,你最開始的收益分析並不必定是你最終能拿到的收益,這就是接下來我要講的另一個例子,即你的分析可能從頭開始就是錯的。app
這就要講到另一個東西了,HomeKit 除了多設備之間的數據同步通信,還有在一個設備內部的進程間的通信。一旦涉及到進程間的通信,就會有各個類的序列化和反序列化。那麼最開始的時候很簡單,咱們用了 iOS 原生的 NSKeyedArchiver 和 NSKeyedUnarchiver。這兩個東西很是經典、好用,可是它們須要一些資源和計算力。因爲 iPhone 和 iPad 計算力足夠強大咱們一開始並無意識到這個問題,直到咱們後面推出了手表。若是有用過 Apple Watch 的人大概知道,第一代和第二代的 Apple Watch 硬件性能很是差,你若是用過的話,可能會感受打開一個 App 會很是的久,因此致使體驗很是糟糕。咱們當時在想咱們究竟能作些什麼來提高,至少加速一下啓動時間。咱們作了一個集中在後端的內部分析,最後發現序列化和反序列化佔了一個很是大的比重,將近有超過 50% 的時間花在了上面。在通過一些研究後咱們發現,若是是用 ProtoBuf 來進行序列化和反序列化,資源佔用能夠減小 50% 以上,時間能夠節約 70%。這顯然是一組很可觀的數字,但它的成本也很是高。機器學習
咱們要把底層涉及跨進程通信的類所有針對 ProtoBuf 進行修改。首先須要針對每一個類設計 proto 文件。而後須要把底層的數據模型的各類操做都作相應修改。由於 HomeKit 裏涉及進程間通信的類很是多,並且相互之間的關係錯綜複雜,這工程量其實不小。因此目前看來,這個收益-成本圖跟前一個 CRDT 基本相似,也是一個高成本高收益的項目。分佈式
可是咱們當時花了好幾個月把它寫了出來,到最後快收尾的階段咱們作端對端的測試的時候才發現一個很嚴重的問題:端對端的測試體驗提高並無很明顯,總耗時提高甚至小於 30%。爲何如今端到端測試的結果跟最初咱們分析後端的結果相差這麼多?答案並不難發現。在整個 Home App 冷啓動過程當中,實際上是分爲前端 UI 渲染和後端數據處理兩部分,而 UI 渲染佔了其中的大頭。實際在後臺傳輸和後臺數據處理即序列化和反序列化這邊,大概也就只佔了不到 30% 的時間。因此即使咱們把整個後端數據處理的時間提高了 70%,也不過是減小了 30% 中的 70%。這對總體用戶感覺提高能夠說是微乎其微,畢竟若是你已經等了五秒,你不會再介意多等一秒的。
圖上藍點是真實收益,紅點是理想收益。顯然咱們最初早期的分析中並無分析全面,因此有了一個很是理想的收益。然而實際分析以後才發現真實的收益其實很是低,徹底不值得這麼高的成本。因此在作收益-成本分析的時候,儘可能從系統全局的角度去作分析。僅侷限於本身的範圍,不少時候會誤判成本或者收益。
上述兩個例子都還好,都有最佳的解決方案,你的選擇無非是要仍是不要。但工做中不少時候你會遇到一個問題:你手頭可能有不少的工具,你究竟要用哪個?下面講得這個例子就是相似的。
HomeKit 做爲一個智能家居平臺須要知道你的家在哪,這樣才能給你提供更好的功能。比方說等你到家的時候幫你把燈打開、當你離開家的時候幫你把門鎖上等相似的功能,這前提即是須要知道你家的位置。因爲一些隱私的問題,咱們不能讓用戶手動輸入,因此咱們不得不經過代碼來判斷你家在哪。這個解決方案至關得「naive」,就是當你連上你家的 Wi-Fi,和本地的設備連上之後,咱們就判斷你已經回家了,咱們會向 CoreLocation 請求一個當前的位置,把這個位置當成你的家的位置。這看起來很是無害、很是簡單有效的一個方案,但其實有個問題,在於:
這兩個因素加起來,可能會致使咱們獲得你家的位置實際離你好幾個街區。這個影響很是嚴重,總不能離我我家好幾個街區的時候就自動把我家門打開了。最後怎麼解決這個問題呢?當你連上你家 Wi-Fi 之後,咱們一直連續獲取你的當前位置,直到必定時間之後,這樣我就會有你的一系列的位置。如今的問題變成了我有一堆你的數據疊,要怎麼樣纔可以知道你家在哪。這實際上是一組有很明顯特徵的模塊數據,你極可能是離了你家一大段的時候你就徑直走向了家門口,或者你開車繞了一大段再走回你家,但無論怎樣它就是一串數據點點向你家,而且停在了你家,這是一個很明顯的數據特徵。
那麼對於對模式識別,或是對機器學習有所研究的人來說,會很明顯意識到咱們能夠訓練出一個模型。訓練出來後,把你當前的數據點扔進去,根據往期的模式識別出來。這是一個很完全的解決方案,並且也很精確,可是成本尚待考量。若是咱們須要引入模式識別這一套很複雜的算法,或者一套更高級的機器學習算法,咱們須要把它拉進咱們的代碼庫裏,咱們還須要訓練和維護它的數據,咱們還須要把它的模型創建出來,這會消耗掉好幾個全職工程師的幾個月時間。模式識別的收益很是高,由於它能一直準確推算出你的家位置在哪,可是有沒有更好的方法?就是能夠沒有那麼準確但成本大幅度降低。
若是你上過跟機器學習相關課程的話,你會知道一個很是經典且簡單的算法,叫作KMeans 算法。它是用來作聚類的,它就很適合解決這個問題。首先,咱們的數據具備很明顯的模式,即一系列的點連向了你家並在你家門口停下來了,因此這個假設即是你家的點是最多的。那麼咱們用 KMeans 算法把這些位置點作一個聚類,把它聚成幾類,而後把聚類最多的那個點做爲你家終點的位置,這是一個很不錯的解決方案。通過咱們測試,雖然它的效果不能達到 100% 準確,可是在 99% 的狀況下都可以判斷出你家的位置。再加上這個位置在使用時是做爲 Geofence,會有一個最小 20 米的半徑,略微的誤差幾乎沒有影響。同時,它收集數據的時間大概只有那麼幾分鐘,收集到的數據點其實很是少,咱們大概只須要迭代不到10次左右個人數據就能收斂,我甚至連提早判斷退出的收斂算法都不須要,我大概只須要強制迭代幾回就能獲得一個很好的結果了。因此能夠看到它的成本其實很是低,實在沒有必要去上模式識別。
假如你日常不知道這些東西,那你可能沒法作這些取捨,對於我的開發者的技術成長來講,能夠多關注像掘金這樣的技術社區,多去了解新興的技術,你沒必要對每樣新技術都很熟悉,只須要清楚它大概能解決什麼問題,這樣才能讓你在遇到問題時想起來手上還有這樣的工具,屆時真須要它的時候再進行深刻的瞭解。
最後跟你們分享一個我我的的小例子:最近也快要入夏了,我便萌生了減脂的想法,那麼有一套減肥方案叫」生酮飲食「,感興趣的能夠回去瞭解一下。它的原理是減小碳水化合物攝入,增長蛋白質和脂肪攝入。那麼一旦開始這套飲食以後,你買食物的時候會開始關注它的養分標籤表,看看它到底含了多少的碳水、脂肪和蛋白質。
國內的養分標籤都是統一地以」每100克「做爲一份的單位,但美國這邊商家比較奸詐,明明有個碳水化合物很高的食物,商家把它每份的重量降得特別小,好比只有10克,那我這一份的碳水化合物在數值上可能也就只有2克。若是你不看每份的重量,只看碳水化合物在每份當中的份量的話,你會以爲它很低,但實際上它很高。
上圖是我在幾個產品上截取下來的食物養分表,能夠看到左邊這個一份是31克,它含了3克的碳水化合物;中間這個是114克,它含了42克的碳水化合物。因此呢這兩份的計數單位是不同的。那麼有了這個需求以後,我在想我可否作一個小工具,把這兩個格式工整、字體統一的養分表掃下來,讓小工具作一個轉換,最後在個人手機上顯示出來。
既然如今的需求是這樣,我如何解決這個問題?顯然我首先須要一個圖像識別的庫,把養分表中的數字和文字識別出來,我才能作下一步的處理。當時我在 GitHub 上找了一番,找到一個還不錯的庫。正準備寫這個工具的時候,個人一位朋友提醒我這些食物養分表可能網上都有。一番搜索後,果不其然我在美國農業部的官網找到了這些公開的數據。你要作的只是把食物的標籤代碼輸入進去,它就會返回一個網頁。有了網頁以後,咱們要作的事情就很是簡單了,直接扒一下網頁的內容把須要的數據找出來就能夠了,根本不須要剛纔的那個庫。 如今有兩套工具,可是這就看你我的的喜愛了。比方說我對網絡方面不太瞭解,可能就更傾向於 OCR 識別標籤。要是我比較熟悉 Python 或者 Swift,我比較喜歡處理網絡請求,那可能更傾向於使用美國農業部網站。
因此你要作的就是善用搜索引擎,來了解你手上的工具,這兩套工具一個走的是圖像識別,另外一個走的是網絡請求,雖然解決的方法不一樣,可是最後解決的問題是同一個。日常要多閱讀別的領域文章,掘金上的就很不錯,多瞭解新的技術趨勢。
最後總結一下,今天主要分享的有三點
以上就是這些,謝謝你們。
Android P 新特性大起底 - 李寄超 | JTalk 第六期