专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

万字长文详解Flink作业提交流程(二)

  2。1。3虚拟Transformation的转换
  虚拟的Transformation生成的时候不会转换为SteramNode,而是添加为虚拟节点。
  privatevoidaddEdgeInternal(IntegerupStreamVertexID,IntegerdownStreamVertexID,inttypeNumber,StreamPartitionerlt;?partitioner,ListStringoutputNames,OutputTagoutputTag,ShuffleModeshuffleMode){当上游是sideoutput时,递归调用,并传入sideoutput信息if(virtualSideOutputNodes。containsKey(upStreamVertexID)){intvirtualIdupStreamVertexID;upStreamVertexIDvirtualSideOutputNodes。get(virtualId)。f0;if(outputTagnull){outputTagvirtualSideOutputNodes。get(virtualId)。f1;}addEdgeInternal(upStreamVertexID,downStreamVertexID,typeNumber,partitioner,null,outputTag,shuffleMode);}当上游是select时,递归调用,并传入select信息elseif(virtualSelectNodes。containsKey(upStreamVertexID)){intvirtualIdupStreamVertexID;upStreamVertexIDvirtualSelectNodes。get(virtualId)。f0;if(outputNames。isEmpty()){selectionsthathappendownstreamoverrideearlierselectionsoutputNamesvirtualSelectNodes。get(virtualId)。f1;}addEdgeInternal(upStreamVertexID,downStreamVertexID,typeNumber,partitioner,outputNames,outputTag,shuffleMode);}当上游是Partition时,递归调用,并传入Partition信息elseif(virtualPartitionNodes。containsKey(upStreamVertexID)){intvirtualIdupStreamVertexID;upStreamVertexIDvirtualPartitionNodes。get(virtualId)。f0;if(partitionernull){partitionervirtualPartitionNodes。get(virtualId)。f1;}shuffleModevirtualPartitionNodes。get(virtualId)。f2;addEdgeInternal(upStreamVertexID,downStreamVertexID,typeNumber,partitioner,outputNames,outputTag,shuffleMode);}不是以上逻辑转换的情况,真正构建StreamEdgeelse{StreamNodeupstreamNodegetStreamNode(upStreamVertexID);StreamNodedownstreamNodegetStreamNode(downStreamVertexID);Ifnopartitionerwasspecifiedandtheparallelismofupstreamanddownstreamoperatormatchesuseforwardpartitioning,userebalanceotherwise。没有指定partitioner时,会为其选择forward或者rebalanceif(partitionernullupstreamNode。getParallelism()downstreamNode。getParallelism()){partitionernewForwardPartitionerObject();}elseif(partitionernull){partitionernewRebalancePartitionerObject();}if(partitionerinstanceofForwardPartitioner){if(upstreamNode。getParallelism()!downstreamNode。getParallelism()){thrownewUnsupportedOperationException(Forwardpartitioningdoesnotallowchangeofparallelism。Upstreamoperation:upstreamNodeparallelism:upstreamNode。getParallelism(),downstreamoperation:downstreamNodeparallelism:downstreamNode。getParallelism()Youmustuseanotherpartitioningstrategy,suchasbroadcast,rebalance,shuffleorglobal。);}}if(shuffleModenull){shuffleModeShuffleMode。UNDEFINED;}创建StreamEdge,并将该SteramEdge添加到上游的输出,下游的输入。StreamEdgeedgenewStreamEdge(upstreamNode,downstreamNode,typeNumber,outputNames,partitioner,outputTag,shuffleMode);getStreamNode(edge。getSourceId())。addOutEdge(edge);getStreamNode(edge。getTargetId())。addInEdge(edge);}}2。2作业图
  JobGraph可以由流计算的StreamGraph和批处理的OptimizedPlan转换而来。流计算中,在StreamGraph的基础上进行了一些优化,如果通过OperatorChain机制将算子合并起来,在执行时,调度在同一个Task线程上,避免数据的跨线程、跨网段的传递。
  2。2。1JobGraph核心对象JobVertex
  经过算子融合优化后符合条件的多个SteramNode可能会融合在一起生成一个JobVertex,即一个JobVertex包含一个或多个算子,JobVertex的输入是JobEdge,输出是IntermediateDataSet。JobEdge
  JobEdge是JobGraph中连接IntermediateDataSet和JobVertex的边,表示JobGraph中的一个数据流转通道,其上游数据源是IntermediateDataSet,下游消费者是JobVertex。数据通过JobEdge由IntermediateDataSet传递给JobVertex。IntermediateDataSet
  中间数据集IntermediateDataSet是一种逻辑结构,用来表示JobVertex的输出,即该JobVertex中包含的算子会产生的数据集。不同的执行模式下,其对应的结果分区类型不同,决定了在执行时刻数据交换的模式。
  IntermediateDataSet的个数与该JobVertex对应的StreamNode的出边数量相同,可以是一个或者多个。2。2。2JobGraph生成过程
  StreamingJobGraphGenerator负责流计算JobGraph的生成,在转换前需要进行一系列的预处理。privateJobGraphcreateJobGraph(){preValidate();makesurethatallverticesstartimmediately设置调度模式jobGraph。setScheduleMode(streamGraph。getScheduleMode());Generatedeterministichashesforthenodesinordertoidentifythemacrosssubmissionifftheydidntchange。为每个节点生成确定的hashid作为唯一表示,在提交和执行过程中保持不变。MapInteger,byte〔〕hashesdefaultStreamGraphHasher。traverseStreamGraphAndGenerateHashes(streamGraph);Generatelegacyversionhashesforbackwardscompatibility为了向后保持兼容,为每个节点生成老版本的hashidListMapInteger,byte〔〕legacyHashesnewArrayList(legacyStreamGraphHashers。size());for(StreamGraphHasherhasher:legacyStreamGraphHashers){legacyHashes。add(hasher。traverseStreamGraphAndGenerateHashes(streamGraph));}MapInteger,ListTuple2byte〔〕,byte〔〕chainedOperatorHashesnewHashMap();真正对SteramGraph进行转换,生成JobGraph图setChaining(hashes,legacyHashes,chainedOperatorHashes);setPhysicalEdges();设置共享slotgroupsetSlotSharingAndCoLocation();setManagedMemoryFraction(Collections。unmodifiableMap(jobVertices),Collections。unmodifiableMap(vertexConfigs),Collections。unmodifiableMap(chainedConfigs),idstreamGraph。getStreamNode(id)。getMinResources(),idstreamGraph。getStreamNode(id)。getManagedMemoryWeight());配置checkpointconfigureCheckpointing();jobGraph。setSavepointRestoreSettings(streamGraph。getSavepointRestoreSettings());如果有之前的缓存文件的配置,则重新读入JobGraphGenerator。addUserArtifactEntries(streamGraph。getUserArtifacts(),jobGraph);settheExecutionConfiglastwhenithasbeenfinalizedtry{设置执行环境配置jobGraph。setExecutionConfig(streamGraph。getExecutionConfig());}catch(IOExceptione){thrownewIllegalConfigurationException(CouldnotserializetheExecutionConfig。Thisindicatesthatnonserializabletypes(likecustomserializers)wereregistered);}returnjobGraph;}
  预处理完毕后,开始构建JobGraph中的点和边,从Source向下遍历StreamGraph,逐步创建JObGraph,在创建的过程中同事完成算子融合(OperatorChain)优化。
  执行具体的Chain和JobVertex生成、JobEdge的关联、IntermediateDataSet。从StreamGraph读取数据的StreamNode开始,递归遍历同时将StreamOperator连接在一起。
  整理构建的逻辑如下(看上图!!!):
  1)从Source开始,Source与下游的FlatMap不可连接,Source是起始节点,自己成为一个JobVertx。
  2)此时开始一个新的连接分析,FlatMap是起始节点,与下游的KeyedAgg也不可以连接,那么FlatMap自己成为一个JobVertex。
  3)此时开始一个新的连接分析。KeyedAgg是起始节点,并且与下游的Sink可以连接,那么递归地分析Sink节点,构造Sink与其下游是否可以连接,因为Slink没有下游,所以KeyedAgg和Sink节点连接在一起,共同构成了一个JobVertex。在这个JobVertex中,KeyedAgg是起始节点,index编号为0,sink节点index编号为1。
  构建JobVertex的时候需要将StreamNode中的重要配置信息复制到JobVertex中。构建好JobVertex之后,需要构建JobEdge将JobVertex连接起来。KeyedAgg和Sink之间构成了一个算子连接,连接内部的算子之间无序构成JobEdge进行连接。
  在构建JobEdge的时候,很重要的一点是确定上游JobVertex和下游JobVertex的数据交换方式。此时根据ShuffleMode来确定ResultPartition类型,用FlinkPartition来确定JobVertex的连接方式。
  Shuffle确定了ResultPartition,那么就可以确定上游JobVertex输出的IntermediateDataSet的类型了,也就知道JobEdge的输入IntermediateDataSet。
  ForwardPartitioner和RescalePartitioner两种类型的Partitioner转换为DistributionPattern。POINTWISE的分发模式。其他类型的Partitioner统一转换为DistributionPattern。ALLTOALL模式。
  JobGraph的构建和OperatorChain优化:privateListStreamEdgecreateChain(IntegerstartNodeId,IntegercurrentNodeId,MapInteger,byte〔〕hashes,ListMapInteger,byte〔〕legacyHashes,intchainIndex,MapInteger,ListTuple2byte〔〕,byte〔〕chainedOperatorHashes){if(!builtVertices。contains(startNodeId)){ListStreamEdgetransitiveOutEdgesnewArrayListStreamEdge();ListStreamEdgechainableOutputsnewArrayListStreamEdge();ListStreamEdgenonChainableOutputsnewArrayListStreamEdge();StreamNodecurrentNodestreamGraph。getStreamNode(currentNodeId);获取当前节点的出边,判断是否符合OperatorChain的条件分为两类:chainableoutputs,nonchainableoutputsfor(StreamEdgeoutEdge:currentNode。getOutEdges()){if(isChainable(outEdge,streamGraph)){chainableOutputs。add(outEdge);}else{nonChainableOutputs。add(outEdge);}}对于chainable的边,递归调用createchain返回值添加到transitiveOutEdges中for(StreamEdgechainable:chainableOutputs){transitiveOutEdges。addAll(createChain(startNodeId,chainable。getTargetId(),hashes,legacyHashes,chainIndex1,chainedOperatorHashes));}对于无法chain在一起的边,边的下游节点作为Operatorchain的Head节点进行递归调用,返回值添加到transitiveOutEdges中for(StreamEdgenonChainable:nonChainableOutputs){transitiveOutEdges。add(nonChainable);createChain(nonChainable。getTargetId(),nonChainable。getTargetId(),hashes,legacyHashes,0,chainedOperatorHashes);}ListTuple2byte〔〕,byte〔〕operatorHasheschainedOperatorHashes。computeIfAbsent(startNodeId,knewArrayList());byte〔〕primaryHashByteshashes。get(currentNodeId);OperatorIDcurrentOperatorIdnewOperatorID(primaryHashBytes);for(MapInteger,byte〔〕legacyHash:legacyHashes){operatorHashes。add(newTuple2(primaryHashBytes,legacyHash。get(currentNodeId)));}chainedNames。put(currentNodeId,createChainedName(currentNodeId,chainableOutputs));chainedMinResources。put(currentNodeId,createChainedMinResources(currentNodeId,chainableOutputs));chainedPreferredResources。put(currentNodeId,createChainedPreferredResources(currentNodeId,chainableOutputs));if(currentNode。getInputFormat()!null){getOrCreateFormatContainer(startNodeId)。addInputFormat(currentOperatorId,currentNode。getInputFormat());}if(currentNode。getOutputFormat()!null){getOrCreateFormatContainer(startNodeId)。addOutputFormat(currentOperatorId,currentNode。getOutputFormat());}如果当前节点是起始节点,则直接创建JobVertex,否则返回一个空的StreamConfigStreamConfigconfigcurrentNodeId。equals(startNodeId)?createJobVertex(startNodeId,hashes,legacyHashes,chainedOperatorHashes):newStreamConfig(newConfiguration());将StreamNode中的配置信息序列化到Streamconfig中。setVertexConfig(currentNodeId,config,chainableOutputs,nonChainableOutputs);再次判断,如果是Chain的起始节点,执行connect()方法,创建JobEdge和IntermediateDataset否则将当前节点的StreamConfig添加到chainedConfig中。if(currentNodeId。equals(startNodeId)){config。setChainStart();config。setChainIndex(0);config。setOperatorName(streamGraph。getStreamNode(currentNodeId)。getOperatorName());for(StreamEdgeedge:transitiveOutEdges){connect(startNodeId,edge);}config。setOutEdgesInOrder(transitiveOutEdges);config。setTransitiveChainedTaskConfigs(chainedConfigs。get(startNodeId));}else{chainedConfigs。computeIfAbsent(startNodeId,knewHashMapInteger,StreamConfig());config。setChainIndex(chainIndex);StreamNodenodestreamGraph。getStreamNode(currentNodeId);config。setOperatorName(node。getOperatorName());chainedConfigs。get(startNodeId)。put(currentNodeId,config);}config。setOperatorID(currentOperatorId);if(chainableOutputs。isEmpty()){config。setChainEnd();}returntransitiveOutEdges;}else{returnnewArrayList();}}2。2。3算子融合
  一个Operatorchain在同一个Task线程内执行。OperatorChain内的算子之间,在同一个线程内通过方法调用的方式传递数据,能减少线程之间的切换,减少消息的序列化反序列化,无序借助内存缓存区,也无须通过网络在算子间传递数据,可在减少延迟的同时提高整体吞吐量
  operatorchain的条件:
  1)下游节点的入度为1
  2)SteramEdge的下游节点对应的算子不为null
  3)StreamEdge的上游节点对应的算子不为null
  4)StreamEdge的上下游节点拥有相同的slotSharingGroup,默认都是default。
  5)下游算子的连接策略为ALWAYS。
  6)上游算子的连接策略为ALWAYS或者HEAD。
  7)StreamEdge的分区类型为ForwardPartitioner
  8)上下游节点的并行度一致
  9)当前StreamGraph允许chain2。3执行图
  2。3。1ExecutionGraph核心对象ExecutionJobVertex
  该对象和JobGraph中的JobVertex一一对应。该对象还包含了一组ExecutionVertex,数量与该JobVertex中所包含的SteramNode的并行度一致。
  ExecutionJobVertex用来将一个JobVertex封装成一ExecutionJobVertex,并以此创建ExecutionVertex、Execution、IntermediateResult和IntermediateResultPartition,用于丰富ExecutionGraph。
  在ExecutionJobVertex的构造函数中,首先是依据对应的JobVertex的并发度,生成对应个数的ExecutionVertex。其中,一个ExecutionVertex代表一个ExecutionJobVertex的并发子Task。然后是将原来JobVertex的中间结果IntermediateDataSet转化为ExecutionGrap中IntermediateResultExecutionVertex
  ExecutionJobVertex中会对作业进行并行化处理,构造可以并行执行的实例,每个并行执行的实例就是ExecutionVertex。
  构建ExecutionVertex的同时,也回构建ExecutionVertex的输出IntermediateResult。并且将ExecutionEdge输出为IntermediatePartition。
  ExecutionVertex的构造函数中,首先会创建IntermediatePartition,并通过IntermediateResult。setPartition()建立IntermediateResult和IntermediateResultPartition之间的关系,然后生成Execution,并配置资源相关。IntermediateResult
  IntermediateResult又叫做中间结果集,该对象是个逻辑概念,表示ExecutionJobVertex的输出,和JobGraph中的IntermediateDataSet一一对应,同样,一个ExecutionJobVertex可以有多个中间二级果,取决于当前JobVertex有几个出边。
  一个中间结果集包含多个中间结果分区IntermediateResultPartition,其个数等于该JobVertex的并发度。IntermediateResultPartitionIntermediateResultPartition又叫做中间结果分区,表示1个ExecutionVertex输出结果,与ExecutionEdge相关联。ExecutionEdge
  表示ExecutionVertex的输入,连接到上游产生的IntermediateResultPartition。一个Execution对应于唯一的一个IntermediateResultPartition和一个ExecutionVertex。一个ExecutionVertex可以有多个ExecutionEdge。Execution
  ExecutionVertex相当于每个Task的模板,在真正执行的时候,会将ExecutionVertex中的信息包装为一个Execution,执行一个ExecutionVertex的一次尝试。JobManager和TaskManager之间关于Task的部署和Task执行状态的更新都是通过ExecutionAttemptID来标识实例的。在故障或者数据需要重算的情况下,ExecutionVertex可能会有多个ExecutionAttemptID。一个Execution通过ExecutionAttemptID标识。2。3。2ExecutionGrap生成过程
  初始话作业调度器的时候,根据JobGraph生活ExecutionGraph。在SchedulerBase的构造方法中触发构建,最终调用SchedulerBasecreateExecutionGraph触发实际的构建动作,使用ExecutionGraphBuiler构建ExecutionGraph。
  核心代码attachJobGraph:
  构建ExecutionEdge的连接策略:点对点连接(DistributionPattern。POINTWISE)
  该策略用来连接当前ExecutionVertex与上游的IntermediataeResultParition。
  连接分三种情况
  1)一对一连接:并发的Task数量与分区数相等。
  2)多对一连接:下游的Task数量小于上游的分区数,此时分两种情况:
  a:下游Task可以分配同数量的结果分区IntermediataeResultParition。如上游有4个结果分区,下游有2个Task,那么每个Task会分配两个结果分区进行消费。
  b:每个Task消费的上游分区结果数据不均,如上游有3个结果分区,下游有两个Task,那么一个Task分配2个结果分区消费,另一个分配一个结果分区消费。
  3)一对多连接:下游的Task数量多余上游的分区数,此时两种情况:a:每个结果分区的下游消费Task数据量相同,如上游有两个结果分区,下游有4个Task,每个结果分区被两个Task消费。b:每个结果分区的下游消费Task数量不相同,如上游有两个结果分区,下游有3个Task,那么一个结果分区分配2个Task消费,另一个结果分区分配一个Task消费。全连接(DistributionPattern。ALLTOALL)
  该策略下游的ExecutionVertex与上游的所有IntermediataeResultParition建立连接,消费其生产的数据。一般全连接的情况意味着数据在Shuffle。

同人创作的质量可以有多高?命令与征服扭曲暴动模组介绍命令与征服扭曲暴动简称TI,是基于命令与征服泰伯利亚之日的引擎之上而制作的一款独立的游戏模组。这个模组在2007年11月由安德鲁阿罗欧文(AndrewAroOwen)始创,随后又有心魔开发商新作登Steam开放世界中驾驶柴油机甲今日(9月4日),心魔开发商的新动作冒险游戏Wardogz登录Steam商城页面,本作将于2023年第四季度(1011月)上线,游戏暂不支持简体中文。Steam商城地址WardogDNF110级天帝刷图加点推荐今天小编给大家带来DNF110级天帝刷图加点的推荐。在110级版本,天帝刷图的能力还是不容小觑,此次推荐主要有几个小技能的加点的不同。在原来的版本中,大部分玩家都会偷学封印解除这个莫高窟发现将进酒原版,下笔之狂令人直呼再也不喜欢李白了在某游戏中,李白这个英雄有这样一句台词十步杀一人,千里不留行。对某些不太了解李白生平事迹的小伙伴来说,很有可能就会把这句诗,当成是游戏制作组为了贴近李白在游戏中刺客的形象,故意杜撰超级机器人大战30攻略第二主人公与第二台凶鸟30加入方法下面为我们打来超级机器人大战30游戏中,第二主人公与第二台凶鸟30参加的办法。游戏中在挑选主人公性别后,第二名主人公会以隐藏角色的形式参加我方。当玩家在游戏中完成初期地球世界两个区游戏塞尔达传说旷野之息等待运气好不如看我攻略旷野之息的速递有技巧。在王城打第一个boss的时候,如果能在进入过场动画之前射出一箭,并在播报中使这一箭的伤害判断与boss的眼睛(知道如何打击的区域)重合,就可以在过场动画中继续可杰冲击127段被坑,开团上海EDGM嘉木,找到俱乐部也要报仇最近这个赛季可杰一直在冲击127段分路段位,本身这个赛季新版宫本上线后就有很高的强度,且刚好可以用来弥补可杰英雄池缺乏强力上分英雄的空缺,所以可杰一直在练习这个英雄,目前可杰宫本的都说熬夜伤肝,除了早睡,有什么办法能减少伤害?Q养生君,我现在这份工作经常加班到很晚,有时候加完班洗漱一下都过了十二点了。总担心熬夜对肝不好,但又没办法早睡,有什么办法可以减少伤害?熬夜后,最好中午睡个觉。医学研究表明,中午3逛超市,1类食物少买回家吃!医生也说这类食物吃多了堵塞血管近些年来,大家应该经常会听到养护血管这个词,但是很多人对此并不了解,其实养护血管的关键,就是找到伤害血管的原因,避开它们,就能帮助大家拥有一个畅通无阻的血管,而日常我们逛超市的时候注意!亚麻籽油食用时,这三件事千万不要做亚麻籽油已经是家庭健康食用油的首选,那么亚麻籽油如何选择和食用呢?告诉你三件不该做的事情!禁忌1用亚麻籽油高温加热炒菜一般的油大家都是用来炒菜的,可是亚麻籽油您可千万不能高温加热炒午时心经当令,四大主穴安心养神李玉华张波,战略支援部队特色医学中心(原306医院),中医科医学科普中心刘燕崔彦编辑黄帝内经中说,当心经异常时反映到人体的外部症状包括心胸烦闷疼痛咽干口渴眼睛发黄胁痛手臂一面靠小指
电动车的石墨烯电池好在哪里?不说实话不行!很多人都误会它了石墨烯这三个字很多电动车车主都不陌生吧,近几年有关于石墨烯的商品也是多种多样的,应用到了各大领域,大到这个电子器件医用器械,小到生活中的暖手宝内裤这些物品,近些年石墨烯也在我们能源华为AITO问界,降价后销量下滑,余承东又被打脸?最近一年多来,国内的新能源汽车市场,竞争十分激烈,但销售量也在突飞猛进。已经停止生产燃油汽车的比亚迪,在2022年的销售量一举达到了186万辆,超过南北大众一汽二汽丰田本田长安长城三星用台积电代工芯片?对自家工艺没信心,小米11系列也被坑过!据多方消息证实,三星GalaxyS23系列定档2月份发布,搭载高频版骁龙8Gen2处理器,不少粉丝猜测可能由三星自家代工。现在有业内人士透露,三星对自家工艺失望透顶,因此这款处理器排第六,华为Mate50ProDXOMARK屏幕分数出炉141分感谢大家一直以来的支持!科技美学全体成员祝大家新年快乐!去年九月,华为发布了Mate50Pro手机,起售价为6799元。近日,DXOMARK公布了华为Mate50Pro的屏幕得分。2022年中国市场智能手机销量或约2。55亿部创下2015年以来最差1月20日,据CINNOResearch月度国内手机销量监测数据显示,2022年中国市场智能手机销量或仅约2。55亿部,同比下降19,各季度销量同比均呈现负增长,跌幅1421之间,ColorOS发布春节期间版本停更公告,1月31日后恢复正常发版随着春节的来临,近日,ColorOS官方发布了春节期间ColorOS版本停更公告。公告显示,将暂停版本更新及其他发版计划(包括但不限于ColorOS13升级内测升级公测正式版发布)AR滑雪护目镜Sirius将推出今年的CES不乏令人兴奋的公告,包括Sirius的发布,这是一副先进的滑雪护目镜,使用AR和AI技术,在用户滑雪时显示现实世界中的宝贵信息。根据官方网站,这款护目镜由瑞士初创公司O把充电桩让给更需要的车,魏牌教你助人为乐现在的汽车市场,新能源车型逐渐成为宠儿,销售占比越来越高,各种技术百花齐放,目前较为主流的有纯电增程式油混插混,对于这几种技术,我个人的看法如下,也欢迎各位车友一起来讨论。首先讲讲小米12Pro天玑版会成为下一个小米6吗?2000多的价位能买到的旗舰水桶仅此一部。我上一部天玑1000的那个给我坑怕了,其实我还是很犹豫的,观望了很久,目前真正上手感觉还是不错。希望后面的MIUI能够真正的做好优化,相机降价促销真香?特斯拉ModelY的冬季续航究竟如何?爱卡汽车单车测试原创特斯拉又双叒叕降价了!1月6日,特斯拉官方宣布对旗下两款主销车型,Model3和ModelY进行降价。降价后,Model3后轮驱动版的售价从26。59万元降至2谷歌全球大裁1。2万人!CEO跪求离职创始人急推聊天机器人搜索编辑好困Aeneas新智元导读ChatGPT恐要掀起业界革命,谷歌是真被逼急了。虽然连夜紧急裁员12000人,但仍保留了精锐部队,大力发展AI。硅谷再无养老厂!今天,谷歌创立25年
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网