ASP.NET Core AOT

        Native AOT 最初在 .NET 7 中引入,在即将发布的 .NET 8 版本中可以与 ASP.NET Core 一起使用。在这篇文章中,我们从总体角度审视其优点和缺点,并进行测量以量化不同平台上的改进。

源代码:https://download.csdn.net/download/hefeng_aspnet/88689164

介绍
        .NET 8 版本即将发布,将为 ASP.NET Core带来一项新功能:本机提前编译,简称本机 AOT 。通常,在构建和部署 ASP.NET Core 应用程序时,C# 代码首先由 Roslyn(C# 编译器)编译为 Microsoft 中间语言 (MSIL)。在运行时,公共语言运行时 (CLR  ) 的即时编译器 (JIT) 将 MSIL 编译为其运行平台的本机代码(遵循著名的“编译一次,到处运行”原则)。本机 AOT 会将您的代码直接编译为选定目标平台的本机代码 - 运行时不再涉及 JIT。微软承诺更小的磁盘上的应用程序大小、更快的启动时间以及更少的执行期间的内存消耗。

        在这篇文章中,我们将从一般角度探讨本机 AOT 的优点和缺点,所以让我们深入研究它。

本土化
        首先,我创建一个.NET 8 ASP.NET Core 空 Web 应用程序项目,并 通过在 csproj 文件中设置PublishAot MSBuild 属性来激活本机 AOT 。我还禁用了<ImplicitUsings>,以便您可以看到引用代码的实际 using 语句,并在 <InvariantGlobalization>模式下运行,以消除特定于区域性的开销并简化部署 - 您可以在此处阅读有关它的更多信息。

        当您现在运行应用程序时(例如从 IDE 或使用 dotnet run),您将体验不到任何差异。本机 AOT 仅在您发布应用程序时编译为本机代码。在这篇介绍性文章中,我想进行一些测量以确定本机 AOT 和 JIT CLR 部署之间的差异。为此,我将稍微修改 Program.cs:

<Project Sdk="Microsoft.NET.Sdk.Web"><PropertyGroup><TargetFramework>net8.0</TargetFramework><Nullable>enable</Nullable><PublishAot>true</PublishAot><InvariantGlobalization>true</InvariantGlobalization><DefineConstants Condition="'$(PublishAot)' == 'true'">$(DefineConstants);IS_NATIVE_AOT</DefineConstants></PropertyGroup>
</Project>
using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;long initialTimestamp = Stopwatch.GetTimestamp();var builder =
#if IS_NATIVE_AOTWebApplication.CreateSlimBuilder(args);
#elseWebApplication.CreateBuilder(args);
#endifvar app = builder.Build();
app.MapGet("/", () => "Hello World!");
await app.StartAsync();TimeSpan elapsedStartupTime = Stopwatch.GetElapsedTime(initialTimestamp);
Console.WriteLine($"Startup took {elapsedStartupTime.TotalMilliseconds:N3}ms");double workingSet = Process.GetCurrentProcess().WorkingSet64;
Console.WriteLine($"Working Set: {workingSet / (1024 * 1024):N2}MB");await app.StopAsync();

        首先要注意的是在 Native AOT 模式下使用WebApplicaiton.CreateSlimBuilder而不是WebApplication.CreateBuilder – 这将减少默认注册的 ASP.NET Core 功能 数量,从而减少应用程序大小和内存使用量。我插入了对 Stopwatch.GetTimestamp和Stopwatch.GetElapsedTime的调用 来测量 Web 服务器的启动时间。 请注意,我还将调用从app.Run更改为app.StartAsync - 这样,我们不会阻塞执行线程,直到 Web 应用程序实际关闭,而只是直到 HTTP 服务器成功启动为止。此外,我们还获取进程的WorkingSet64 RAM 使用值并打印出已用内存(以兆字节为单位)。测量后,我们使用app.StopAsync正常关闭 Web 应用程序。  

        现在让我们发布应用程序两次,一次启用 Native AOT,一次在常规 CLR 模式下(只需在 csproj 文件旁边的 bash/终端中执行这些语句):

# If you follow along, please execute the following statements in the same "WebApp" folder# This call will have PublishAot set to true because we defined it previously in the csproj file
dotnet publish -c Release -o ./bin/native-aot# For this call, we will explicitly set PublishAot to false via cmd arguments
dotnet publish -c Release -o ./bin/regular-clr /p:PublishAot=false

         您会注意到,由于生成本机代码,第一个命令比第二个命令花费的时间更长。当您导航到 ./bin/native-aot 子文件夹并查看其内容时,您将看到调试符号 (.pdb) 和 appsettings.json 文件旁边只有一个 EXE 文件 – 所有 DLL已合并到可执行文件中,因为本机 AOT 涉及自包含、单文件部署和修剪。只需将其与常规 clr 子文件夹的内容进行比较,您就可以在较小的 EXE 文件旁边找到 DLL。

# On Ubuntu 22.04:
bin
├─ native-aot
│  ├─ WebApp  # Executable, 9.79MB
│  ├─ WebApp.dbg # debugging symbols, 33.02MB
│  └─ appsettings.json # 142B
└─ regular-clr├─ WebApp # Executable, 70.96KB├─ WebApp.deps.json # runtime and hosting information, 388B├─ WebApp.dll # Compiled code of our project, 7KB├─ WebApp.pdb # debgging symbols, 20.5KB├─ WebApp.runtimeconfig.json # runtime information, 600B├─ appsettings.json # 142B└─ web.config # IIS configuration, 482B# On Windows 11
bin
└─ native-aot├─ WebApp.exe  # Executable, 8.77MB├─ WebApp.pdb # debugging symbols, 102.83MB└─ appsettings.json # 151B
# The regular-clr folder on Windows is omitted here, it contains the same files with roughly the same sizes

         设置应用程序后,我们现在可以对 Microsoft 声称在 Native AOT 中改进的内容进行一些测量:启动时间、内存使用情况和应用程序大小。请记住,所有这些值很大程度上取决于实际实现,例如您分配的对象数量、启动期间执行的语句以及您引用的框架和库。即将推出的图表仅类似于我们的小型简单应用程序 - 尽管如此,我们可以使用它来了解其优点以及它们在不同平台上的表现。

启动时间
        现在,我们可以多次执行应用程序以了解启动时间。我启动每个应用程序至少 10 次,然后对结果值取平均值。我的初始测试是在以下目标上执行的:

基于 Ubuntu-22.04-Jammy 的 Docker 容器(在基于 WSL 2 的引擎上运行)
基于 Alpine-3.18 的 Docker 容器(在基于 WSL 2 的引擎上运行)
WSL 2 上的 Ubuntu 22.04
Windows 11

        我们可以看到,Native AOT 的启动时间在 Linux 上约为 14 毫秒,在 Windows 11 上约为 17 毫秒。对于常规 CLR 部署,Ubuntu 上的启动时间约为 70 毫秒,Windows 上约为 80 毫秒。对于运行常规 CRL 构建的基于 Alpine-3.18 的 Docker 容器,存在大约 180 毫秒的巨大异常值,因此我在 Raspberry Pi 4 上重新执行了等效测试 - 这样我们还可以掌握功率较小的机器: 

        在 Raspberry Pi 4 的 ARM Cortex-A72 芯片上,Native AOT 启动时间仅不到 90ms。常规 CLR 构建的启动时间约为 530 毫秒。我们仍然有一个异常值,在 Alpine 3.18 Docker 容器中运行的常规 CLR 构建大约需要 600 毫秒(我还在 Azure 中的专用 Ubuntu 22.04 VM 上检查了这一点,并得到了与 Windows 上相同的结果)。

        总体而言,结果表明本机 AOT 启动时间比使用带有 JIT 的常规 CLR 运行速度大约快四到六倍(如果我们计算出 Alpine 异常值)。

内存使用情况
        让我们看一下 ASP.NET Core 应用程序的内存消耗:

 

        正如我们所看到的,根据底层架构的不同,结果会略有不同。对于本机 AOT,基于 ARM 的应用程序需要的内存量最少,约为 17MB 到 18MB,而在基于 x64 的 Linux 上,工作集范围约为 19MB 到 21MB。Windows x64 的最大工作集接近 23MB。

        与常规 CLR 构建相比,这些仍然是很大的改进:在 Linux 上,工作集减少了 50% 以上,因为常规 CLR 构建占用超过 50MB 的内存。在 Windows 上,CLR JIT 内存消耗略低于 40MB,因此收益并没有那么大。请记住,这些只是启动 Kestrel(ASP.NET Core 的 HTTP 服务器)后的数字 - 这些值将根据您在程序执行期间执行的分配而有很大差异(.NET 垃圾收集器没有任何变化)本机 AOT)。

应用大小

        那么应用程序的大小又如何呢?调用 dotnetpublish 后应用程序有多大?

        仅编译为 MSIL 会产生非常小的二进制文件大小,如上图所示。常规 CLR 部署在 Linux 上的大小仅为 82kB 左右,在 Windows 上为 145kB。相比之下,本机 AOT 在 Linux 上大约需要 10MB,在 Windows 上大约需要 9MB。然而,Native AOT 完全包含了所有必需的运行时组件,因此当我们添加实际的依赖项来运行我们的应用程序时,情况会发生巨大变化。让我们看一下 Docker 镜像的大小:

        在上图中,我们看到,特别是基于 Alpine 的 Docker 镜像在使用 Native AOT 时非常小。对于包含 我们的准系统 ASP.NET Core 应用程序的图像来说,大约 18MB 是一个惊人的值,大约是常规 CLR 构建的图像大小的 15%。基于 Ubuntu 的镜像也有类似的情况:它们的大小在 80MB 到 90MB 之间,不到常规 216MB 镜像大小的 40%。所有这些都减少了部署的有效负载,尤其是在云原生场景中。

用于本机 AOT 的 Dockerfile
        您可能想知道如何自己为 Native AOT 构建上述基于 Alpine 的 Dockerfile?让我们看一下:

FROM alpine:3.18 AS prepare
WORKDIR /app
RUN adduser -u 1000 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuserFROM mcr.microsoft.com/dotnet/sdk:8.0.100-rc.2-alpine3.18 AS build
RUN apk update && apk upgrade
RUN apk add --no-cache clang build-base zlib-dev
WORKDIR /code
COPY ./WebApp.csproj .
ARG RUNTIME_ID=linux-musl-x64
RUN dotnet restore -r $RUNTIME_ID
COPY . .
RUN dotnet publish \-c Release \-r $RUNTIME_ID \-o /app \--no-restoreFROM prepare AS final
COPY --chown=appuser --from=build /app/WebApp ./WebApp
ENTRYPOINT ["./WebApp"]

        在第一个“准备”阶段,我们将从基本的 Alpine 3.18 镜像开始,该镜像默认使用 root 用户。为了避免以特权运行我们的 Web 应用程序,我们首先创建一个 /app 目录,编译后的 Web 应用程序将复制到其中,然后运行 ​​adduser 命令来创建具有较少权限的标准用户。我们使用以下参数:

-u 1000 设置新用户的ID
–disabled-password 不再需要新用户的密码
–gecos “”是设置帐户的一些常规信息(如“真实姓名”或“电话号码”)的简短方法 – 由于我们只传递空字符串,相应的字段将被初始化为空,操作系统不会询问这些信息用户首次登录时的信息
        然后,我们立即使用chown -R appuser /app将 app 文件夹的权限移交给这个新用户,并使用USER appuser切换到它。

然后是重要的“构建”阶段,我们使用 Native AOT 发布应用程序。我们从 mcr.microsoft.com/dotnet/sdk:8.0.100-rc.2-alpine3.18映像开始(.NET 8 正式发布后,您很可能可以使用 mcr.microsoft.com/dotnet/sdk :8.0-alpine3.18 映像,请参阅  Microsoft Artifact Registry了解详细信息)并使用apk update && apk Upgrade更新和升级 Alpine 的 APK 包管理器的存储库,以安装 clang、build-base 和 zlib-dev。在 Alpine 上构建本机 AOT 需要这些包 - 您可以在这篇 Microsoft Learn 文章中找到有关本机 AOT 构建先决条件的信息。

        最后,我们恢复NuGet包并跨多个层发布,以便可以缓存第一步以减少重建图像所需的时间。我们只需复制 csproj 文件,然后对其调用dotnet Restore ,并使用$RUNTIME_ID构建参数传入运行时标识符。默认情况下,它设置为 linux-musl-x64,但您可以轻松地将其更改为 linux-musl-arm64 来构建基于 arm 的系统。然后,我们复制其余的源文件并调用dotnetpublish在一个 Dockerfile 层中以发布模式构建和发布整个应用程序 - 请记住,PublishAot在我们的 csproj 文件中已打开。  

        然后,发布的本机 AOT 应用程序驻留在 /app 顶级文件夹中。我们可以简单地使用COPY –chown=appuser –from=build /app/WebApp ./WebApp命令将其从构建阶段复制到最后阶段 (该命令还将所有权一次性转移给 appuser)。请注意,我们仅复制单个可执行文件,而不是 .dbg 文件,也没有 appsettings.json 文件(您希望使用环境变量来配置应用程序)。此外,我们的本机 AOT 部署没有进一步的运行时依赖项- 我们的应用程序在普通的 Alpine 3.18 映像上运行,只需复制并执行二进制文件即可。

缺点
        因此,我们看到启动时间、内存使用量和 Docker 映像大小显着减少。我们不再需要打包任何运行时依赖项。我们现在应该切换到 Native AOT 吗?可能不会,因为您将放弃 .NET 生态系统中您可能了解和喜爱的许多功能。

        最重要的是:你不能使用未绑定的反射。本机 AOT 应用程序在发布期间会被修剪,即静态代码分析器将检查所有程序集中的类型及其成员,并确定它们是否被调用。这样做是因为本机代码指令比 MSIL 指令占用更多的空间,因此获得合理的二进制大小的唯一方法是在本机代码生成期间删除未使用的代码。

        不幸的是,这有 一些警告,其中一些是:

        1、不使用未绑定的反射,例如基于反射的 DTO 反序列化
        2、没有运行时代码生成
        3、不动态加载程序集(即不支持插件式应用程序)
        

        静态代码分析器在很大程度上取决于代码的调用结构,解决诸如反射调用之类的复杂问题既不容易完成,也无法在合理的时间范围内完成。然而,这导致许多依赖反射的框架与本机 AOT 部署不兼容:
 
        1、无法使用像 Newtonsoft.Json 这样在序列化和反序列化过程中依赖反射的序列化器。DTO 的 JSON 序列化仅适用于 System.Text.Json 与 Roslyn Source Generators 的结合。这与 IConfiguration 的反序列化是一样的:微软引入了 一个专用的源生成器 ,它发出反序列化代码以避免未绑定的反射。
        2、同样,像 Entity Framework Core 和 Dapper 这样的对象/关系映射器 (ORM) 依赖使用未绑定反射来实例化实体并填充其属性,它们也不与本机 AOT 兼容。他们都计划添加对源生成器的支持,但这在 .NET 8 发布时还没有准备好。
        3、不支持 ASP.NET Core MVC 和 SignalR – 仅最小 API 和 GRPC 可用于创建端点和流数据。有一个最小 API 源生成器,可以使用 MapXXX 方法处理端点注册。
        4、不直接支持本机代码的自动化测试:像 xunit 和 nunit 这样的单元测试框架都无法在本机 AOT 模式下运行。目前,.NET 团队专注于 分析器方法 ,因此当您构建应用程序并运行测试时,Roslyn 分析器将创建警告,提示在本机 AOT 模式下运行时的潜在问题。请注意,当您在 IDE 中使用 dotnet run、dotnet test 或类似功能时,您的应用程序将以常规 CLI 模式运行 - 本机 AOT 仅在您的应用程序发布时才会生效。
        5、本机代码的有限调试:发布后,您的应用程序是真正的本机二进制文件,因此 C# 的托管调试器将无法使用它。部署后我们需要使用gdb或WinDbg等调试器。您还需要调试符号(.dbg 或 .pdb 文件)以获得有用的调试体验。


        这些只是 Native AOT 的主要缺点。一般来说,您需要检查您想要合并的每个框架或库的本机 AOT 兼容性。严重依赖反射的流行框架需要在编译时用某种形式的代码生成替换此代码才能兼容。可以在此处找到本机 AOT 支持的 ASP.NET Core 功能列表。


结论
        本机 AOT 是 ASP.NET Core 的一个有趣的开发,它可能会在即将发布的版本(例如 .NET 9 和 10)中获得更多关注。主要目的是在云本机场景中,您更喜欢具有较小图像大小和内存占用的微服务,并且减少启动时间。对于需要快速启动的无服务器功能也是如此——在这些场景中,Native AOT 可以大放异彩。在常规 CLR 模式下运行时,JIT 在启动时需要编译大量代码(尤其是静态方法/成员),而在 Native AOT 模式下可以完全省略。但不要忘记:Native AOT 生成的代码不一定更快。事实上,分层 JIT 编译过程可以考虑目标系统(因为它就在您的代码旁边运行)——这是编译时创建的本机代码无法考虑的。

        库和框架仍然需要赶上原生 AOT 支持。当前依赖于未绑定反射的代码必须转换为编译时生成的代码,很可能是通过源生成器。一旦第三方框架和库支持它,开发人员是否会采用这种部署模型将会很有趣——尤其是 Entity Framework Core 在这里很重要。接下来的几年也将非常有趣,如何进一步调整 Native AOT 以与 Go 和 Rust 等语言竞争。

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

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

相关文章

ebay产品名称规则是什么?eBay产品主图规则是什么?-站斧浏览器

ebay产品名称规则是什么&#xff1f; 1、简洁明了&#xff1a;在eBay上&#xff0c;产品命名应该简洁明了&#xff0c;避免使用过长或复杂的词汇。买家通常会使用关键词搜索商品&#xff0c;因此使用简洁的命名可以提高产品在搜索结果中的排名。 2、准确描述&#xff1a;产品…

《使用ThinkPHP6开发项目》 - ThinkPHP6创建菜单模块

#CSDN 年度征文&#xff5c;回顾 2023&#xff0c;赢专属铭牌等定制奖品# 一、创建菜单模块 1、创建系统菜单表 CREATE TABLE menu (id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 菜单ID,menu_name varchar(32) NOT NULL DEFAULT COMMENT 菜单名称,path varchar(2…

KBDPL.DLL文件丢失,软件游戏无法启动,修复方法

不少小伙伴&#xff0c;求助说遇到Windows弹窗提示“KBDPL.DLL文件丢失&#xff0c;应用无法启动的问题”&#xff0c;不知道应该怎么修复&#xff1f; 首先&#xff0c;先来了解“KBDPL.DLL文件”是什么&#xff1f; kbdpl.dll是Windows操作系统的一部分&#xff0c;是一个动…

三菱plc的点动控制循环(小灯闪烁,单控气缸循环)

以为前一段时间小编做了一个气缸定时循环的程序&#xff0c;根据程序有不足之处&#xff0c;所以小编写下这篇文章&#xff0c;将网络上的plc小灯控制进行总结&#xff01;如果对你有帮助&#xff0c;不要忘了点赞收藏&#xff01;如果有更加好的梯形图&#xff0c;欢迎评论&am…

八怪:再谈 MySQL 8 这两个精准的时间戳

MySQL 8.0 的 binlog 中多了 immediate_commit_timestamp 和 original_commit_timestamp 的信息&#xff0c;网上也有很多文章进行解释&#xff0c;最近也刚好遇到相关问题&#xff0c;刚好稍微学习一下。 作者&#xff1a;高鹏&#xff08;八怪&#xff09;&#xff0c;《MySQ…

教育机构培训系统小程序功能清单

制作一款适合自己的教育机构培训系统小程序&#xff0c;可以为学员提供更便捷的学习体验&#xff0c;同时提高机构的教学效率。今天将详细介绍如何使用乔拓云平台制作教育机构培训系统小程序。 在浏览器搜索乔拓云&#xff0c;登录到后台&#xff0c;选择教育系统并点击进入。在…

基于SSM(非maven)的教室预约管理系统——有报告(Javaweb)

项目简介 本项目为基于SSM&#xff08;非maven&#xff09;的教室预约管理系统&#xff0c;本项目主要分为二种角色&#xff1a;用户&#xff0c;管理员 管理员拥有功能&#xff1a;教室信息管理、预约审核管理、预约记录查询、用户注册管理、修改个人信息、退出登录等 用户…

2024年阿里云优惠券领取及使用教程

阿里云作为国内领先的云计算服务提供商&#xff0c;一直致力于为客户提供优质、高效的服务。为了更好地回馈客户&#xff0c;阿里云经常会推出各种优惠活动&#xff0c;其中就包括阿里云优惠券。本文将详细介绍如何领取及使用阿里云优惠券。 一、阿里云优惠券介绍 阿里云优惠券…

SpringBoot的基础配置

问题导入 入门案例中没有引入spring-webmvc等依赖包&#xff0c;没有配置Tomcat服务器&#xff0c;为什么能正常启动&#xff1f;我们没有配置端口号&#xff0c;为什么端口是8080&#xff1f; 起步依赖 starter SpringBoot中常见项目名称&#xff0c;定义了当前项目使用的所…

CEC2017(Python):五种算法(DBO、HHO、RFO、SSA、PSO)求解CEC2017

一、5种算法简介 1、蜣螂优化算法DBO 2、哈里斯鹰优化算法HHO 3、红狐优化算法RFO 4、麻雀搜索算法SSA 5、粒子群优化算法PSO 二、CEC2017简介 参考文献&#xff1a; [1]Awad, N. H., Ali, M. Z., Liang, J. J., Qu, B. Y., & Suganthan, P. N. (2016). “Problem d…

阿里云系统盘测评ESSD、SSD和高效云盘IOPS、吞吐量性能参数表

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云百科aliyunbaike.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延等性…

炫云常见咨询问题TOP榜(云渲染软件专题)

在上一期&#xff0c;小编带大家盘点了年度炫云云渲染使用相关常见咨询问题TOP20。这份榜单不仅是对过去一年用户关注焦点的回顾&#xff0c;更是一个汇总了各类问题解答的宝典。无论您是初次使用还是老用户&#xff0c;都能帮助您更快速地解决疑问&#xff0c;提升使用炫云各类…

Harmony 开始支持 Flutter ,聊聊 Harmony 和 Flutter 之间的因果

原创作者&#xff1a;恋猫de小郭 相信大家都已经听说过&#xff0c;明年的 Harmony Next 版本将正式剥离 AOSP 支持 &#xff0c;基于这个话题我已经做过一期问题汇总 &#xff0c;当时在 现有 App 如何兼容 Harmony Next 问题上提到过&#xff1a; 华为内部也主导适配目前的主…

用js让用户输入一个数累加和

需求&#xff1a;用户输入一个数&#xff0c; 计算 1 到这个数的和。 比如 用户输入的是 5&#xff0c; 则计算 1~5 之间的累加和 并且输出到控制台 <body><script>let numprompt(请输入一个数)let sum0for(let i1;i<num;i){sumi}console.log(sum)</script…

进程终结之道:kill与pskill的神奇战斗

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 进程终结之道&#xff1a;kill与pskill的神奇战斗 前言基本用法kill命令&#xff1a;基础语法&#xff1a;选项&#xff1a;示例&#xff1a; pskill命令&#xff1a;基础语法&#xff1a;选项&#x…

【算法与数据结构】763、LeetCode划分字母区间

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题要求为&#xff1a; 1.尽可能多的划分片段2.字母只能出现在一个片段中3.片段连接起来仍然是s&…

pytorch04:网络模型创建

目录 一、模型创建过程1.1 以LeNet网络为例1.2 LeNet结构1.3 nn.Module 二、网络层容器(Containers)2.1 nn.Sequential2.1.1 常规方法实现2.1.2 OrderedDict方法实现 2.2 nn.ModuleList2.3 nn.ModuleDict2.4 三种容器构建总结 三、AlexNet网络构建 一、模型创建过程 1.1 以LeNe…

如何使用内网穿透工具实现远程SSH访问Deepin系统

文章目录 前言1. 开启SSH服务2. Deppin安装Cpolar3. 配置ssh公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 前言 Deepin操作系统是一个基于Debian的Linux操作系统&#xff0c;专注于使用者对日常办公、学习、生活和娱乐的操作体验的极致&#xff0…

leetcode递归算法题总结

递归本质是找重复的子问题 本章目录 1.汉诺塔2.合并两个有序链表3.反转链表4.两两交换链表中的节点5.Pow(x,n) 1.汉诺塔 汉诺塔 //面试写法 class Solution { public:void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {dfs(a,b…

踩坑记录-安装nuxt3报错:Error: Failed to download template from registry: fetch failed;

报错复现 安装nuxt3报错&#xff1a;Error: Failed to download template from registry: fetch failednpx nuxi init nuxt-demo 初始化nuxt 项目 报错 Error: Failed to download template from registry: fetch faile 解决方法 配置hosts Mac电脑&#xff1a;/etc/hostswin电…