speaker 1: 大家好,这里是原子能哥的一个春节,实在是不好意思。 话不多说,植物正题。 speaker 2: 今天的话题从链表开始。 链表应该是大家学到的第一个高级的数据结构,它的设计很巧妙,每一个数据指向下一个数据,环环相扣。 在链表里面插入一个数据,你永远只需要改动三个地方。 而与此相比,同样是把数据串起来的数组结构,运气不好的时候你可能甚至要重写整一个数组。 所以在数据写入的这个时间复杂度上,数组是on链表是O一肉眼可见的,不在一个级别上。 而在软件设计中,数据的完整性拥有了最高的优先度,所以写入速度比读取速度更难优化。 因为你可以允许几千万人同时读取一个数据,但是你只能够让一个人写入以上的知识。 很容易让你得出一个结论,那就是拥有O一写入速度的链表应该就是最好的数据结构之一,应该是软件设计中的首选。 但现实是恰恰相反,因为在现实世界里面,链表是很慢很慢的。 首先在现实中写入数据之前有一个非常重要的步骤,那就是找到要写入的地方。 因为计算机是个笨蛋,他找地址只会沿着源头一步一步的找,先找到这个变量的地址,然后在这个地方发现第一个数据,沿着它指向的地址再来到第二个数据,以此类推,一直到他来到他要改动的这个数据的家里。 而数组结构,无论这个数组有多长,它都只有一个地址。 所以寻址的动作永远就只有一次。 说到这里就可能会有聪明的小伙伴猜到了,这个寻指的动作是很耗时的,它实际上是比写入动作的耗时至少高两个数量级。 那么在这个量级上写入时间就是可以完全忽略的。 现在我们再回去算这个时间复杂度的话,你就会发现数组才是O 1,链表才是那个O N。 而因为cpu缓存的出现,链表的问题在现代计算机上面被进一步的放大。 Cpu缓存的工作原理很简单,cpu要找什么数据,它就会把那整个片区的内存数据都复制过来,放到缓存里面之后,cpu就可以直接从缓存里面获取整一个数据,而不需要再去找问内存。 根据等级的不同,缓存的读取速度会比内存快大约100倍。 而数组结构的数据因为都在同一个地址里,所以运气好的时候缓存会把它整个复制过来,直接获得100倍的提速。 但链表在设计上就和cpu缓存八字不合,因为链表的每一个数据节点它都会随机的分布在内存的各个角落。 每一个节点地址的跳转对于cpu缓存来说都是一个无法预测的命运舞台。 对于任何的缓存机制,不可预测性是最致命的,它代表着你大部分时间缓存都无法命中,你一直都在更新缓存,但是你又一直都用不上缓存。 所以在现实世界里面,除了极个别极特定的场景适合使用链表结构,绝大部分情况下数组都是更好的选择。 其实我们会经常陷入类似链表那样的情况。 在很多的领域我们都会特意打造这么一个与外部隔绝的温室。 我们在这个温室里面去培育知识,投入无数的精力去深入挖掘它、优化它,却很少有考虑这朵花离开了这个温室之后还能不能存活。 这种做法的初衷是好的,它能够让大家的精力更集中,目标更明确,更能做出成绩。 但只是在实际操作中,人们往往无法把握好这个度。 一个很经典例子就是2006年的奈飞百万美元大赛。 这是奈飞为了提升自家推荐系统的准确度,想到一个省钱的小妙招。 比赛内容很简单,奈飞提供了1亿条用户给电影的打分记录,其中有100万条抹去了分数,参赛选手要做的就是猜这百万条记录实际上都打了多少分。 比赛使用的是m1作为评判的标准,奈飞自家的推荐系统,他的ms一误差值是0.9525,谁能够第一个做到误差值比他低10%,就能够拿到100万美元,超过4万个团队参加了比赛。 然后三年之后,一个名叫bcrave programatic chaos的团队第一个突破10%,拿下百万大奖,结束了比赛。 这个团队是确实是很厉害的,因为他们刚开始提交的模型就比奈飞的准确度高了7%,剩下了3%他们花了整整三年。 根据官方的说明,他们是融入了上百个数据模型,进行了各种的排列组合和融合。 这个团队拿了钱,奈飞拿到了模型,这本该是一个皆大欢喜的结局。 反转出现在了三年之后,奈飞的官方宣布他们当年根本就没有用这个模型,而是保留了自家的推荐系统。 而他们给的理由也很简单,他们研究了一番之后发现这个模型的结构太复杂,投产成本太高了。 这也很好理解,毕竟是上百个模型合并起来的融合怪。 而他们自家的现有的推荐系统呢,就是一个非常普通的线性模型,非常容易维护。 而在这个故事里面的奈菲就犯了我之前提到的错误。 他们知道自己的推荐系统在生产环境里面需要应对哪些挑战,对于准确度、速度、扩容能力、开发成本和运营成本等等有哪些需求。 但是基于不知道什么原因,可能是为了简化这个比赛规则吧,他们只是选取了M S一作为评判标准。 那就等于是打造了一个和现实完全不一致的温室,结果就是花了100万买了一朵出了温室就死的花。 这一类的事件在学术界呢跟的是家常便饭。 如果有见过别人怎么水论文,或者是亲自参与过水论文的人,应该都能够理解我说的话。 当然业界也跑不掉啊,类似的问题一样存在。 我们计算机行业的每一个产品,每一个业务,它都是处处紧密相连的一个复杂机器。 但是呢我们的企业就特别自信满满的把每一个角色,每个步骤都拆分成一个独立的温室。 从企业管理的角度来说,这种做法其实也算合理。 因为角色的定位越精准,工作范围划分越明确,你就越容易对这个人进行管理。 但是工作内容可以拆分,工作影响无法拆分。 每个人的每个决策都会对其他人的工作造成影响,而每一个影响都可能会造成连锁反应。 我见过很多失败的产品,在后续的追溯的时候就发现好像大家都没有犯错,每个人都完成了自己的ki找都找不出一个背锅的。 那是因为每个人都被安排在自己的那个温室里面,只需做自己被委派的事情,而忽略了外面的世界。 其实打破温室间的壁垒并不难,只要你找到问题的所在,做一些职务上面的调整,就能够得到很好的效果。 比如说你让项目经理和项目的技术负责人一起进行项目管理,尤其是在早期的筹备阶段,项目管理很多经验都来自于制造业。 我见过不少没有技术背景的项目经理,都很机械的照搬这些经验。 但我们不像制造业那样可以精准的衡量一切的行为。 软件开发是一个具有极大的不确定性的一个生产流程,这是它的本质,是没办法用一些指标给硬压下来的。 所以让项目经理在筹备的阶段就能更多的获得技术负责人的输入,能够更好的让他们对整个项目的落地难度有一个更清晰的认识。 而对于技术负责人更多的参与项目高层建设,能够让他们更好的理解项目的源头、他的目的,他在业务中的定位,也能够帮他们在开发中做出更理性的决策。 另一个我推荐的做法就是开发团队管理自己产品的运维。 在自动化框架满地跑了今天系统运维不再是天书,你的开发团队更有能力自己负责自己产品的运维。 表面上看这是能够提升运维的响应能力,毕竟谁都不会比造产品的人更懂自己的bug在哪里。 但更重要的是能够让开发团队感受到他们的开发决策是如何影响到生产环境的效果的。 俗话说不当家不知柴米贵,当你的程序员在周六凌晨收到告警通知要爬起来处理生产环境的bug时候,他们在下一个开发周期就会更加三思而后行。 而DevOps团队能够把他们宝贵的人生从每一个系统的日常维护里面抽出来,就能够让他们接待更多的系统、更多的项目,有更多的经验去总结、去挖掘,他们作为一个运维可以做出了具有更高价值的贡献。 我在很多地方看到过DevOps的困境,不仅是大公司的运维团队,还有专门搞运维的初创公司,他们每天都是在各种客户的小问题、小细节里面奔波,永远都有处理不完的ticket,永远都在泥场里面打滚。 作为de只有ops没有de一辈子都是古二仔。 speaker 1: 我见过很多人一辈子都在温室里,在读书的时候,他们的探索止步于考试的范围,考完就是永别。 在做研究的时候,他们的课题只针对一个特定的参数。 只要观察到这个参数达标就教论文,后续爱咋咋的。 在工作的时候,上级委派他们做什么他们就做什么,对其他事情毫不关心,我不反对他们过得很舒服。 有些人也喜欢这样的生活,但如果你对计算机有一点热爱,有一点点的追求。 那我鼓励你去尝试打破自己所在的温室,让自己做的事情更有现实价值,让编程再次伟大。