.NET 9 运行时中的新增功能
本文介绍了适用于 .NET 9 的 .NET 运行时中的新功能和性能改进。
文章目录
- 一、支持修剪的功能开关的属性模型
- 二、UnsafeAccessorAttribute 支持泛型参数
- 三、垃圾回收
- 四、控制流实施技术
- .NET 安装搜索行为
- 性能改进
- 循环优化
- 感应变量加宽
- Arm64 上的索引后寻址
- 强度降低
- 循环计数器可变方向
- 内联改进
- PGO 改进:类型检查和强制转换
- .NET 库中的 Arm64 矢量化
- Arm64 代码生成
- 更快的异常
- 代码布局
- 减少地址暴露
- AVX10v1 支持
- 硬件内部代码生成
- 浮点和 SIMD 运算的常量折叠
- Arm64 SVE 支持
- 盒子的对象堆栈分配
一、支持修剪的功能开关的属性模型
使用两个新属性可以定义 .NET 库(和您)可用于切换功能区域的功能开关。如果某个功能不受支持,则在使用本机 AOT 进行修剪或编译时,将删除不受支持(因此未使用)的功能,从而使应用程序大小更小。
- FeatureSwitchDefinitionAttribute 用于在剪裁时将 feature-switch 属性视为常量,并且可以删除由 switch 保护的死代码:
if (Feature.IsSupported)Feature.Implementation();public class Feature
{[FeatureSwitchDefinition("Feature.IsSupported")]internal static bool IsSupported => AppContext.TryGetSwitch("Feature.IsSupported", out bool isEnabled) ? isEnabled : true;internal static Implementation() => ...;
}
When the app is trimmed with the following feature settings in the project file, Feature.IsSupported is treated as false, and Feature.Implementation code is removed.
在项目文件中使用以下功能设置修剪应用时,Feature.IsSupported 将被视为 false,并删除 Feature.Implementation 代码。
<ItemGroup><RuntimeHostConfigurationOption Include="Feature.IsSupported" Value="false" Trim="true" />
</ItemGroup>
- FeatureGuardAttribute 用于将功能开关属性视为使用 RequiresUnreferencedCodeAttribute、RequiresAssemblyFilesAttribute 或 RequiresDynamicCodeAttribute 批注的代码的守卫。例如:
if (Feature.IsSupported)Feature.Implementation();public class Feature
{[FeatureGuard(typeof(RequiresDynamicCodeAttribute))]internal static bool IsSupported => RuntimeFeature.IsDynamicCodeSupported;[RequiresDynamicCode("Feature requires dynamic code support.")]internal static Implementation() => ...; // Uses dynamic code
}
When built with true, the call to Feature.Implementation() doesn’t produce analyzer warning IL3050, and Feature.Implementation code is removed when publishing.
使用 true 生成时,对 Feature.Implementation() 的调用不会生成分析器警告 IL3050,并且在发布时会删除 Feature.Implementation 代码。
二、UnsafeAccessorAttribute 支持泛型参数
UnsafeAccessorAttribute 功能允许对调用方无法访问的类型成员进行不安全访问。此功能是在 .NET 8 中设计的,但在实现时不支持泛型参数。.NET 9 添加了对 CoreCLR 和本机 AOT 方案的泛型参数的支持。下面的代码显示了示例用法。
using System.Runtime.CompilerServices;public class Class<T>
{private T? _field;private void M<U>(T t, U u) { }
}class Accessors<V>
{[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field")]public extern static ref V GetSetPrivateField(Class<V> c);[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "M")]public extern static void CallM<W>(Class<V> c, V v, W w);
}internal class UnsafeAccessorExample
{public void AccessGenericType(Class<int> c){ref int f = ref Accessors<int>.GetSetPrivateField(c);Accessors<int>.CallM<string>(c, 1, string.Empty);}
}
三、垃圾回收
现在默认启用对应用程序大小的动态适应 (DATAS)。它旨在适应应用程序内存要求,这意味着应用程序堆大小应与长期数据大小大致成正比。DATAS 在 .NET 8 中作为可选功能引入,并在 .NET 9 中得到了显著更新和改进。
四、控制流实施技术
默认情况下,Windows 上的应用程序启用控制流强制技术 (CET)。它通过添加硬件强制堆栈保护来防止面向返回的编程 (ROP) 漏洞,从而显著提高安全性。这是最新的 .NET 运行时安全缓解措施。
CET 对启用 CET 的流程施加了一些限制,并可能导致较小的性能回归。有多种控件可以选择退出 CET。
.NET 安装搜索行为
现在可以配置 .NET 应用程序,使其应如何搜索 .NET 运行时。此功能可用于私有运行时安装,或用于更强地控制执行环境。
性能改进
对 .NET 9 进行了以下性能改进:
- 循环优化
- 内联改进
- PGO 改进:类型检查和强制转换
- .NET 库中的 Arm64 矢量化
- Arm64 代码生成
- 更快的异常
- 代码布局
- 减少地址暴露
- AVX10v1 支持
- 硬件内部代码生成
- 浮点和 SIMD 运算的常量折叠
- Arm64 SVE 支持
- 盒子的对象堆栈分配
循环优化
改进循环的代码生成是 .NET 9 的首要任务。现在提供以下改进:
- 感应变量加宽
- 索引后寻址
- 强度降低
- 循环计数器可变方向
归纳变量扩大和后索引寻址类似:它们都使用循环索引变量优化内存访问。但是,它们采用不同的方法,因为 Arm64 提供 CPU 功能,而 x64 不提供。由于 CPU/ISA 功能和需求的不同,为 x64 实施了归纳变量加宽。
感应变量加宽
64 位编译器具有一种称为归纳变量 (IV) 展宽的新优化。
IV 是一个变量,其值会随着包含循环的迭代而变化。在下面的 for 循环中,i 是一个 IV: for (int i = 0; i < 10; i++)。如果编译器可以分析 IV 的值在其循环迭代中如何演变,则它可以为相关表达式生成性能更高的代码。
请考虑以下循环访问数组的示例:
static int Sum(int[] nums)
{int sum = 0;for (int i = 0; i < nums.Length; i++){sum += nums[i];}return sum;
}
索引变量 i 的大小为 4 字节。在程序集级别,64 位寄存器通常用于在 x64 上保存数组索引,在以前的 .NET 版本中,编译器生成的代码将 i 扩展到 8 个字节以进行数组访问,但继续将 i 视为其他位置的 4 字节整数。但是,将 i 扩展到 8 字节需要在 x64 上添加额外的指令。随着 IV 加宽,64 位 JIT 编译器现在在整个循环中将 i 加宽到 8 字节,省略了零扩展名。遍历数组非常常见,并且这种指令删除的好处很快就会增加。
Arm64 上的索引后寻址
索引变量经常用于读取内存的 Sequences 区域。考虑惯用的 for 循环:
static int Sum(int[] nums)
{int sum = 0;for (int i = 0; i < nums.Length; i++){sum += nums[i];}return sum;
}
对于循环的每次迭代,索引变量 i 用于读取 nums 中的整数,然后 i 递增。在 Arm64 程序集中,这两个操作如下所示:
ldr w0, [x1]
add x1, x1, #4
ldr w0, [x1] 将 x1 中内存地址处的整数加载到 w0 中;这对应于源代码中 nums[i] 的访问。然后,加上 x1、x1、#4 将 x1 中的地址增加 4 个字节(整数的大小),移动到 nums 中的下一个整数。此指令对应于在每次迭代结束时执行的 i++ 操作。
Arm64 支持索引后寻址,其中“index”寄存器在使用其地址后自动递增。这意味着两条指令可以合并为一条,从而提高循环效率。CPU 只需要解码一条指令而不是两条指令,并且循环的代码现在对缓存更加友好。
更新后的程序集如下所示:
ldr w0, [x1], #0x04
末尾的 #0x04 表示 x1 中的地址在用于将整数加载到 w0 后递增 4 个字节。64 位编译器现在在生成 Arm64 代码时使用后索引寻址。
强度降低
强度降低是一种编译器优化,其中操作被替换为更快的、逻辑上等效的操作。此技术对于优化循环特别有用。考虑惯用的 for 循环:
static int Sum(int[] nums)
{int sum = 0;for (int i = 0; i < nums.Length; i++){sum += nums[i];}return sum;
}
以下 x64 程序集代码显示了为循环正文生成的代码片段:
add ecx, dword ptr [rax+4*rdx+0x10]
inc edx
这些指令分别对应于表达式 sum += nums[i] 和 i++。rcx(ecx 保存此寄存器的低 32 位)包含 sum 的值,rax 包含 nums 的基址,rdx 包含 i 的值。要计算 nums[i] 的地址,请将 rdx 中的索引乘以 4(整数的大小)。然后将此偏移量添加到 rax 中的基址,加上一些填充。(读取 nums[i] 处的整数后,将其添加到 rcx 中,并且 rdx 中的索引递增。换句话说,每个数组访问都需要一个乘法和一个加法运算。
乘法比加法更昂贵,用后者代替前者是降低力量的典型动机。为避免在每次内存访问时计算元素的地址,您可以重写示例以使用指针而不是索引变量访问 nums 中的整数:
static int Sum2(Span<int> nums)
{int sum = 0;ref int p = ref MemoryMarshal.GetReference(nums);ref int end = ref Unsafe.Add(ref p, nums.Length);while (Unsafe.IsAddressLessThan(ref p, ref end)){sum += p;p = ref Unsafe.Add(ref p, 1);}return sum;
}
源代码更复杂,但在逻辑上等同于初始实现。此外,程序集看起来更好:
add ecx, dword ptr [rdx]
add rdx, 4
rcx(ecx 保存此寄存器的低 32 位)仍然保存 sum 的值,但 rdx 现在保存 p 指向的地址,因此访问 nums 中的元素只需要我们取消引用 rdx。第一个示例中的所有乘法和加法都已替换为单个 add 指令,以将指针向前移动。
在 .NET 9 中,JIT 编译器会自动将第一个索引模式转换为第二个索引模式,而无需重写任何代码。
循环计数器可变方向
64 位编译器现在可以识别循环的 counter 变量何时仅用于控制迭代次数,并将循环转换为倒计时而不是向上。
在惯用的 for (int i = …) 模式中,counter 变量通常会增加。请考虑以下示例:
for (int i = 0; i < 100; i++)
{DoSomething();
}
但是,在许多架构上,递减循环的计数器性能更高,如下所示:
for (int i = 100; i > 0; i--)
{DoSomething();
}
对于第一个示例,编译器需要发出一条递增 i 的指令,然后发出一条执行 i < 100 比较的指令,如果条件仍然为真,则发出一个条件跳转以继续循环 — 总共有三条指令。但是,如果计数器的方向是颠倒的,则需要少一条指令。例如,在 x64 上,编译器可以使用 dec 指令来递减 i;当 I 达到 0 时,DEC 指令会设置一个 CPU 标志,该标志可以用作紧跟在 DEC 之后的跳转指令的条件。
代码大小的减少很小,但如果循环运行大量迭代,则性能改进可能会非常显著。
内联改进
其中之一。NET 对 JIT 编译器的内部内联的目标是尽可能多地消除阻止方法内联的限制。.NET 9 支持内联:
- 需要运行时查找的共享泛型。
例如,请考虑以下方法:
static bool Test<T>() => Callee<T>();
static bool Callee<T>() => typeof(T) == typeof(int);
当 T 是引用类型(如 string)时,运行时将创建共享泛型,这些泛型是 Test 和 Callee 的特殊实例,由所有 ref 类型 T 类型共享。为了实现此目的,运行时会构建将泛型类型映射到内部类型的字典。这些词典是按泛型类型(或按泛型方法)专用的,并在运行时访问以获取有关 T 和依赖于 T 的类型的信息。从历史上看,实时编译的代码只能针对根方法的字典执行这些运行时查找。这意味着 JIT 编译器无法将被调用方内联到 Test 中 — 即使两个方法都通过同一类型实例化,也被调用方的内联代码也无法访问正确的字典。
.NET 9 通过在被调用方中自由启用运行时类型查找来解除此限制,这意味着 JIT 编译器现在可以将被调用方等方法内联到 Test 中。
假设我们在另一个方法中调用 Test。在伪代码中,内联如下所示:
static bool Test<string>() => typeof(string) == typeof(int);
该类型检查可以在编译期间计算,因此最终代码如下所示:
static bool Test<string>() => false;
对 JIT 编译器内联的改进可能会对其他内联决策产生复合影响,从而显著提高性能。例如,内联 Callee 的决定可能使对 Test 的调用也被内联,依此类推。这产生了数百个基准测试改进,其中至少 80 个基准测试提高了 10% 或更多。
- 访问 Windows x64、Linux x64 和 Linux Arm64 上的线程本地静态。
对于静态类成员,该成员的一个实例正存在于该类的所有实例中,这些实例“共享”该成员。如果静态成员的值对于每个线程都是唯一的,则使该值为线程本地值可以提高性能,因为它无需并发基元即可从其包含的线程安全地访问静态成员。
以前,在本机 AOT 编译的程序中访问线程本地静态数据时,编译器需要向运行时发出调用,以获取线程本地存储的基址。现在,编译器可以内联这些调用,从而大大减少访问此数据的指令。
PGO 改进:类型检查和强制转换
默认情况下,.NET 8 启用动态按配置优化 (PGO)。NET 9 扩展了 JIT 编译器的 PGO 实现,以分析更多代码模式。启用分层编译后,JIT 编译器已将插桩插入到您的程序中以分析其行为。当它使用优化进行重新编译时,编译器会利用它在运行时构建的配置文件来做出特定于程序当前运行的决策。在 .NET 9 中,JIT 编译器使用 PGO 数据来提高类型检查的性能。
确定对象的类型需要调用运行时,这会带来性能损失。当需要检查对象的类型时,为了正确起见,JIT 编译器会发出此调用(编译器通常不能排除任何可能性,即使它们看起来不太可能)。但是,如果 PGO 数据表明某个对象可能是特定类型,则 JIT 编译器现在会发出一个快速路径,该路径以较低的成本检查该类型,并仅在必要时回退到调用运行时的慢速路径。
.NET 库中的 Arm64 矢量化
新的 EncodeToUtf8 实现利用 JIT 编译器在 Arm64 上发出多寄存器加载/存储指令的能力。此行为允许程序使用更少的指令处理更大的数据块。跨各个域的 .NET 应用应该会在支持这些功能的 Arm64 硬件上看到吞吐量改进。一些基准测试将其执行时间缩短了一半以上。
Arm64 代码生成
JIT 编译器已经能够转换其连续加载的表示形式,以使用 arm64 上的 ldp 指令(用于加载值)。.NET 9 将此功能扩展到存储操作。
str 指令将单个 register 中的数据存储到 memory,而 stp 指令存储一对 registers 的数据。使用 stp 而不是 str 意味着可以用更少的 store 操作完成相同的任务,从而缩短执行时间。减少一条指令似乎是一个很小的改进,但如果代码在循环中运行大量迭代,则性能提升会迅速增加。
例如,请考虑以下代码段:
class Body { public double x, y, z, vx, vy, vz, mass; }static void Advance(double dt, Body[] bodies)
{foreach (Body b in bodies){b.x += dt * b.vx;b.y += dt * b.vy;b.z += dt * b.vz;}
}
b.x、b.y 和 b.z 的值在循环体中更新。在程序集级别,每个成员都可以使用 str 指令进行存储;或者使用 STP,可以用一条指令处理其中两个 STORE (B.X 和 B.Y,或 B.Y 和 B.Z,因为这些对在内存中是连续的)。要使用 stp 指令同时存储到 b.x 和 b.y,编译器还需要确定计算 b.x + (dt * b.vx) 和 b.y + (dt * b.vy) 彼此独立,并且可以在存储到 b.x 和 b.y 之前执行。
更快的异常
CoreCLR 运行时采用了一种新的异常处理方法,该方法提高了异常处理的性能。新实现基于 NativeAOT 运行时的异常处理模型。此更改删除了对 Windows 结构化异常处理 (SEH) 及其在 Unix 上的模拟的支持。除 Windows x86(32 位)之外的所有环境都支持新方法。
根据一些异常处理微基准测试,新的异常处理实现速度提高了 2-4 倍。
默认情况下,新实现处于启用状态。但是,如果需要切换回旧版异常处理行为,可以通过以下任一方式执行此操作:
- 在 runtimeconfig.json 文件中设置为 System.Runtime.LegacyExceptionHandling to true 。
- 将 DOTNET_LegacyExceptionHandling 环境变量设置为 1 .
代码布局
编译器通常使用基本块来推理程序的控制流,其中每个块都是一段代码,只能在第一条指令处输入,并通过最后一条指令退出。基本块的顺序很重要。如果一个 block 以 branch 指令结尾,则控制流会转移到另一个 block。块重新排序的一个目标是通过最大化 fall-through 行为来减少生成代码中的 branch instructions 数量。如果每个基本块后面都跟着它最有可能的后继者,它就可以 “落入” 它的后继者中,而不需要跳跃。
直到最近,JIT 编译器中的块重新排序还受到 flowgraph 实现的限制。在 .NET 9 中,JIT 编译器的块重新排序算法已替换为更简单、更全局的方法。流图数据结构已重构为:
- 删除有关区块排序的一些限制。
- 将执行可能性嵌入到块之间的每个控制流更改中。
此外,随着方法的流程图的转换,配置文件数据会传播和维护。
减少地址暴露
在 .NET 9 中,JIT 编译器可以更好地跟踪局部变量地址的使用情况,并避免不必要的地址暴露。
当使用局部变量的地址时,JIT 编译器在优化方法时必须采取额外的预防措施。例如,假设编译器正在优化一个方法,该方法在对另一个方法的调用中传递局部变量的地址。由于被调用方可能会使用地址来访问局部变量,因此为了保持正确性,编译器会避免转换变量。寻址公开的局部变量会显著抑制编译器的优化潜力。
AVX10v1 支持
为 AVX10 添加了新的 API,这是 Intel 的新 SIMD 指令集。您可以使用新的 Avx10v1 API 通过矢量化操作在支持 AVX10 的硬件上加速 .NET 应用程序。
硬件内部代码生成
许多硬件内部 API 希望用户为某些参数传递常量值。这些常量直接编码到 intrinsic 的底层指令中,而不是加载到 registers 中或从内存中访问。如果未提供常量,则内部函数将替换为对功能等效但速度较慢的回退实现的调用。
请考虑以下示例:
static byte Test1()
{Vector128<byte> v = Vector128<byte>.Zero;const byte size = 1;v = Sse2.ShiftRightLogical128BitLane(v, size);return Sse41.Extract(v, 0);
}
调用 to Sse2.ShiftRightLogical128BitLane 中 size 的使用可以用常量 1 替换,在正常情况下,JIT 编译器已经能够进行这种替换优化。但是,在确定是生成 的 Sse2.ShiftRightLogical128BitLane 加速代码还是回退代码时,编译器检测到传递的是变量而不是常量,并过早地决定不“内部化”调用。从 .NET 9 开始,编译器会识别更多此类情况,并将 variable 参数替换为其常量值,从而生成加速代码。
浮点和 SIMD 运算的常量折叠
常量折叠是 JIT 编译器中的现有优化。常量折叠是指将可在编译时计算的表达式替换为它们计算的常量,从而消除运行时的计算。.NET 9 添加了新的常量折叠功能:
-
对于浮点二进制运算,其中一个操作数是常量:
x + NaN 现在折叠为 NaN。
x * 1.0 现在折叠为 x。
x + -0 现在折叠为 x。 -
对于硬件内部函数。例如,假设 x 是向量:
x + 矢量。Zero 现在折叠为 x。
x & 矢量。零现在折叠成向量。零。
x & 矢量。AllBitsSet 现在折叠为 x。
Arm64 SVE 支持
.NET 9 引入了对可扩展矢量扩展 (SVE) 的实验性支持,SVE 是 ARM64 CPU 的 SIMD 指令集。.NET 已经支持 NEON 指令集,因此在支持 NEON 的硬件上,您的应用程序可以利用 128 位矢量寄存器。SVE 支持灵活的矢量长度,最高可达 2048 位,每条指令可解锁更多数据处理。在 .NET 9 中,Vector 在面向 SVE 时为 128 位宽,未来的工作将允许缩放其宽度以匹配目标计算机的矢量寄存器大小。您可以使用新的 System.Runtime.Intrinsics.Arm.Sve API 在支持 SVE 的硬件上加速 .NET 应用程序。
.NET 9 中的 SVE 支持是实验性的。下面的 System.Runtime.Intrinsics.Arm.Sve API 标有 ExperimentalAttribute,这意味着它们在未来版本中可能会发生变化。此外,通过 SVE 生成的代码的调试器单步执行和断点可能无法正常工作,从而导致应用程序崩溃或数据损坏。
盒子的对象堆栈分配
值类型(如 int 和 struct)通常在堆栈而不是堆上分配。但是,为了启用各种 Code Pattern,它们经常被 “装箱” 到对象中。
请考虑以下代码段:
static bool Compare(object? x, object? y)
{if ((x == null) || (y == null)){return x == y;}return x.Equals(y);
}public static int RunIt()
{bool result = Compare(3, 4);return result ? 0 : 100;
}
Compare 编写方便,因此如果您想比较其他类型,例如字符串或双精度值,您可以重用相同的实现。但在此示例中,它也具有性能缺点,即要求对传递给它的任何值类型进行装箱。
为 RunIt 生成的 x64 汇编代码如下所示:
push rbx
sub rsp, 32
mov rcx, 0x7FFB9F8074D0 ; System.Int32
call CORINFO_HELP_NEWSFAST
mov rbx, rax
mov dword ptr [rbx+0x08], 3
mov rcx, 0x7FFB9F8074D0 ; System.Int32
call CORINFO_HELP_NEWSFAST
mov dword ptr [rax+0x08], 4
add rbx, 8
mov ecx, dword ptr [rbx]
cmp ecx, dword ptr [rax+0x08]
sete al
movzx rax, al
xor ecx, ecx
mov edx, 100
test eax, eax
mov eax, edx
cmovne eax, ecx
add rsp, 32
pop rbx
ret
对 CORINFO_HELP_NEWSFAST 的调用是装箱整数参数的堆分配。另请注意,没有对 Compare 的任何调用;编译器决定将其内联到 RunIt 中。这种内联意味着 box 永远不会 “escape”。换句话说,在 Compare 的整个执行过程中,它知道 x 和 y 实际上是整数,并且可以在不影响比较逻辑的情况下安全地将它们拆箱。
从 .NET 9 开始,64 位编译器在堆栈上分配未转义的框,从而解锁其他几个优化。在此示例中,编译器现在省略了堆分配,但是因为它知道 x 和 y 是 3 和 4,所以它也可以省略 Compare 的主体;编译器可以在编译时确定 x.Equals(y) 为 false,因此 RunIt 应始终返回 100。以下是更新后的程序集:
mov eax, 100
ret
相关文章:
.NET 9 运行时中的新增功能
本文介绍了适用于 .NET 9 的 .NET 运行时中的新功能和性能改进。 文章目录 一、支持修剪的功能开关的属性模型二、UnsafeAccessorAttribute 支持泛型参数三、垃圾回收四、控制流实施技术.NET 安装搜索行为性能改进循环优化感应变量加宽Arm64 上的索引后寻址强度降低循环计数器可…...
Linux下安装mysql8.0版本
先确定我的下载安装的目录,安装文件是下载在 /opt/install 目录下面 (安装地址不同的话注意修改地址) 1.在线下载 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz2.解压 tar -xvf mysql-8.0.20-linux-glibc2.12-x86_64.t…...
kvm-dmesg:从宿主机窥探虚拟机内核dmesg日志
在虚拟化环境中,实时获取虚拟机内核日志对于系统管理员和开发者来说至关重要。传统的 dmesg 工具可以方便地查看本地系统的内核日志,但在KVM(基于内核的虚拟机)环境下,获取虚拟机内部的内核日志则复杂得多。为了简化这…...
植物明星大乱斗15
能帮到你的话,就给个赞吧 😘 文章目录 player.hplayer.cppparticle.hparticle.cpp player.h #pragma once #include <graphics.h> #include "vector2.h" #include "animation.h" #include "playerID.h" #include &…...
go-zero(三) 数据库操作
go-zero 数据库操作 在本篇文章中,我们将实现一个用户注册和登录的服务。我们将为此构建一个简单而高效的 API,包括请求参数和响应参数的定义。 一、Mysql连接 1. 创建数据库和表 在 MySQL 中创建名为 test_zero的数据库,并创建user 表 …...
SQL面试题——间隔连续问题
间隔连续问题 某游戏公司记录的用户每日登录数据如下 +----+----------+ | id| date| +----+----------+ |1001|2021-12-12| |1001|2021-12-13| |1001|2021-12-14| |1001|2021-12-16| |1001|2021-12-19| |1001|2021-12-20| |1002|2021-12-12| |1002|2021-12-16| |1002|…...
vim配置 --> 在创建的普通用户下
在目录/etc/ 下面,有个名为vimrc 的文件,这是系统中公共的vim配置文件对所有用户都有效 我们现在创建一个普通用户 dm 创建好以后,我们退出重新链接 再切换到普通用户下 再输入密码(是不显示的,输入完后,…...
(计算机毕设)基于SpringBoot+Vue的房屋租赁系统的设计与实现
博主可接毕设设计!!! 各种毕业设计源码只要是你有的题目我这里都有源码 摘 要 社会的发展和科学技术的进步,互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。互…...
【含开题报告+文档+PPT+源码】基于SpringBoot的医院药房管理系统
开题报告 在科技迅速发展的今天,各行各业都在积极寻求与现代技术的融合,以提升自身的运营效率和竞争力。医疗行业作为关乎国计民生的关键领域,其信息化建设的步伐尤为迅速。医院药房作为医疗体系中的核心环节,其管理效率和服务质…...
基于SpringBoot的“数码论坛系统设计与实现”的设计与实现(源码+数据库+文档+PPT)
基于SpringBoot的“数码论坛系统设计与实现”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体结构图 系统首页界面图 数码板…...
Linux-第2集-打包压缩 zip、tar WindowsLinux互传
欢迎来到Linux第2集,这一集我会非常详细的说明如何在Linux上进行打包压缩操作,以及解压解包 还有最最重要的压缩包的网络传输 毕竟打包压缩不是目的,把文件最终传到指定位置才是目的 由于打包压缩分开讲没有意义,并且它们俩本来…...
项目进度计划表:详细的甘特图的制作步骤
甘特图(Gantt chart),又称为横道图、条状图(Bar chart),是一种用于管理时间和任务活动的工具。 甘特图由亨利劳伦斯甘特(Henry Laurence Gantt)发明,是一种通过条状图来…...
Cargo Rust 的包管理器
Cargo->Rust 的包管理器 Cargi简介Cargo 的主要功能1. 创建项目2. 管理依赖3. 构建项目4. 运行项目5. 测试代码6. 检查代码7. 生成文档8. 发布和分享包 Cargo 的核心文件1. Cargo.toml2. Cargo.lock **Cargo 的生态系统** 常用命令总结Hello, Cargo! 示例 Cargi简介 Cargo …...
【Rust 编程语言工具】rustup-init.exe 安装与使用指南
rustup-init.exe 是用于安装和管理 Rust 编程语言工具链的 Windows 可执行文件。Rust 是一种系统级编程语言,旨在提供安全、并发和高性能的功能。rustup-init.exe 是官方提供的安装器,用于将 Rust 安装到 Windows 操作系统中,并配置相关环境。…...
集群聊天服务器(12)nginx负载均衡器
目录 负载均衡器nginx负载均衡器优势 如何解决集群聊天服务器跨服务器通信问题?nginx的TCP负载均衡配置nginx配置 负载均衡器 目前最多只能支持2w台客户机进行同时聊天 所以要引入集群,多服务器。 但是客户连哪一台服务器呢?客户并不知道哪一…...
数据挖掘英语及概念
分类 classify 上涨或跌 回归 regression 描述具体数值 分类模型评估 1.混淆(误差)矩阵 confusion matrix 2.ROC曲线 receiver operating characteristic curve 接收者操作特征曲线 3.AUC面积 area under curve ROC曲线下与坐标轴围成的面积&#x…...
springboot第82集:消息队列kafka,kafka-map
官网下载链接:https://kafka.[apache].org/downloads 我下载的是[Scala]2.12 - kafka_2.12-3.1.0.tgz kafka只需要解压下载的压缩包就行了,我这里解压的路径是D:\kafka_2.12-3.1.0,kafka的运行需要依赖zookeeper,当前版本已经内置…...
sql server查看当前正在执行的sql
#统计某类sql执行次数,并按总体cpu消耗时间降序排序 with a as ( select er.session_id,db_name(er.database_id) as DBNAME,sy.last_batch AS 最后执行时间, er.cpu_time ,er.total_elapsed_time/1000 as sum_elapsed_time_s, CAST(csql.text AS varchar(8000)) A…...
STM32设计学生宿舍监测控制系统-分享
目录 前言 一、本设计主要实现哪些很“开门”功能? 二、电路设计原理图 电路图采用Altium Designer进行设计: 三、实物设计图 四、程序源代码设计 五、获取资料内容 前言 本项目旨在利用STM32单片机为核心,结合传感器技术、无线通信技…...
HAproxy 详解
一、基本概念 1.1 什么是 HAproxy? HAproxy(High Availability Proxy)是一个开源的高性能负载均衡器和反向代理服务器,它主要用于在网络上分发流量,以提高网站或应用程序的可用性和性能。HAproxy 可以处理大量的并发…...
间接采购管理:主要挑战与实战策略
间接采购支出会悄然消耗掉企业的现金流,即使是管理完善的公司也难以避免。这是因为间接支出不直接关联特定客户、产品或项目,使采购人员难以跟踪。但正确管理间接支出能为企业带来显著收益——前提是要有合适的工具。本文将分享管理间接支出的关键信息与…...
2411rust,正与整128
原文 长期以来,Rust在x86-32和x86-64架构上128位整数的对齐与C语言不一致.最近已解决此问题,但该修复带来了一些值得注意的效果. 作为用户,除非如下,否则不用担心: 1,假设i128/u128对齐,而不是用align_of 2,忽略improper_ctypes*检查,并在FFI中使用这些类. 除x86-32和x86-64…...
将 HTML 转换为 JSX:JSX 和 JSX 规则
JSX 是 JavaScript 的语法扩展。您可以在 JavaScript 文件中编写 HTML 格式。 它基于 Web、Html、Css 和 JavaScript。Web 开发人员将页面内容分别编写为 Html 文件,将设计编写为 Css 文件,将逻辑编写为 JavaScript 文件。 须知 : JSX 是一个…...
将 FastAPI 部署到生产服务器(一套 全)
将 FastAPI 部署到生产服务器(全) 文章目录 将 FastAPI 部署到生产服务器(全)一、前言二、Fastapi项目 生产环境配置1. 准备环境2. 编写 FastAPI 应用3. 使用 Uvicorn 运行应用4. 配置生产级服务器 Gunicorn4.1 配置 Gunicorn 和 …...
题解 洛谷 Luogu P1873 [COCI 2011/2012 #5] EKO / 砍树 二分答案 C/C++
题目传送门: P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷 | 计算机科学教育新生态https://www.luogu.com.cn/problem/P1873思路: 很简单的二分答案 每次找区间中点 m,判断以 m 为高度砍下的木头是否够 h 即可 代码: #defin…...
SpringCloud SaToken整合微服务 集成Redis 网关路由权限拦截 服务间内部调用鉴权
介绍 作为 API 网关,通常负责路由、负载均衡、安全控制等功能。进行 统一鉴权 的做法意味着将所有微服务的认证和授权逻辑集中到网关层,而不是每个微服务单独实现。这样做有许多好处,微服务只关心核心业务逻辑,不需要处理身份验证…...
Oracle ADB 导入 BANK_GRAPH 的学习数据
Oracle ADB 导入 BANK_GRAPH 的学习数据 1. 下载数据2. 导入数据运行 setconstraints.sql 1. 下载数据 访问 https://github.com/oracle-quickstart/oci-arch-graph/tree/main/terraform/scripts,下载, bank_accounts.csvbank_txns.csvsetconstraints.…...
优化 MFC CGridCtrl 的表格布局与功能
在使用 MFC 的 CGridCtrl 控件创建表格时,遇到的一个典型问题是,当表格滚动条出现时,最后一列会显示空白。这篇博客将记录解决这一问题的详细过程,同时总结了 CGridCtrl 初始化及优化的关键步骤,帮助开发者快速搭建一个…...
koa-body 的详细使用文档
目录 koa-body install Features Hello World - Quickstart Usage with koa-router Usage with unsupported text body type Options 关于 parsedMethods 的说明 文件支持 关于未解析请求主体的说明 一些强大的选择 使用总结 koa-body 功能齐全的 koa body 解析器中…...
信息系统与互联网中的安全、隐私及伦理问题
1 伦理(Ethics) 1.1 伦理框架(Ethical Frameworks) 自然法与权利(Natural Law and Rights) 定义:基于人类自然权利的伦理思想,强调生命、自由和财产等基本权利。应用:隐…...
马鞍山专业网站制作公司/南京seo代理
Scala中的特质类似于Java中的接口,不过trait中可以有实现的方法,也可以有属性.如果是一个trait可以用 extends 剩下的就可以用with trait Person{def eat(str:String){println(str)} } trait Worker{def work{println("working")} } class Student extends Worker w…...
学校网站怎么做推广/苏州seo招聘
产品经理如何进行项目管理?这篇就结合我自己的经验,以及拜访过的项目经理前辈来系统梳理下,如何做好一次成功的项目管理。 参考资料:项目任务管理内容较长,全文干货,心急的小伙伴建议直接定位到需要的部分&…...
佛山网站建设网站制作公司/搜索量最大的关键词
用在vue 的时候注意,将isClick这个东西弄成data里面的变量,而不是作为函数内部的公共变量,不然会达不到效果 var isClick true; btn.οnclickfunction(){if(isClick) {isClick false;//事件console.log(测试中不中);//状态setTimeout(func…...
网站建设排版规定/产品免费推广网站有哪些
1 线程实现方式 序号实现方式描述1实现Runnable接口无返回值,Thread方式也是实现Runnable接口2实现Callable接口有返回值3实现Executor接口线程池方式,ExecutorService接口继承Executor接口,Executors类使用ExecutorService接口创建线程池 2…...
龙岩小程序报价/宁波seo在线优化公司
前面的话 HTML5不仅新增了语义型区块级元素及表单类元素,也新增了一些其他的功能性元素,这些元素由于浏览器支持等各种原因,并没有被广泛使用 文档描述 <details>主要用于描述文档或文档某个部分的细节,与<summary>配…...
新网站在谷歌上面怎么做推广/网站推广优化公司
电脑使用时间久了,就容易出现C盘空间不足的情况。而这个时候,很多朋友都会选择使用安全管理软件对电脑进行清理操作,但是效果却不明显。过一段时间之后,C盘就会再次出现空间不足的情况!有很多朋友想过要对电脑C盘文件夹…...