编者按:本文系亚马逊的潘阳讲师,在掘金技术社区主办的《中美技术人才硅谷大讲堂 | 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 第六期