.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

在这里插入图片描述

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

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

相关文章

深入解析TK技术下视频音频不同步的成因与解决方案

随着互联网和数字视频技术的飞速发展&#xff0c;音视频同步问题逐渐成为网络视频播放、直播、编辑等过程中不可忽视的技术难题。尤其是在采用TK&#xff08;Transmission Keying&#xff09;技术进行视频传输时&#xff0c;由于其特殊的时序同步要求&#xff0c;音视频不同步现…

MongoDB:数据迁移

业余人员学习 第一种:通过MongoDB命令 参考链接: MongoDB的备份(mongodump)与恢复(mongorestore)_MongoDB_脚本之家 MongoDB数据库管理:全面掌握mongodump和mongorestore的备份与恢复技巧_8055096的技术博客_51CTO博客 1.1、首先进入操作命令行,都不需要进入MongoDB […

网络安全练习之 ctfshow_web

文章目录 VIP题目限免&#xff08;即&#xff1a;信息泄露题&#xff09;源码泄露前台JS绕过协议头信息泄露robots后台泄露phps源码泄露源码压缩包泄露版本控制泄露源码(git)版本控制泄露源码2(svn)vim临时文件泄露cookie泄露域名txt记录泄露敏感信息公布内部技术文档泄露编辑器…

【俄罗斯方块】

【俄罗斯方块】 C语言实现C实现Java实现Python实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 首先把经典的俄罗斯方块简化一下&#xff1a;方块有顺序地从屏幕顶端掉下至底部&#xff0c;当碰到障碍物或底部时停下&#xff0c;同时变成…

优化装配,提升品质:虚拟装配在汽车制造中的关键作用

汽车是各种零部件的有机结合体&#xff0c;因此汽车的装配工艺水平和装配质量直接影响着汽车的质量与性能。在汽车装配过程中&#xff0c;经常会发生零部件间干涉或装配顺序不合理等现象&#xff0c;且许多零部件制造阶段产生的质量隐患要等到实际装配阶段才能显现出来&#xf…

【算法】日期问题(C/C++)

目录 日期问题概述 一、闰年判断 问题描述&#xff1a; 解决方法&#xff1a; 代码实现&#xff1a; 二、回文日期 问题描述&#xff1a; 链接&#xff1a;2867. 回文日期 - AcWing题库 解决方法&#xff1a; 代码实现&#xff1a; 三、日期差值 问题描述&#xff1…

FIFO架构专题-异步FIFO及信号

概述 FIFO按时钟可分为&#xff1a;异步FIFO、同步FIFO。 定义 同步FIFO&#xff1a;读时钟和写时钟都相同的FIFO。同步FIFO内部没有异步处理&#xff0c;因此结构简单&#xff0c;资源占用较少。 异步FIFO&#xff1a;读时钟和写时钟可以不同的FIFO。异步FIFO内部有专门的异…

类和对象(上)--类、类的实例化(对象)、this指针

1.类 1.1定位&#xff1a; 和namespace一样&#xff0c;类也有类域。同样起到既保护又限制的功能。别人不能随意访问类里的东西&#xff0c;得通过特定的方式来访问&#xff08;访问方法和命名空间域一样&#xff0c;三种方法&#xff09;。 1.2作用 在C语言中&#xff0c;…

Leetcode 路径总和

使用递归算法 class Solution {public boolean hasPathSum(TreeNode root, int targetSum) {// 如果节点为空&#xff0c;返回falseif (root null) {return false;}// 如果是叶子节点&#xff0c;检查路径和是否等于目标值if (root.left null && root.right null) …

程序里sendStringParametersAsUnicode=true的配置导致sql server cpu使用率高问题处理

一 问题描述 近期生产环境几台sql server从库cpu使用率总是打满&#xff0c;发现抓的带变量值的慢sql&#xff0c;手动代入变量值执行并不慢&#xff0c;秒级返回&#xff0c;不知道问题出在哪里。 二 问题排查 用扩展事件或者sql profiler抓慢sql&#xff0c;抓到了变量值&…

传输层协议TCP

一.TCP协议格式 对于传输层协议我们之前是学过了UDP&#xff0c;对于传输层协议是存在了一定的了解的&#xff0c;所以现在我们再来看TCP协议格式&#xff1a; 我们之前学过UDP的报文格式&#xff0c;所以源端口和目的端口是不需要进行再次讲解的&#xff0c;对于32序号和确认序…

学习笔记024——Ubuntu 安装 Redis遇到相关问题

目录 1、更新APT存储库缓存&#xff1a; 2、apt安装Redis&#xff1a; 3、如何查看检查 Redis版本&#xff1a; 4、配置文件相关设置&#xff1a; 5、重启服务&#xff0c;配置生效&#xff1a; 6、查看服务状态&#xff1a; 1、更新APT存储库缓存&#xff1a; sudo apt…

C++为函数提供的型特性——缺省参数与函数重载

目录 一、缺省参数 二、函数重载 一、缺省参数 C为函数提供了一项新的特性——缺省参数。缺省参数指的是当前函数调用中省略了实参自动使用的一个值。这极大地提高了函数的灵活性 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值 。在调⽤该函数时&#xff0c;如果没有…

前端框架Vue3基础部分

什么是Vue&#xff1f; Vue是一个能用于构建用户交互页面&#xff08;动态网页&#xff09;的渐进式JavaScript框架&#xff0c;易学易用&#xff0c;性能出色&#xff0c;适用性强的Web前端框架。 Vue的设计模式&#xff1f; Vue的设计模式&#xff1a;MVVM模式 MVVM设计模…

安达发|APS自动排程软件异常预警处理场景介绍

APS生产排单软件通过预先设定好相关基本资料与约束规则&#xff0c;当订单、机台、工具、材料、上下班时间等任何影响生产计划的因素变化后&#xff0c;只需执行“一键式排程计算”&#xff0c;系统即可生成相应的生产计划。它不仅能够高效地安排生产任务&#xff0c;优化资源分…

在阿里云快速启动Appsmith搭建前端页面

什么是Appsmith Appsmith是一个开源的低代码开发平台&#xff0c;它使得开发者能够快速地构建内部工具、业务管理系统、CRM系统等。Appsmith通过提供一系列预建的UI组件&#xff08;如表格、图表、表单等&#xff09;&#xff0c;以及对数据库、API调用的直接支持&#xff0c;…

命令执行简单(棱角社区有毒)

前言&#xff1a;小迪安全2022第一节反弹shell&#xff0c;小迪用的是两台都是云服务器&#xff0c;没有服务器可以在自己的主机上搭建也是可以的&#xff0c;主机上搭两个网站 思路&#xff1a;生成一个木马文件&#xff0c;下载到本机&#xff0c;然后利用本机上传到目标主机…

基于Ruoyi的同一token跨系统访问,后端单点登录并且鉴权方案

基于Ruoyi的同一token跨系统访问,后端单点登录并且鉴权方案 需求场景以及先决条件默认方案改造思路改造代码,一共4个类需要变更完整需要修改的代码 需求场景以及先决条件 同一环境下的多个ruoyi项目,各自使用相同的一组用户(我这里用的是LDAP的登录,不影响本文),但是每个权限拥…

图像/文字差异类型验证码识别 无需训练

某像差异在个别全家桶验证方便有使用&#xff0c;对于这种验证码类型如下&#xff1a; 首先还是目标检测&#xff0c;直接用 dddd 自带的detection 就足够了。 特征提取 其次经过观察&#xff0c;差异答案与其他三个无非就是颜色&#xff0c;字体&#xff0c;方向&#xff0c…

【AI+教育】一些记录@2024.11.16

《万字长文&#xff0c;探讨关于ChatGPT的五个最核心问题》 万字长文&#xff0c;探讨关于ChatGPT的五个最核心问题关于 ChatGPT 铺天盖地的信息让人无所适从。本文则试图提炼出五个关键问题&#xff1a;如何理解这次范式突破&#xff0c;未来能达到的技术天花板&#xff0c;行…