V8 编译浅谈

简介:本文是一个 V8 编译原理知识的介绍文章,旨在让大家感性的了解 JavaScript 在 V8 中的解析过程。

image.png

作者 | 子弈
来源 | 阿里技术公众号

一 简介

本文是一个 V8 编译原理知识的介绍文章,旨在让大家感性的了解 JavaScript 在 V8 中的解析过程。本文主要的撰写流程如下:

  • 解释器和编译器:计算机编译原理的基础知识介绍
  • V8 的编译原理:基于计算机编译原理的知识,了解 V8 对于 JavaScript 的解析流程
  • V8 的运行时表现:结合 V8 的编译原理,实践 V8 在解析流程中的具体运行表现
本文仅代表个人观点,文中若有错误欢迎指正。

二 解释器和编译器

大家可能一直疑惑的问题:JavaScript 是一门解释型语言吗?要了解这个问题,首先需要初步了解什么是解释器和编译器以及它们的特点是什么。

1 解释器

解释器的作用是将某种语言编写的源程序作为输入,将该源程序执行的结果作为输出,例如 Perl、Scheme、APL 等都是使用解释器进行转换执行:

image.png

2 编译器

编译器的设计是一个非常庞大和复杂的软件系统设计,在真正设计的时候需要解决两个相对重要的问题:

  • 如何分析不同高级程序语言设计的源程序
  • 如何将源程序的功能等价映射到不同指令系统的目标机器

image.png

中间表示(IR)

中间表示(Intermediate Representation,IR)是程序结构的一种表现方式,它会比抽象语法树(Abstract Syntax Tree,AST)更加接近汇编语言或者指令集,同时也会保留源程序中的一些高级信息,具体作用包括:

  • 易于编译器的错误调试,容易识别是 IR 之前的前端还是之后的后端出的问题
  • 可以使得编译器的职责更加分离,源程序的编译更多关注如何转换成 IR,而不是去适配不同的指令集
  • IR 更加接近指令集,从而相对于源码可以更加节省内存空间

image.png

优化编译器

IR 本身可以做到多趟迭代从而优化源程序,在每一趟迭代的过程中可以研究代码并记录优化的细节,方便后续的迭代查找并利用这些优化信息,最终可以高效输出更优的目标程序:

image.png

优化器可以对 IR 进行一趟或者多趟处理,从而生成更快执行速度或者更小体积的目标程序(例如找到循环中不变的计算并对其进行优化从而减少运算次数),也可能用于产生更少异常或者更低功耗的目标程序。除此之外,前端和后端内部还可以细分为多个处理步骤,具体如下图所示:

image.png

3 两者的特性比较

解释器和编译器的具体特性比较如下所示:

image.png

需要注意早期的 Web 前端要求页面的启动速度快,因此采用解释执行的方式,但是页面在运行的过程中性能相对较低。为了解决这个问题,需要在运行时对 JavaScript 代码进行优化,因此在 JavaScript 的解析引擎中引入了 JIT 技术。

4 JIT 编译技术

JIT (Just In Time)编译器是一种动态编译技术,相对于传统编译器而言,最大的区别在于编译时和运行时不分离,是一种在运行的过程中对代码进行动态编译的技术。

image.png

5 混合动态编译技术

为了解决 JavaScript 在运行时性能较慢的问题,可以通过引入 JIT 技术,并采用混合动态编译的方式来提升 JavaScript 的运行性能,具体思路如下所示:

image.png

采用上述编译框架后,可以使得 JavaScript 语言:

  • 启动速度快:在 JavaScript 启动的时候采用解释执行的方式运行,利用了解释器启动速度快的特性
  • 运行性能高:在 JavaScript 运行的过程中可以对代码进行监控,从而使用 JIT 技术对代码进行编译优化

三 V8 的编译原理

V8 是一个开源的 JavaScript 虚拟机,目前主要用在 Chrome 浏览器(包括开源的 Chromium)以及 Node.js 中,核心功能是用于解析和执行 JavaScript 语言。为了解决早期 JavaScript 运行性能差的问题,V8 经历了多个历史的编译框架衍变之后(感兴趣的同学可以了解一下早期的 V8 编译框架设计),引入混合动态编译的技术来解决问题,具体详细的编译框架如下所示:

image.png

1 Ignition 解释器

Ignition 的主要作用是将 AST 转换成 Bytecode(字节码,中间表示)。在运行的过程中,还会使用类型反馈(TypeFeedback)技术并计算热点代码(HotSpot,重复被运行的代码,可以是方法也可以是循环体),最终交给 TurboFan 进行动态运行时的编译优化。Ignition 的解释执行流程如下所示:

image.png

在字节码解释执行的过程中,会将需要进行性能优化的运行时信息指向对应的 Feedback Vector(反馈向量,之前也被称为 Type Feedback Vector),Feeback Vector 中会包含根据内联缓存(Inline Cache,IC)来存储的多种类型的插槽(Feedback Vector Slot)信息,例如 BinaryOp 插槽(二进制操作结果的数据类型)、Invocation Count(函数的调用次数)以及 Optimized Code 信息等。

这里不会过多讲解每个执行流程的细节问题。

2 TurboFan 优化编译器

TurboFan 利用了 JIT 编译技术,主要作用是对 JavaScript 代码进行运行时编译优化,具体的流程如下所示:

image.png

图片出处 An Introduction to Speculative Optimization in V8。

需要注意 Profiling Feedback 部分,这里主要提供 Ignition 解释执行过程中生成的运行时反馈向量信息 Feedback Vector ,Turbofan 会结合字节码以及反馈向量信息生成图示(数据结构中的图结构),并将图传递给前端部分,之后会根据反馈向量信息对代码进行优化和去优化。

这里的去优化是指让代码回退到 Ignition 进行解释执行,去优化本质是因为机器码已经不能满足运行诉求,例如一个变量从 string 类型转变成 number 类型,机器码编译的是 string 类型,此时已经无法再满足运行诉求,因此 V8 会执行去优化动作,将代码回退到 Ignition 进行解释执行。

四 V8 的运行时表现

在了解 V8 的编译原理之后,接下来需要使用 V8 的调试工具来具体查看 JavaScript 的编译和运行信息,从而加深我们对 V8 的编译过程认知。

1 D8 调试工具

如果想了解 JavaScript 在 V8 中的编译时和运行时信息,可以使用调试工具 D8。D8 是 V8 引擎的命令行 Shell,可以查看 AST 生成、中间代码 ByteCode、优化代码、反优化代码、优化编译器的统计数据、代码的 GC 等信息。D8 的安装方式有很多,如下所示:

  • 方法一:根据 V8 官方文档 Using d8 以及 Building V8 with GN 进行工具链的下载和编译
  • 方法二:使用别人已经编译好的 D8 工具,可能版本会有滞后性,例如 Mac 版
  • 方法三:使用 JavaScript 引擎版本管理工具,例如 jsvu,可以下载到最新编译好的 JavaScript 引擎

本文使用方法三安装 v8-debug 工具,安装完成后执行 v8-debug --help 可以查看有哪些命令:

# 执行 help 命令查看支持的参数
v8-debug --helpSynopsis:shell [options] [--shell] [<file>...]d8 [options] [-e <string>] [--shell] [[--module|--web-snapshot] <file>...]-e        execute a string in V8--shell   run an interactive JavaScript shell--module  execute a file as a JavaScript module--web-snapshot  execute a file as a web snapshotSSE3=1 SSSE3=1 SSE4_1=1 SSE4_2=1 SAHF=1 AVX=1 AVX2=1 FMA3=1 BMI1=1 BMI2=1 LZCNT=1 POPCNT=1 ATOM=0
The following syntax for options is accepted (both '-' and '--' are ok):--flag        (bool flags only)--no-flag     (bool flags only)--flag=value  (non-bool flags only, no spaces around '=')--flag value  (non-bool flags only)--            (captures all remaining args in JavaScript)Options:# 打印生成的字节码--print-bytecode (print bytecode generated by ignition interpreter)type: bool  default: --noprint-bytecode# 跟踪被优化的信息--trace-opt (trace optimized compilation)type: bool  default: --notrace-opt--trace-opt-verbose (extra verbose optimized compilation tracing)type: bool  default: --notrace-opt-verbose--trace-opt-stats (trace optimized compilation statistics)type: bool  default: --notrace-opt-stats# 跟踪去优化的信息--trace-deopt (trace deoptimization)type: bool  default: --notrace-deopt--log-deopt (log deoptimization)type: bool  default: --nolog-deopt--trace-deopt-verbose (extra verbose deoptimization tracing)type: bool  default: --notrace-deopt-verbose--print-deopt-stress (print number of possible deopt points)# 查看编译生成的 AST--print-ast (print source AST)type: bool  default: --noprint-ast# 查看编译生成的代码--print-code (print generated code)type: bool  default: --noprint-code# 查看优化后的代码--print-opt-code (print optimized code)type: bool  default: --noprint-opt-code# 允许在源代码中使用 V8 提供的原生 API 语法--allow-natives-syntax (allow natives syntax)type: bool  default: --noallow-natives-syntax

2 生成 AST

我们编写一个 index.js 文件,在文件中写入 JavaScript 代码,执行一个简单的 add 函数:

function add(x, y) {return x + y
}console.log(add(1, 2));

使用 --print-ast 参数可以打印 add 函数的 AST 信息:

v8-debug --print-ast ./index.js[generating bytecode for function: ]
--- AST ---
FUNC at 0
. KIND 0
. LITERAL ID 0
. SUSPEND COUNT 0
. NAME ""
. INFERRED NAME ""
. DECLS
. . FUNCTION "add" = function add
. EXPRESSION STATEMENT at 41
. . ASSIGN at -1
. . . VAR PROXY local[0] (0x7fb8c080e630) (mode = TEMPORARY, assigned = true) ".result"
. . . CALL
. . . . PROPERTY at 49
. . . . . VAR PROXY unallocated (0x7fb8c080e6f0) (mode = DYNAMIC_GLOBAL, assigned = false) "console"
. . . . . NAME log
. . . . CALL
. . . . . VAR PROXY unallocated (0x7fb8c080e470) (mode = VAR, assigned = true) "add"
. . . . . LITERAL 1
. . . . . LITERAL 2
. RETURN at -1
. . VAR PROXY local[0] (0x7fb8c080e630) (mode = TEMPORARY, assigned = true) ".result"[generating bytecode for function: add]
--- AST ---
FUNC at 12
. KIND 0
. LITERAL ID 1
. SUSPEND COUNT 0
. NAME "add"
. PARAMS
. . VAR (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
. . VAR (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
. DECLS
. . VARIABLE (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
. . VARIABLE (0x7fb8c080e580) (mode = VAR, assigned = false) "y"
. RETURN at 25
. . ADD at 34
. . . VAR PROXY parameter[0] (0x7fb8c080e4d8) (mode = VAR, assigned = false) "x"
. . . VAR PROXY parameter[1] (0x7fb8c080e580) (mode = VAR, assigned = false) "y"

我们以图形化的方式来描述生成的 AST 树:

image.png

VAR PROXY 节点在真正的分析阶段会连接到对应地址的 VAR 节点。

3 生成字节码

AST 会经过 Ignition 解释器的 BytecodeGenerator 函数生成字节码(中间表示),我们可以通过 --print-bytecode 参数来打印字节码信息:

v8-debug --print-bytecode ./index.js[generated bytecode for function:  (0x3ab2082933f5 <SharedFunctionInfo>)]
Bytecode length: 43
Parameter count 1
Register count 6
Frame size 48
OSR nesting level: 0
Bytecode Age: 00x3ab2082934be @    0 : 13 00             LdaConstant [0]0x3ab2082934c0 @    2 : c3                Star1 0x3ab2082934c1 @    3 : 19 fe f8          Mov <closure>, r20x3ab2082934c4 @    6 : 65 52 01 f9 02    CallRuntime [DeclareGlobals], r1-r20x3ab2082934c9 @   11 : 21 01 00          LdaGlobal [1], [0]0x3ab2082934cc @   14 : c2                Star2 0x3ab2082934cd @   15 : 2d f8 02 02       LdaNamedProperty r2, [2], [2]0x3ab2082934d1 @   19 : c3                Star1 0x3ab2082934d2 @   20 : 21 03 04          LdaGlobal [3], [4]0x3ab2082934d5 @   23 : c1                Star3 0x3ab2082934d6 @   24 : 0d 01             LdaSmi [1]0x3ab2082934d8 @   26 : c0                Star4 0x3ab2082934d9 @   27 : 0d 02             LdaSmi [2]0x3ab2082934db @   29 : bf                Star5 0x3ab2082934dc @   30 : 63 f7 f6 f5 06    CallUndefinedReceiver2 r3, r4, r5, [6]0x3ab2082934e1 @   35 : c1                Star3 0x3ab2082934e2 @   36 : 5e f9 f8 f7 08    CallProperty1 r1, r2, r3, [8]0x3ab2082934e7 @   41 : c4                Star0 0x3ab2082934e8 @   42 : a9                Return 
Constant pool (size = 4)
0x3ab208293485: [FixedArray] in OldSpace- map: 0x3ab208002205 <Map>- length: 40: 0x3ab20829343d <FixedArray[2]>1: 0x3ab208202741 <String[7]: #console>2: 0x3ab20820278d <String[3]: #log>3: 0x3ab208003f09 <String[3]: #add>
Handler Table (size = 0)
Source Position Table (size = 0)
[generated bytecode for function: add (0x3ab20829344d <SharedFunctionInfo add>)]
Bytecode length: 6
// 接受 3 个参数, 1 个隐式的 this,以及显式的 x 和 y
Parameter count 3
Register count 0
// 不需要局部变量,因此帧大小为 0 
Frame size 0
OSR nesting level: 0
Bytecode Age: 00x3ab2082935f6 @    0 : 0b 04             Ldar a10x3ab2082935f8 @    2 : 39 03 00          Add a0, [0]0x3ab2082935fb @    5 : a9                Return 
Constant pool (size = 0)
Handler Table (size = 0)
Source Position Table (size = 0)

add 函数主要包含以下 3 个字节码序列:

// Load Accumulator Register
// 加载寄存器 a1 的值到累加器中
Ldar a1
// 读取寄存器 a0 的值并累加到累加器中,相加之后的结果会继续放在累加器中
// [0] 指向 Feedback Vector Slot,Ignition 会收集值的分析信息,为后续的 TurboFan 优化做准备
Add a0, [0]
// 转交控制权给调用者,并返回累加器中的值
Return 

这里 Ignition 的解释执行这些字节码采用的是一地址指令结构的寄存器架构。

关于更多字节码的信息可查看 Understanding V8’s Bytecode。

4 优化和去优化

JavaScript 是弱类型语言,不会像强类型语言那样需要限定函数调用的形参数据类型,而是可以非常灵活的传入各种类型的参数进行处理,如下所示:

function add(x, y) { // + 操作符是 JavaScript 中非常复杂的一个操作return x + y
}add(1, 2);
add('1', 2);
add(, 2);
add(undefined, 2);
add([], 2);
add({}, 2);
add([], {});

为了可以进行 + 操作符运算,在底层执行的时候往往需要调用很多 API,比如 ToPrimitive(判断是否是对象)、ToString、ToNumber 等,将传入的参数进行符合 + 操作符的数据转换处理。

在这里 V8 会对 JavaScript 像强类型语言那样对形参 x 和 y 进行推测,这样就可以在运行的过程中排除一些副作用分支代码,同时这里也会预测代码不会抛出异常,因此可以对代码进行优化,从而达到最高的运行性能。在 Ignition 中通过字节码来收集反馈信息(Feedback Vector),如下所示:

image.png

为了查看 add 函数的运行时反馈信息,我们可以通过 V8 提供的 Native API 来打印 add 函数的运行时信息,具体如下所示:

function add(x, y) {return x + y
}// 注意这里默认采用了 ClosureFeedbackCellArray,为了查看效果,强制开启 FeedbackVector
// 更多信息查看: A lighter V8:https://v8.dev/blog/v8-lite
%EnsureFeedbackVectorForFunction(add);
add(1, 2);
// 打印 add 详细的运行时信息
%DebugPrint(add);

通过 --allow-natives-syntax 参数可以在 JavaScript 中调用 %DebugPrint 底层 Native API(更多 API 可以查看 V8 的 runtime.h 头文件):

image.png


 

image.png

image.png

这里的 SharedFunctionInfo(SFI)中保留了一个 InterpreterEntryTrampoline 指针信息,每个函数都会有一个指向 Ignition 解释器的 trampoline 指针,每当 V8 需要进去去优化时,就会使用此指针使代码回退到解释器相应的函数执行位置。

为了使得 add 函数可以像 HotSpot 代码一样被优化,在这里强制做一次函数优化:

image.png

通过 --trace-opt 参数可以跟踪 add 函数的编译优化信息:

image.png

image.png


 

image.png

image.png

需要注意的是 V8 会自动监测代码的结构变化,从而执行去优化。例如下述代码:

function add(x, y) {return x + y
}%EnsureFeedbackVectorForFunction(add);add(1, 2); 
%OptimizeFunctionOnNextCall(add);
add(1, 2); 
// 改变 add 函数的传入参数类型,之前都是 number 类型,这里传入 string 类型
add(1, '2'); 
%DebugPrint(add);

我们可以通过 --trace-deopt 参数跟踪 add 函数的去优化信息:

image.png


 

image.png

image.png

需要注意的是代码在执行去优化的过程中会产生性能损耗,因此在日常的开发中,建议使用 TypeScript 对代码进行类型声明,这样可以一定程度提升代码的性能。

五 总结

本文对于 V8 的研究还处在一个感性的认知阶段,并没有深入到 V8 底层的源码。通过本文可以对 V8 的编译原理有一个感性的认知,同时也建议大家可以使用 TypeScript,它确实能在一定程度上对 JavaScript 代码的编写产生更好的指导作用。

原文链接

本文为阿里云原创内容,未经允许不得转载。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/511761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rabbitmq接收不到消息 防火墙_用PHP+RabbitMQ实现消息的发送和接收

消费者&#xff1a;接收消息逻辑&#xff1a;创建连接-->创建channel-->创建交换机-->创建队列-->绑定交换机/队列/路由键-->接收消息<?php /************************************* * PHP amqp(RabbitMQ) Demo - consumer * Author: Linvo * Date: 2018/7/…

斩获大奖|阿里云PolarDB-X引领云原生分布式数据库新时代

简介&#xff1a;阿里云原生分布式数据库PolarDB-X荣获“2021年度最佳分布式数据库”。 12月15-16日&#xff0c;以“引领分布式云变革 助力湾区数字经济”为主题的全球分布式云大会在深圳隆重召开&#xff0c;本届大会由全球分布式云联盟、深圳科技交流服务中心、深圳市通信学…

Gartner:2022年中国IT支出预计将突破5.5亿美元

2022年全球IT支出预计将达到4.4万亿美元&#xff0c;2022年中国IT支出预计将突破5.5亿美元&#xff0c;相比2021年增长7.76% 分析、云计算、无缝客户体验和安全等领域将成为首席信息官的重点采购和投资领域 根据Gartner的最新预测&#xff0c;2022年全球IT支出预计将达到4.4万亿…

深度 | 从DevOps到BizDevOps, 研发效能提升的系统方法

简介&#xff1a;研发效能提升不知从何下手、一头雾水&#xff1f;阿里资深技术专家一文为你揭秘研发效能提升的系统方法。 注&#xff1a;本文是对云栖大会何勉分享内容的整理 这几年“研发效能”一直是热词&#xff0c;很多组织都会启动研发效能提升专项。我与其中的很多有过…

mac mysql 链接_mac上搭建mysql环境配置和Navicat连接mysql

mac上搭建mysql环境配置注意&#xff1a;mysql版本要和你的MAC版本保持一致2、一路傻瓜式点击下一步此处选择“Use Legacy Password Encryption”&#xff0c;否则使用navicat连接mysql的时候&#xff0c;会报无法加载身份验证的错误。3、环境配置打开终端&#xff0c;输入&…

io_uring vs epoll ,谁在网络编程领域更胜一筹?

简介&#xff1a;从定量分析的角度&#xff0c;通过量化 io_uring 和 epoll 两种编程框架下的相关操作的耗时&#xff0c;来分析二者的性能差异。 本文作者&#xff1a;王小光&#xff0c;「高性能存储技术SIG」核心成员。 背景 io_uring 在传统存储 io 场景已经证明其价值&a…

Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?

作者 | 码哥呀来源 | CSDN博客在《Redis 数据缓存满了怎么办&#xff1f;》我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据。淘汰策略如下所示&#xff1a;redis内存淘汰设置过期时间的 keyvolatile-ttl、volatile-random、volatile-lru、volatile-lfu 这…

量化感知训练实践:实现精度无损的模型压缩和推理加速

简介&#xff1a;本文以近期流行的YOLOX[8]目标检测模型为例&#xff0c;介绍量化感知训练的原理流程&#xff0c;讨论如何实现精度无损的实践经验&#xff0c;并展示了量化后的模型能够做到精度不低于原始浮点模型&#xff0c;模型压缩4X、推理加速最高2.3X的优化效果。 1. 概…

此表单只能填写一次_暴雪战网国服账号修改邮箱只能填写表单申请

暴雪战网国服账号只认身份信息&#xff0c;注册必须实名&#xff0c;而且实名信息千万不要乱填&#xff0c;不然账号出现问题&#xff0c;需要上传证件图片的&#xff0c;客服会核实与注册实名内容是否一致&#xff0c;不然无法帮助玩家解决一些问题。国服账号邮箱没有什么权限…

贾扬清演讲实录:一个AI开发者的奇幻漂流

简介&#xff1a;2021阿里灵杰AI工程化峰会&#xff0c;贾扬清深度解读阿里灵杰大数据和AI一体化平台。 演讲人&#xff1a;贾扬清 演讲主题&#xff1a;一个AI开发者的奇幻漂流 活动&#xff1a;2021阿里灵杰AI工程化峰会 对于绝大多数人来说&#xff0c;这一波AI浪潮兴许…

上云避坑指南100篇|「云」上玩法虽多,小心水土不服

商业智能BI发展至今&#xff0c;从市场增速来看&#xff0c;我国已进入 BI 及 DA&#xff08;数据分析&#xff09;领域的第一方阵&#xff0c;并成为发展最快的国家之一。 IDC 数据显示&#xff0c;2020 年中国商业智能软件市场规模为 5.8 亿美元&#xff0c;同比增长 17.1%&a…

如何基于LSM-tree架构实现一写多读

简介&#xff1a;传统MySQL基于binlog复制的主备架构有它的局限性&#xff0c;包括存储空间有限&#xff0c;备份恢复慢&#xff0c;主备复制延迟等问题&#xff0c;为了解决用户对于云上RDS(X-Engine)大容量存储&#xff0c;以及弹性伸缩的诉求&#xff0c;PolarDB推出了历史库…

Dubbo-go v3.0 正式发布 ——打造国内一流开源 Go 服务框架

简介&#xff1a;Dubbo-go 是常新的&#xff0c;每年都在不断进化。介绍 Dubbo-go 3.0 工作之前&#xff0c;先回顾其过往 6 年的发展历程&#xff0c;以明晰未来的方向。 作者 | 李志信 来源 | 阿里技术公众号 作者介绍&#xff1a; 李志信&#xff08;github laurencelizhix…

谁还没经历过死锁呢?

作者 | 敖丙来源 | 敖丙之前刚学习多线程时&#xff0c;由于各种锁的操作不当&#xff0c;经常不经意间程序写了代码就发生了死锁&#xff0c;不是在灰度测试的时候被测出来&#xff0c;就是在代码review的时候被提前发现。这种死锁的经历不知道大家有没有&#xff0c;不过怎么…

阿里巴巴超大规模Kubernetes基础设施运维体系解读

简介&#xff1a;ASI&#xff1a;Alibaba Serverless infrastructure&#xff0c;阿里巴巴针对云原生应用设计的统一基础设施。ASI 基于阿里云公共云容器服务 ACK之上&#xff0c;支撑集团应用云原生化和云产品的Serverless化的基础设施平台。 作者 | 仔仁、墨封、光南 来源 | …

搜索NLP行业模型和轻量化客户定制

简介&#xff1a;开放搜索NLP行业模型和轻量化客户定制方案&#xff0c;解决减少客户标注成本、完全无标注或少量简单标注的等问题&#xff0c;让搜索领域扩展更易用。 特邀嘉宾&#xff1a; 徐光伟&#xff08;昆卡&#xff09;--阿里巴巴算法专家 搜索NLP算法 搜索链路 …

CICD 的供应链安全工具 Tekton Chains

作者 | Addo Zhang来源 | 云原生指北软件供应链是指进入软件中的所有内容及其来源&#xff0c;简单地可以理解成软件的依赖项。依赖项是软件运行时所需的重要内容&#xff0c;可以是代码、二进制文件或其他组件&#xff0c;也可以是这些组件的来源&#xff0c;比如存储库或者包…

python计算不规则图形面积_python opencv中的不规则形状检测和测量

正如我在评论中提到的那样,对于这个问题,分水岭似乎是一个很好的方法.但是当你回答时,定义标记的前景和背景是困难的部分&#xff01;我的想法是使用形态梯度沿着冰晶获得良好的边缘并从那里开始工作;形态梯度似乎很有效.import numpy as npimport cv2img cv2.imread(image.pn…

深度解析开源推荐算法框架EasyRec的核心概念和优势

简介&#xff1a;如何通过机器学习PAI实现快速构建推荐模型 作者&#xff1a;程孟力 - 机器学习PAI团队 随着移动app的普及&#xff0c;个性化推荐和广告成为很多app不可或缺的一部分。他们在改善用户体验和提升app的收益方面带来了巨大的提升。深度学习在搜广推领域的应用也…

助力公益数字化 火山引擎向公益机构捐赠多款技术产品

5月18日&#xff0c;字节跳动公益联合火山引擎举办了“科技应用创新让公益更美好”线上交流会&#xff0c;与中国红十字基金会、壹基金等多家公益机构探讨如何利用科技信息化产品提升公益事业的效率&#xff0c;从而进一步解决社会问题。 交流会上&#xff0c;火山引擎联合Pic…