Android硬件渲染流程

Android硬件渲染流程

  • 一.渲染流程
    • 1.VSync信号的监听
    • 2.VSync信号触发绘制
  • 二.渲染原理
    • 1.画布的获取
      • 1.1 画布的创建
      • 1.2 渲染指令列表的创建
    • 2.绘制与渲染指令
      • 2.1 矩形的绘制
      • 2.2 硬件渲染指令
      • 2.3 节点的绘制
    • 3.绘制的提交
      • 3.1 绘制结果的保存
      • 3.2 绘制结果的获取
    • 4.层级的构建
      • 4.1 绘制结果的更新
      • 4.2 构建的分发
      • 4.3 构建数据的保存
    • 5.渲染与渲染管线
      • 5.1 节点的更新
      • 5.2 节点的渲染
  • 三.总结
    • 1.硬件渲染
    • 2.绘制流程
      • 2.1 渲染指令
      • 2.2 对应关系
    • 3.渲染流程
      • 3.1 渲染指令列表的获取
      • 3.2 渲染层级的构建
      • 3.3 渲染管线的渲染

一.渲染流程

1.VSync信号的监听

    在Android中,App的渲染流程是从ViewRootImpl开始的。在回调Activity的onResume方法后,会调用ViewRootImpl的requestLayout方法触发页面中View的测量与绘制。

    在requestLayout方法中,首先会调用checkThread方法检查当前线程是否为UI线程,如果不是,则抛出异常。接下来会调用scheduleTraversals方法。
0
    在ViewRootImpl的scheduleTraversals方法中,主要做了两件事:

1)向主线程的消息队列发送一个同步信息屏障。

2)提交callbackType类型为CALLBACK_TRAVERSAL的TraversalRunnable。
1

2.VSync信号触发绘制

    当VSync信号到来时,会执行TraversalRunnable的run方法,该方法内部会调用ViewRootImpl的doTraversal方法。
2
    在ViewRootImpl的doTraversal方法中,主要做了两件事:

1)移除主线程消息队列的同步信息屏障。

2)调用performTraversals方法。
3
    在ViewRootImpl的performDraw方法中,会调用draw方法。在ViewRootImpl的draw方法中,如果开启了硬件渲染,就会从mAttachInfo的mThreadedRenderer中获取ThreadedRenderer。并调用ThreadedRenderer的draw方法。
4
    在ThreadedRenderer的draw方法中主要做了两件事:

1)从DecorView开始递归构建DisplayList。

2)唤醒Render线程对DisplayList进行渲染。
5
    在ThreadedRenderer的updateRootDisplayList中主要做了四件事:

1)从DecorView开始向下分发draw方法,递归构建DisplayListOp。

2)获取最顶层的RecordingCanvas。

3)通过DecorView获取最终的RenderNode并绘制到RecordingCanvas上。

4)将DisplayListOp填充到Native层的RootRenderNode中。
6

二.渲染原理

1.画布的获取

    在硬件渲染中,每个View都有一个RenderNode。当调用RenderNode的beginRecording方法时,内部会调用RecordingCanvas的静态方法obtain获取RenderNode。
7    在RecordingCanvas的静态方法obtain中,会创建RecordingCanvas。
8
    在RecordingCanvas的构造方法中,主要做了两件事:

1)创建Native层Canvas并返回对应的地址。

2)调用父类的构造方法对返回的地址进行保存。
9

1.1 画布的创建

    RecordingCanvas的nCreateDisplayListCanvas方法对应的Native实现为android_graphics_DisplayListCanvas的android_view_DisplayListCanvas_createDisplayListCanvas函数。

    在android_view_DisplayListCanvas_createDisplayListCanvas函数中,主要做了三件事:

1)获取Native层的RenderNode。

2)创建Canvas。

3)返回Canvas对应的地址。
10
    在Canvas::create_recording_canvas方法中,会创建SkiaRecordingCanvas。
11
    在SkiaRecordingCanvas的构造方法中,会调用initDisplayList方法,初始化DisplayList。
12

1.2 渲染指令列表的创建

    在SkiaRecordingCanvas的initDisplayList方法中,主要做了三件事:

1)清除上一帧的SkiaDisplayList,如果SkiaDisplayList为空,则再创建一个SkiaDisplayList。

2)绑定SkiaDisplayList与RecordingCanvas。

3)将RecordingCanvas保存到SkiaCanvas中。
13
    在SkiaDisplayList的attachRecorder方法中,会将SkiaDisplayList中的SkiaDisplayData与RecordingCanvas绑定。
14

2.绘制与渲染指令

2.1 矩形的绘制

    在硬件绘制过程中,当调用Canvas的drawRect方法时,在Canvas的drawRect方法中,会调用nDrawRect方法。
15
    Canvas的nDrawRect方法对应的Native实现为android_graphics_Canvas的drawRect函数。在drawRect函数中,主要做了两件事:

1)获取Native层Canvas。

2)通过Canvas绘制矩形。
16
    SkiaRecordingCanvas继承自SkiaCanvas。这里的drawRect方法在SkiaRecordingCanvas的父类SkiaCanvas中实现。

    在SkiaCanvas的drawRect方法中,会调用SkCanvas的drawRect方法。
17
    在SkCanvas的drawRect方法中,会调用子类RecordingCanvas的onDrawRect方法。
18
    在RecordingCanvas的onDrawRect方法中,会调用之前保存的DisplayListData的drawRect方法。在DisplayListData的drawRect方法中,会创建一个DrawRect指令并保存。
19

2.2 硬件渲染指令

    在DisplayListData中,所有的绘制指令都存储在一块连续的内存中。

    在Android中,所有的绘制指令都继承了Op。Op是一个结构体,在Op中有两个字段,type表示指令的类型,skip表示指令的长度。

struct Op {
    uint32_t type : 8;
    uint32_t skip : 24;
  };

    在DisplayListData的push方法中,主要做了四件事:

1)计算当前指令的长度。

2)判断所有的绘制指令是否超过内存最大容量,如果超过最大容量,则进行扩容,重新分配内存,每次扩容增加4096个字节。

3)计算当前绘制指令在内存中的位置,并在指定位置处创建当前指令的实例对象。

4)为当前绘制指令的type和skip赋值。
20

2.3 节点的绘制

    在ThreadedRenderer的updateViewTreeDisplayList方法中,会调用View的updateDisplayListIfDirty方法。
21    在View的updateDisplayListIfDirty方法中,主要做了三件事:

1)从自身的RenderNode中获取RecordingCanvas。

2)通过Flag判断,如果是ViewGroup且自身不用绘制,则分发子View去绘制,否则直接绘制。

3)结束绘制。
22
    在View的draw方法中,首先会绘制自身。然后对子View进行绘制。dispatchDraw方法在View中为空实现,如果一个View不是ViewGroup,那么dispatchDraw方法不会对任何View进行绘制分发。
23
    View的dispatchDraw方法在ViewGroup中被重写。如果一个View是ViewGroup,那么dispatchDraw方法会对子View进行绘制分发。在ViewGroup的dispatchDraw方法中,会调用drawChild方法。
24
    在ViewGroup的drawChild方法中,会调用View的draw方法。这里的draw方法是View中一个重载的draw方法,只在ViewGroup中调用。
25
    在View重载的draw方法中,主要做了三件事:

1)判断是否开启硬件渲染。

2)如果开启硬件渲染,则对当前View构建DisplayList,保存到当前View的RenderNode并返回。

3)如果当前View的DisplayList不为空,则将当前View的RenderNode绘制到父View的Canvas上。
26
    在RecordingCanvas的drawRenderNode方法中,会调用nDrawRenderNode方法。
27
    RecordingCanvas的nDrawRenderNode方法对应的Native层实现为android_graphics_DisplayListCanvas的android_view_DisplayListCanvas_drawRenderNode函数。

    在android_view_DisplayListCanvas_drawRenderNode函数中,主要做了三件事:

1)根据地址获取Native层Canvas。

2)根据地址获取View对应的Native层的RenderNode。

3)将RenderNode绘制到Canvas上。
28
    根据硬件渲染中Canvas的创建过程可以知道,这里的Canvas实际上是SkiaRecordingCanvas。

    在SkiaRecordingCanvas的drawRenderNode方法中,主要做了两件事:

1)将RenderNode封装成RenderNodeDrawable,保存到SkiaDisplayList中用于记录。

2)对封装好的RenderNodeDrawable进行绘制。
29
    SkiaRecordingCanvas继承自SkiaCanvas。这里的drawDrawable方法在SkiaRecordingCanvas的父类SkiaCanvas中实现。

    在SkiaCanvas的drawDrawable方法中,会调用SkCanvas的drawDrawable方法。
30
    在SkCanvas的drawDrawable方法中,会调用子类RecordingCanvas的onDrawDrawable方法。
31
    在RecordingCanvas的onDrawDrawable方法中,会调用之前保存的DisplayListData的drawDrawable方法。在DisplayListData的drawDrawable方法中,会创建一个DrawDrawable指令并保存。
32

3.绘制的提交

3.1 绘制结果的保存

    在RenderNode的endRecording方法中,主要做了两件事:

1)停止记录,标记DisplayList可用。

2)释放RecordingCanvas。
33
    在RecordingCanvas的finishRecording方法中,会调用nFinishRecording方法。
34
    RecordingCanvas的nFinishRecording方法对应的Native实现为android_graphics_DisplayListCanvas的android_view_DisplayListCanvas_finishRecording函数。

    在android_view_DisplayListCanvas_finishRecording函数中,主要做了三件事:

1)获取Canvas,实际获取的是SkiaRecordingCanvas。

2)获取RenderNode。

3)调用SkiaRecordingCanvas的finishRecording结束绘制。
35
    在SkiaRecordingCanvas的finishRecording方法中,主要做了三件事:

1)暂停标记并获取SkiaDisplayList。

2)将SkiaDisplayList封装成DisplayList,DisplayList是对SkiaDisplayListWrapper的重命名,SkiaDisplayListWrapper会保存SkiaDisplayList。

3)将DisplayList保存到RenderNode中。
36

3.2 绘制结果的获取

    在DisplayList构建完成后, 会调用ThreadedRenderer的syncAndDrawFrame方法唤醒Render线程进行渲染。在ThreadedRenderer的syncAndDrawFrame中,会调用nSyncAndDrawFrame方法。
37
    ThreadedRenderer的nSyncAndDrawFrame方法对应的Native实现为android_graphics_HardwareRenderer的android_view_ThreadedRenderer_syncAndDrawFrame函数。

    在android_view_ThreadedRenderer_syncAndDrawFrame函数中,主要做了两件事:

1)获取RenderProxy。

2)调用RenderProxy的syncAndDrawFrame方法。
38
    在RenderProxy的syncAndDrawFrame方法中,会调用DrawFrameTask的drawFrame方法。在DrawFrameTask的drawFrame方法中,会调用postAndWait方法。
39
    在DrawFrameTask的postAndWait方法中,主要做了三件事:

1)对DrawFrameTask的run方法进行封装。

2)将封装后的对象添加到RenderThread的队列中。

3)UI线程进入阻塞状态。
40
    当RenderTread执行任务时,会调用DrawFrameTask的run方法。在DrawFrameTask的run方法中,主要做了三件事:

1)获取UI线程构建的DisplayList。

2)唤醒UI线程。

3)根据DisplayList进行绘制。
41

4.层级的构建

    在DrawFrameTask的syncFrameState方法中,主要做了两件事:

1)处理硬件加速层,如TextureView的绘制。

2)构建TreeInfo。
42
    在CanvasContext的prepareTree方法中,主要做了两件事:

1)保存LayerUpdateQueue到TreeInfo中,为后续后构建TreeInfo做准备。LayerUpdateQueue用于保存待绘制的RenderNode。

2)遍历RenderNode构建TreeInfo。
43
    在RenderNode的prepareTreeImpl方法中,主要做了三件事:

1)获取DisplayList。

2)通过DisplayList分发子节点构建TreeInfo。

3)将完成构建的当前RenderNode保存到LayerUpdateQueue中。
44

4.1 绘制结果的更新

    在RenderNode的pushStagingDisplayListChanges方法中,会调用syncDisplayList方法,对DisplayList进行锁定保存。
45
    在RenderNode的syncDisplayList方法中,主要做了三件事:

1)遍历mStagingDisplayList中保存的RenderNode,对RednerNode的引用加1。mStagingDisplayList是用于暂存本次构建好的DisplayList的变量。

2)遍历mDisplayList中保存的RenderNode,对RednerNode的引用减1,并清空mDisplayList中对SkiaDisplayList的引用。mDisplayList是用于保存下次待渲染的DisplayList的变量。

3)将本次构建好的DisplayList保存到下次待渲染的DisplayList。
46
    DisplayList是对SkiaDisplayListWrapper的重命名。在SkiaDisplayListWrapper的updateChildren方法中,会调用SkiaDisplayList的updateChildren方法。
47
    在SkiaDisplayList的updateChildren方法中,主要做了三件事:

1)遍历获取RenderNodeDrawable。

2)从RenderNodeDrawable中获取RenderNode。

3)将RenderNode作为参数,执行参数中传入的function方法。
48

4.2 构建的分发

    DisplayList是对SkiaDisplayListWrapper的重命名。在SkiaDisplayListWrapper的prepareListAndChildren方法中,会调用SkiaDisplayList的prepareListAndChildren方法。
49
    在SkiaDisplayList的prepareListAndChildren方法中,主要做了四件事:

1)遍历获取RenderNodeDrawable。

2)从RenderNodeDrawable中获取RenderNode。

3)对TreeInfo中的属性进行更新。

4)将RenderNode和TreeInfo作为参数,执行参数中传入的function方法。
50
    这里传入的function是RenderNode的prepareTreeImpl方法,这样就实现了子RenderNode构建TreeInfo。

4.3 构建数据的保存

    在RenderNode的pushLayerUpdate方法中,会对当前的RenderNode进行保存。
51

5.渲染与渲染管线

    在Android中,渲染管线有两种:SkiaOpenGLPipeline和SkiaVulkanPipeline,底层实现分别对应着OpenGL和Vulkan。下面所有的IRenderPipeline以SkiaOpenGLPipeline为例。

    在CanvasContext的draw方法中,主要做了三件事:

1)获取可绘制的缓存。

2)使用GPU按照绘制指令绘制界面。

3)将绘制好的图形缓冲通过Binder交给SurfaceFlinger进行合成与显示,即上帧。
52
    在SkiaOpenGLPipeline的draw方法中,主要做了两件事:

1)创建SkSurface指针并初始化。

2)开始渲染。
53

    SkiaOpenGLPipeline的renderFrame方法在SkiaOpenGLPipeline的父类SkiaPipeline中实现。

    在SkiaPipeline的renderFrame方法中,主要做了四件事:

1)获取SkCanvas。

2)处理发生变化的Layer,更新对应RenderNode。

3)对所有的renderNode进行绘制。

4)释放SkCanvas。
54

5.1 节点的更新

    在SkiaPipeline的renderLayersImpl方法中,主要做了三件事:

1)遍历LayerUpdateQueue,获取Entry,并从Entry中获取RenderNode.

2)从RenderNode中获取SkCanvas。

3)将RenderNode分装成RenderNodeDrawable,并绘制到SkCanvas上。
55
    RenderNodeDrawable继承自SkDrawable。在RenderNodeDrawable的draw方法中,会调用onDraw方法,onDraw方法在RenderNodeDrawable被重写。在RenderNodeDrawable的onDraw方法中,会调用forceDraw方法。在RenderNodeDrawable的forceDraw方法中,会调用drawContent方法。
56
    在RenderNodeDrawable的drawContent方法中,主要做了两件事:

1)从RenderNode中获取DisplayList。

2)绘制DisplayList。
57
    在SkiaDisplayList的draw方法中,会调用DisplayListData的draw方法。
58
    在DisplayListData的draw方法中,会调用map方法。在DisplayListData的map方法中,主要做了五件事:

1)计算绘制指令内存中绘制指令的终止区域。

2)从绘制指令内存的起始位置进行遍历。

3)获取绘制指令的类型和长度。

4)根据指令的类型,调用指令的绘制方法。

5)根据指令的长度,计算下一个绘制指令的位置。
59
    Array<Fn>是模版生成的代码,其中的模版参数Fn代表不同类型的绘制指令的draw方法的调用。以DrawRect指令为例,当ptr指针指向DrawRect指令时,会根据指令类型从Array<Fn>取出对应DrawRect的draw方法调用的Fn,当调用Fn时,会触发DrawRect的draw方法的执行。

    在DrawRect的draw方法中,会通过SkCanvas的draw方法完成绘制。
60

5.2 节点的渲染

    在SkiaPipeline的renderFrameImpl方法中,主要做了两件事:

1)对RenderNode进行遍历,将RenderNode封装成RenderNodeDrawable。

2)对RenderNodeDrawable进行渲染。
61
    与renderLayerImpl不同,renderFrameImpl绘制到缓存对应的SkCanvas上,而不是自身的SkCanvas上。

三.总结

1.硬件渲染

    Android硬件渲染分成两个部分:渲染指令列表的构建和渲染指令列表的渲染,分别对应着ThreadedRenderer的updateRootDisplayList方法和syncAndDrawFrame方法。即绘制过程和渲染过程是分开的。

    绘制过程发生在UI线程,渲染过程发生在Render线程。

2.绘制流程

    在硬件渲染中,每个View对应着一个RenderNode。每个RenderNode中保存着对应的SkiaDisplayList。

    硬件的绘制过程,本质上是将待绘制的数据封装成对应的硬件渲染指令,保存到SkiaDisplayList中。子View绘制到完成后,父View会对子View的RenderNode进行绘制,将子View的RenderNode绘制指令保存到SkiaDisplayList中,形成绘制的层级结构。

    画布的获取本质上是对SkiaDisplayList的创建与初始化。

    绘制的提交本质上是将构建好的SkiaDisplayList保存到RenderNode中。

2.1 渲染指令

    在Android中,所有的绘制指令都继承了Op。Op是一个结构体,在Op中有两个字段,type表示指令的类型,skip表示指令的长度。

struct Op {
    uint32_t type : 8;
    uint32_t skip : 24;
  };

    所有的渲染指令都保存到DisplayListData中,DisplayListData本质上是一块连续的内存。

2.2 对应关系

    SkiaRecordingCanvas继承自SkiaCanvas,RecordingCanvas继承自SkCanvas。

    RecordingCanvas负责管理硬件渲染指令和DisplayListData、操作硬件渲染指令在DisplayListData上的填充。SkiaRecordingCanvas是对RecordingCanvas的封装,负责DisplayListData的大小分配与初始化、SkiaDisplayList的管理和子View的RenderNode更新保存。

    RecordingCanvas直接操作DisplayListData,SkiaRecordingCanvas直接操作SkiaDisplayList。SkiaDisplayList是对DisplayListData的屏蔽封装。

3.渲染流程

    硬件的渲染过程分成三部分:渲染指令列表(SkiaDisplayList)的获取、渲染层级(TreeInfo)的构建、渲染管线(IRenderPipeline)的渲染。

3.1 渲染指令列表的获取

    渲染指令列表的获取过程会阻塞UI线程,在获取完成后会唤醒UI线程。

3.2 渲染层级的构建

    渲染层级的构建过程本质上是从DecorView的RootRenderNode开始向下判断哪些子View的RenderNode发生了变化,并把变化的RenderNode保存到TreeInfo中。

3.3 渲染管线的渲染

    在Android中,渲染管线有两种:SkiaOpenGLPipeline和SkiaVulkanPipeline,底层实现分别对应着OpenGL和Vulkan。

    渲染管线的渲染过程分成两部分:RenderNode的更新渲染和RenderNode的最终渲染。

    渲染管线在渲染过程中会将RenderNode封装成RenderNodeDrawable。RenderNode的更新渲染本质上就是在RenderNode自身的SkCanvas上绘制。RenderNode的最终渲染本质上就是在生产消费模型对应的SkCanvas上绘制。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker 安装kingbase V8r6

下载 官网下载&#xff0c;注意&#xff1a;这里下载 Docker 版本v8r6 安装 # 导入镜像 docker load -i kingbase.tar# 重命名 docker tag [image-name]:[tag] [new-image-name]:[new-tag]# 删除 docker rmi [image-name]:[tag]# 创建容器 docker run -tid \ --privileged \…

2024-6-遥远的救世主

2024-6-遥远的救世主 2024-4-18 豆豆 fatux&#xff1a; 2021.5.26 看完电视剧《天道》之后购买本书&#xff0c;断断续续一直没有读完。 非常好奇&#xff0c;一个什么样的作者能写出如此奇书。老丁&#xff0c;一个智者&#xff0c;智者是多么孤独&#xff0c;因为找不到同…

Javascript特效之鼠标悬停特效【css】

先看一看效果&#xff0c;是不是很炫酷啊&#xff1f;&#xff1f; HTML代码&#xff1a; <!DOCTYPE html> <html><head><meta charset""UTF-8"" /><title>CSS特效&#xff1a;鼠标悬停效果</title><link rel&q…

Linux下Vision Mamba环境配置+多CUDA版本切换

上篇文章大致讲了下Vision Mamba的相关知识&#xff0c;网上关于Vision Mamba的配置博客太多&#xff0c;笔者主要用来整合下。 笔者在Win10和Linux下分别尝试配置相关环境。 Win10下配置 失败 \textcolor{red}{失败} 失败&#xff0c;最后出现的问题如下&#xff1a; https://…

深入了解数据库设计中的规范化与反规范化

目录 零、前言 一、一些基本术语 二、关系模式 2.1. 什么是关系模式 2.2. 示例 三、数据依赖 3.1. 函数依赖 3.1.1. 完全函数依赖 3.1.2. 部分函数依赖 3.1.3. 传递函数依赖 3.2. 多值依赖 3.3. 连接依赖 四、规范化 4.1. 第一范式&#xff08;1NF&#xff09; …

C++/ cuda kernel中的模版元编程识别 kernel 模版的数据类型

1&#xff0c;模版元编程 模板元编程是一种利用 C 模板系统在编译时进行计算和生成代码的技术。其原理基于模板特化、递归、模板参数推导等特性&#xff0c;通过模板实例化和展开&#xff0c;在编译时生成代码&#xff0c;以实现在编译期间进行复杂计算和代码生成的目的。 2&am…

Git--本地仓库

文章目录 工作区和暂存区工作区&#xff08;Working Directory&#xff09;版本库&#xff08;Repository&#xff09; 初始化git仓库添加文件到版本库步骤 查看修改内容查看工作区和暂存区状态已add文件已修改/新增 的未add文件git跟踪修改原理 查看提交历史版本回退撤销修改撤…

【组合数学】常考试题答案

一、单项选择题&#xff08;每小题3分&#xff0c;共15分&#xff09; 1. 用3个“1”和4个“0”能组成&#xff08; &#xff09;个不同的二进制数字。 A. 35 B. 36, C. 37, D. 38 2. 整除300的正整数的个数为&#xff08;  &#xff09;。 A. 14…

面试中算法(A星寻路算法)

一、问题需求&#xff1a; 迷宫寻路游戏中&#xff0c;有一些小怪物要攻击主角&#xff0c;现在希望你给这些小怪物加上聪 明的AI (Artificial Intelligence&#xff0c;人工智能&#xff09;&#xff0c;让它们可以自动绕过迷宫中的障碍物&#xff0c;寻找到主角的所在。 A星…

DNS服务的部署与配置(2)

1、dns的安装及开启 dnf install bind.x86_64 -y #安装 #Berkeley Internet Name Domain (BIND) systemctl enable --now named #启用dns服务&#xff0c;服务名称叫named firewall-cmd --permanent --add-servicedns #火墙设置 firewall-cmd --reload …

学 Java 具体能干什么?

学习 Java 后&#xff0c;你可以从事许多不同的工作和项目&#xff0c;涵盖了广泛的应用领域。以下是一些具体的应用场景和工作方向&#xff1a; 1. 企业级应用开发 Java 是企业级应用开发的首选语言之一&#xff0c;特别适合开发大规模、分布式、多层次的企业应用程序。 Jav…

使用 LangFuse 意外被挂马!我是怎么恢复系统稳定的?

在使用 LangFuse 过程中,被意外挂马!通过一番折腾服务恢复正常~ 本文将详细介绍应对恶意脚本和进程的完整方案,包括识别、清理、恢复和预防步骤。 阿里云扫到的信息 被执行的 Base64 SUlaQnRTCmV4ZWMgJj4vZGV2L251bGwKSUhDa0hQbmQ9Li8uJChkYXRlfG1kNXN1bXxoZWFkIC1jMjApCl…

深度学习面试问题总结(21)| 模型优化

本文给大家带来的百面算法工程师是深度学习模型优化面试总结&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;我们还将介绍一些常见的深度学习面试问题&#xff0c;并提供参考的回答及其理论基础&a…

【WEEK13】 【DAY5】Shiro第五部分【中文版】

2024.5.24 Friday 接上文【WEEK13】 【DAY4】Shiro第四部分【中文版】 目录 15.7.Shiro请求授权的实现15.7.1.修改ShiroConfig.java15.7.1.1.添加一行验证授权的代码15.7.1.2.重启 15.7.2.修改MyController.java15.7.3.修改ShiroConfig.java15.7.4.重启15.7.5.修改UserRealm.ja…

汽车以太网发展现状及挑战

一、汽车以太网技术联盟 目前推动汽车以太网技术应用与发展的组织包括&#xff1a;OPEN Alliance&#xff08;One-Pair Ether-Net Alliance SIG&#xff09;联盟&#xff0c;主要致力于汽车以太网推广与使用&#xff0c;该联盟通过推进 BroadR- Reach 单对非屏蔽双绞线以太网传…

深入探索python编程中的字典结构

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、字典的特点与基础操作 二、安全访问与哈希函数 三、字典的应用案例 四、总结 在编程的…

基于Python Selenium web测试工具 - 基本用法详解

这篇文章主要介绍了Selenium&#xff08;Python web测试工具&#xff09;基本用法,结合实例形式分析了Selenium的基本安装、简单使用方法及相关操作技巧,需要的朋友可以参考下 本文实例讲述了Selenium基本用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; Seleni…

代码随想录|Day42|动态规划 part07|● 70. 爬楼梯 (进阶)● 322. 零钱兑换 ● 279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 322. 零钱兑换 class Solution: def climbStairs(self, n: int) -> int: if n < 1: return n dp [0] * (n 1) dp[0] 0 dp[1] 1 dp[2] 2 for i in range(3, n 1): dp[i] dp[i - 1] dp[i - 2] return dp[n] 279.完全平方数…

moviepy入门

1. 简介 由于恶心的工作和没有规划的部门安排&#xff0c;我被排到了算法部门&#xff0c;从事和算法没有半毛钱关系的业务上&#xff0c;也就是。。。搞视频。咋说呢&#xff1f;视频这东西我没有一点基础&#xff0c;还好有前人写好的代码&#xff0c;用的是moviepy和ffmpeg…

在CSDN上成长的感悟,你的粉丝长啥样?

文章目录 一、写作的初衷1. 记录所学内容2.巩固所学知识3.分享与帮助4.方便后续查找5.获取激励 二、你的粉丝长啥样&#xff1f;1. 粉丝的特点与困惑2. 关于粉丝&#xff0c;细思极恐 三、继续前行、坚持初心 在CSDN上写博文&#xff0c;对于我来说&#xff0c;不仅仅是一个记录…