使用自动矢量化编译Neon

概述

作为程序员,你有多种方式使用 Neon 技术:

  • 支持 Neon 的开源库,例如 Arm Compute Library提供一种最简单的方式使用 Neon
  • 编译器的自动矢量优化特性可以利用 Neon 技术自动优化你的代码
  • Neon intrinsics内建函数,编译器用相应的 Neon 指令进行了封装,你可以在 C/C++ 代码中直接使用 Neon 指令
  • 对于经验丰富的程序员来说,为了获得极佳的性能,手动编写 Neon 汇编也是一种方法

本文介绍了如何使用Arm Compiler 6中的自动矢量化功能自动生成包含Armv8 Advanced SIMD指令的代码。它包含许多 Neon 代码生成的示例,并强调了如何让编译器生成最佳的性能。

本文对 Arm 开发人员非常有用,对于那些想要使用 Neon 技术而不想编写汇编的人来说尤其有用。在本文结束时,您将获得:

  • 哪些 Arm Compiler 命令行选项可以启用生成 Advanced SIMD 代码
  • 使用 Arm Compiler 6 的各种优化功能编写C/C++代码
  • 在哪里可以找到不同编译器的文档

如果还不熟悉 Neon,在开始之前,你应该先阅读Neon 简介。

本文中的示例使用 Arm Compiler 6,专为在裸机设备上运行的嵌入式应用程序开发而设计。如果你无法获取 Arm
Compiler 6,可以使用 Arm Development Studio Gold Edition 中提供的30天免费试用版。

尽管本指南使用 Arm Compiler 6,你也可以改为其他编译器。你需要查阅编译器文档,找出示例中使用的编译器选项。可以生成 Neon 代码的自动矢量化编译器包括:

  • Arm Compiler6,专为在裸机设备上运行的嵌入式应用程序开发而设计,也是本文示例中使用的编译器
  • Arm C/C++ Compiler,专为Linux用户空间应用程序开发而设计,最初是为了高性能计算
  • LLVM‑clang,基于 LLVM 的开源工具链
  • GCC,开源 GNU 工具链

为什么要依赖编译器进行自动矢量优化?

尽管手动编写汇编或者使用 Neon intrinsics 函数可以对 Neon 进行深入控制,但是这些方法可能会导致移植复杂,成本增加。

在许多情况下,高质量的编译器可以生成设计时间少但是质量同样好的代码。允许编译器自动识别代码中可以使用 Advanced SIMD 指令的过程称为自动矢量优化。

从具体的编译技术来看,自动矢量优化包括:

  • 循环矢量优化:展开循环以减少迭代次数,同时在每次迭代中执行更多的操作
  • Superword-Level (SLP) 矢量优化:将标量运算捆绑在一起,使用全位宽的 Advanced SIMD 指令

自动矢量化编译器包括Arm Compiler6,Arm C/C++ Compiler,LLVM‑clang,GCC。

依赖编译器进行自动矢量优化的好处:

  • 只要不存在特定架构的代码,比如内联汇编或者intrinsics,用高级语言实现的程序就是可移植的
  • 现代编译器能够自动执行高级优化
  • 针对给定的微架构就像设置编译器选项一样简单,而优化汇编程序则需要对目标硬件有深入的了解

然而,自动矢量化可能并非都是正确的选择:

  • 虽然源代码可以与体系结构无关,但它可能必须依赖特定的编译器才能生成最佳的代码
  • 高级语言或编译器选项的微小变动可能会导致生成的代码发生重大且不可预测的变化

使用编译器生成 Neon 代码适用于大多数项目。仅当生成的代码无法提供必要的性能,或者高级语言不支持特定的硬件功能时,才需要使用 Neon 的其他方法。例如配置系统寄存器控制浮点功能必须在汇编代码中执行。

使用 Arm Compiler 6 编译 Neon

要启用自动矢量优化,你必须指定适当的编译器选项:

  • 选择一个具有 Neon 功能的处理器
  • 指定自动矢量优化的等级

此外,指定‑Rpass=loop编译选项会输出编译器优化循环的诊断信息,其中包括矢量化宽度和交错计数。

选择一个具有 Neon 功能的处理器

所有标准 Armv8‑A 都需要实现 Neon,因此任何 Armv8‑A 架构或处理器都允许生成 Neon 代码。

如果您只想在一个特定处理器上运行代码,则可以只针对该处理器。对于这个处理器的微架构性能已经进行了优化,因此仅需保证代码在该处理器上运行即可。

如果您希望代码在各种处理器上运行,您可以针对一种架构。生成的代码需要在实施该架构的任何处理器上运行,因此性能可能会受到影响。

选择 Armv8‑A 的AArch64 状态:

armclang --target=aarch64-arm-none-eabi

选择 Cortex-A53 的 AArch32 状态:

armclang --target=arm-arm-none-eabi -mcpu=cortex-a53

对于较旧的 Armv7 架构,Neon 是可选的,你可以使用‑mcpu‑march‑mfpu选项来指定启用 Neon。

指定自动矢量优化等级

Arm Compiler 6 提供了各种优化等级,可通过‑O选项进行选择:

OptionMeaningAuto-vectorization
-O0Minimum optimizationNever
-O1Restricted optimizationDisabled by default.
-O2High optimizationEnabled by default.
-O3Very high optimizationEnabled by default.
-OsReduce code size, balancing code size against code speed.Enabled by default.
-OzSmallest possible code sizeEnabled by default.
-OfastOptimize for high performance beyond -O3Enabled by default.
-OmaxOptimize for high performance beyond -OfastEnabled by default.

更多关于编译选项信息请查看Selecting optimization options, in the Arm Compiler User Guide和 -O, in the Arm Compiler armclang Reference Guide。

默认情况下,自动矢量优化在O2或者更高等级下启用。-fno-vectorize选项允许你禁用自动矢量优化。

在优化等级‑O1下,默认禁用自动矢量化。-fvectorize选项允许你启用自动矢量优化。

在优化级等级‑O0下,始终禁用自动矢量优化。如果指定‑fvectorize选项,编译器会忽略。

向量加法示例

让我们看看如何使用编译器选项来自动优化简单的 C 程序。

  1. 创建一个vec_add.c文件,函数将两个 32 位浮点数组相加。

    void vec_add(float *vec_A, float *vec_B, float *vec_C, int len_vec) 
    {
    	int i;
    	for (i=0; i<len_vec; i++) {
    		vec_C[i] = vec_A[i] + vec_B[i];
    	}
    }
    
  2. 编译代码,不使用自动矢量化

    armclang --target=aarch64-arm-none-eabi -g -c -O1 vec_add.c
    
  3. 反汇编生成的目标文件,查看生成的指令

    fromelf --disassemble vec_add.o -o disassembly_vec_off.txt
    

    反汇编代码类似如下:

    vec_add 			; Alternate entry point
    		CMP w3,#1
    		B.LT |L3.36|
    		MOV w8,w3
    |L3.12|
    		LDR s0,[x0],#4
    		LDR s1,[x1],#4
    		SUBS x8,x8,#1
    		FADD s0,s0,s1
    		STR s0,[x2],#4
    		B.NE |L3.12|
    |L3.36|
    		RET
    

    在这里我们可以看到该函数的标签为vec_add,后面是该函数的汇编指令。FADD指令是执行操作的核心部分,但代码未使用 Neon ,因为一次仅执行一个加法操作。我们可以看到这一点,因为FADD指令在标量寄存器S0S1上运行。

  4. 重新编译代码,这次使用自动矢量优化

    armclang --target=aarch64-arm-none-eabi -g -c -O1 vec_add.c -fvectorize
    
  5. 反汇编生成的目标文件,查看生成的指令

    fromelf --disassemble vec_add.o -o disassembly_vec_on.txt
    

    反汇编代码类似如下:

    vec_add 		; Alternate entry point
            CMP w3,#1
            B.LT |L3.184|
            CMP w3,#4
            MOV w8,w3
            MOV x9,xzr
            B.CC |L3.140|
            LSL x10,x8,#2
            ADD x12,x0,x10
            ADD x11,x2,x10
            CMP x12,x2
            ADD x10,x1,x10
            CSET w12,HI
            CMP x11,x0
            CSET w13,HI
            CMP x10,x2
            CSET w10,HI
            CMP x11,x1
            AND w12,w12,w13
            CSET w11,HI
            TBNZ w12,#0,|L3.140|
            AND w10,w10,w11
            TBNZ w10,#0,|L3.140|
            AND x9,x8,#0xfffffffc
            MOV x10,x9
            MOV x11,x2
            MOV x12,x1
            MOV x13,x0
    |L3.108|
            LDR q0,[x13],#0x10
            LDR q1,[x12],#0x10
            SUBS x10,x10,#4
            FADD v0.4S,v0.4S,v1.4S
            STR q0,[x11],#0x10
            B.NE |L3.108|
            CMP x9,x8
            B.EQ |L3.184|
    |L3.140|
            LSL x12,x9,#2
            ADD x10,x2,x12
            ADD x11,x1,x12
            ADD x12,x0,x12
            SUB x8,x8,x9
    |L3.160|
            LDR s0,[x12],#4
            LDR s1,[x11],#4
            SUBS x8,x8,#1
            FADD s0,s0,s1
            STR s0,[x10],#4
            B.NE |L3.160|
    |L3.184|
    		RET
    

    从指令FADD v0.4S、v0.4S、v1.4S可以看出,SLP自动矢量优化已经成功。这条指令将四个32位浮点数打包到一个 SIMD 寄存器中,进行加法运算。然而,这会造成代码体积增大,因为它必须检测以下情况:数组长度是否是 SIMD 宽度的整数倍。根据项目和目标硬件的不同,可以决定代码大小增加是否可以接受。这对于手机应用程序来说可以接受,因为与可用内存相比,代码大小变化微不足道,但对于具有少量 RAM 的嵌入式应用程序来说可能是无法接受。

循环中的函数示例

如果你想使用编译器的优化特性,有时对源代码的更改是不可避免的。当代码太复杂而编译器无法自动矢量优化时,或者当你想要覆盖编译器优化特定代码时,可能会发生这种情况。

  1. 创建一个cubed.c新文件,函数计算数组的立方:

    double cubed(double x) 
    {
    	return x*x*x;
    }
    void vec_cubed(double *x_vec, double *y_vec, int len_vec) 
    {
        int i;
        for (i=0; i<len_vec; i++) {
        	y_vec[i] = cubed(x_vec[i]);
    	}
    }
    
  2. 使用自动矢量优化,编译代码:

    armclang --target=aarch64-arm-none-eabi -g -c -O1 -fvectorize cubed.c
    
  3. 反汇编生成的目标文件,查看生成的指令:

    fromelf --disassemble cubed.o -o disassembly.txt
    

    反汇编代码类似如下:

    cubed 				; Alternate entry point
            FMUL d1,d0,d0
            FMUL d0,d1,d0
            RET
            AREA ||.text.vec_cubed||, CODE, READONLY, ALIGN=2
    vec_cubed 			; Alternate entry point
            STP x21,x20,[sp,#-0x20]!
            STP x19,x30,[sp,#0x10]
            CMP w2,#1
            B.LT |L4.48|
            MOV x19,x1
            MOV x20,x0
            MOV w21,w2
    |L4.28|
            LDR d0,[x20],#8
            BL cubed
            SUBS x21,x21,#1
            STR d0,[x19],#8
            B.NE |L4.28|
    |L4.48|
            LDP x19,x30,[sp,#0x10]
            LDP x21,x20,[sp],#0x20
            RET
    

    这段代码有很多问题:

    • 编译器未执行循环或SLP矢量优化,也未内联化立方函数
    • 代码需要对输入指针进行检查,保证两个数组不存在重叠

    这些问题可以通过多种方式解决,例如以更高的优化等级进行编译,但让我们重点讨论在不更改编译器选项的情况下可以更改哪些代码。

  4. 在代码中添加以下宏和限定符,可以覆盖编译器的一些编译优化

    • __attribute__((always_inline))是 Arm 编译器扩展,指示编译器始终尝试内联该函数。在这个例子中,不仅函数被内联,而且编译器还可以执行 SLP 矢量优化。

    内联之前,立方函数仅适用于标量双精度,因此不需要、也没有方法对该函数本身执行 SLP 矢量优化。

    当立方函数被内联后,编译器可以检测到其操作是在数组上执行的,就会使用 Advanced SIMD 指令对代码进行优化。

    • restrict是一个标准 C/C++ 关键字,它告知编译器为数组分别一片独立的内存区域。这就保证不需要进行数组地址重叠检测。
    • #pragma clang loop interleave_count(X)是一个 Clang 语言的扩展,可以让你指定自动矢量化的向量宽度和交错。这个指令也是Arm Compiler的一个"社区“功能

    更多矢量化的宏请参考 clang 文档。

    __always_inline double cubed(double x) 
    {
    	return x*x*x;
    }
    void vec_cubed(double *restrict x_vec, double *restrict y_vec, int len_vec) 
    {
        int i;
        #pragma clang loop interleave_count(2)
        for (i=0; i<len_vec; i++) {
        	y_vec[i] = cubed(x_vec[i]);
    	}
    }
    
  5. 同样进行编译和反汇编,生成代码如下:

    vec_cubed 		; Alternate entry point
            CMP w2,#1
            B.LT |L4.132|
            CMP w2,#4
            MOV w8,w2
            B.CS |L4.28|
            MOV x9,xzr
            B |L4.92|
    |L4.28|
            AND x9,x8,#0xfffffffc
            ADD x10,x0,#0x10
            ADD x11,x1,#0x10
            MOV x12,x9
    |L4.44|
            LDP q0,q1,[x10,#-0x10]
            ADD x10,x10,#0x20
            SUBS x12,x12,#4
            FMUL v2.2D,v0.2D,v0.2D
            FMUL v3.2D,v1.2D,v1.2D
            FMUL v0.2D,v0.2D,v2.2D
            FMUL v1.2D,v1.2D,v3.2D
            STP q0,q1,[x11,#-0x10]
            ADD x11,x11,#0x20
            B.NE |L4.44|
            CMP x9,x8
            B.EQ |L4.132|
    |L4.92|
            LSL x11,x9,#3
            ADD x10,x1,x11
            ADD x11,x0,x11
            SUB x8,x8,x9
    |L4.108|
            LDR d0,[x11],#8
            SUBS x8,x8,#1
            FMUL d1,d0,d0
            FMUL d0,d0,d1
            STR d0,[x10],#8
            B.NE |L4.108|
    |L4.132|
    		RET
    

    此反汇编表明内联、SLP 矢量化和循环矢量化已实现,使用限制指针不需要再进行重叠检查。

    由于需要处理总循环计数不是四倍的情况,代码大小略有增加。循环展开深度为2,SLP 宽度为2,因此有效展开深度为4。如果循环计数始终是4的倍数,我们可以进一步优化。

  6. 假设循环计数始终是四的倍数,我们可以通过mask循环的低位,告知编译器:

    void vec_cubed(double *restrict x_vec, double *restrict y_vec, int len_vec) 
    {
        int i;
        #pragma clang loop interleave_count(1)
        for (i=0; i<(len_vec & ~3); i++) {
        	y_vec[i] = cubed_i(x_vec[i]);
        }
    }
    
  7. 同样进行编译和反汇编,生成代码如下:

    vec_cubed 				; Alternate entry point
            AND w8,w2,#0xfffffffc
            CMP w8,#1
            B.LT |L13.40|
            MOV w8,w8
    |L13.16|
            LDR q0,[x0],#0x10
            SUBS x8,x8,#2
            FMUL v1.2D,v0.2D,v0.2D
            FMUL v0.2D,v0.2D,v1.2D
            STR q0,[x1],#0x10
            B.NE |L13.16|
    |L13.40|
    		RET
    

    代码大小减小了,因为编译器知道不再需要测试和处理任何不是四的倍数的剩余迭代。我们向编译器保证提供的数据始终是向量长度的倍数,从而可以生成更优的代码。

如果使用-O2编译优化,就无需按上面那样更改代码了,但是复杂的代码可能还是需要像上面那样调整,从而获得最佳性能。

下面是完整的代码清单,你可以尝试各种优化等级和展开深度,进行编译和反汇编,观察编译器的自动矢量优化行为。

/*
* Copyright (C) Arm Limited, 2019 All rights reserved.
*
* The example code is provided to you as an aid to learning when working
* with Arm-based technology, including but not limited to programming tutorials.
* Arm hereby grants to you, subject to the terms and conditions of this Licence,
* a non-exclusive, non-transferable, non-sub-licensable, free-of-charge licence,
* to use and copy the Software solely for the purpose of demonstration and
* evaluation.
*
* You accept that the Software has not been tested by Arm therefore the Software
* is provided "as is", without warranty of any kind, express or implied. In no
* event shall the authors or copyright holders be liable for any claim, damages
* or other liability, whether in action or contract, tort or otherwise, arising
* from, out of or in connection with the Software or the use of Software.
*/
#include <stdio.h>
void vec_init(double *vec, int len_vec, double init_val) 
{
    int i;
    for (i=0; i<len_vec; i++) {
    	vec[i] = init_val*i - len_vec/2;
    }
}

void vec_print(double *vec, int len_vec) 
{
    int i;
    for (i=0; i<len_vec; i++) {
    	printf("%f, ", vec[i]);
	}
	printf("\n");
}

double cubed(double x) 
{
	return x*x*x;
}

void vec_cubed(double *x_vec, double *y_vec, int len_vec) 
{
    int i;
    for (i=0; i<len_vec; i++) {
    	y_vec[i] = cubed(x_vec[i]);
    }
}

__attribute__((always_inline)) double cubed_i(double x) 
{
	return x*x*x;
}

void vec_cubed_opt(double *restrict x_vec, double *restrict y_vec, int len_vec) 
{
    int i;
    #pragma clang loop interleave_count(1)
    for (i=0; i<len_vec; i++) {
    	y_vec[i] = cubed_i(x_vec[i]);
    }
}

int main() 
{
    int N = 10;
    double X[N];
    double Y[N];
    vec_init(X, N, 1);
    vec_print(X, N);
    vec_cubed(X, Y, 10);
    vec_print(Y, N);
    vec_cubed_opt(X, Y, 10);
    vec_print(Y, N);
    return 0;
}

自动矢量化的编码最佳实践

如果代码实施比较复杂,编译器进行自动矢量优化的可能性会降低。例如,具有以下特征的循环体难以(或不可能)进行矢量优化:

  • 不同循环迭代之间相互依赖
  • 具有break表达式的循环
  • 具有复杂条件的循环

Arm 建议修改源代码实现以避免这些情况。

例如,自动矢量优化的一个必要条件是,在循环开始时必须知道迭代次数。break语句意味着在循环开始时可能无法知道循环次数,这将阻止自动矢量优化。如果无法完全避免break语句,可以将循环分解为多个可矢量优化和不可矢量化的部分。

关于控制循环矢量化的编译器指令,请参见LLVM-Clang 文档,其中最重要的两个是:

  • #pragma clang loop vectorize(enable)
  • #pragma clang loop interleave(enable)

这些pragma是提示编译器分别执行 SLP 和 Loop 矢量优化,他们是 Arm Compiler 的 COMMUNITY 特性。

Arm C/C++ Linux 用户空间编译器提供了更多关于自动矢量优化的说明,其中许多要点将适用于 LLVM-Clang 变体:

  • Arm C/C++ 编译器:自动矢量化的编码最佳实践
  • Arm C/C++ 编译器:使用 pragma 控制自动矢量化

检查你的知识

以下问题可以测试你学习到的知识:

  1. 什么是 Neon?

    Neon 是 Arm 架构 Advanced SIMD 扩展的实现。所有符合 Armv8-A 架构的处理器(例如,Cortex-A76或Cortex-A57)均实现 Neon。在程序员看来,Neon 提供额外32个128位寄存器,指令可以在这些寄存器的8位、16位、32位或64位通道上运行。

  2. Arm Compiler 如何启用 Neon 代码生成?

    对于 AArch64,使用--target=aarch64-arm-none-eabi,并指定合适的优化等级,例如 -O1 -fvectorize-O2 或者更高。

  3. 假设 Arm 编译器自动展开一个深度为2的循环,如何强制编译器展开到4的深度?

    #pragma clang loop interleave_count (4) 将实现这一点,但仅适用于该循环。

  4. 如何编写源代码来帮助编译器优化?

    考虑如下函数,使用-O1编译:

    float vec_dot(float *vec_A, float *vec_B, int len_vec) 
    {
        float ret = 0;
        int i;
        for (i=0; i<len_vec; i++) {
              ret += vec_A[i]*vec_B[i];
        }
        return ret;
    }
    

    您可以进行以下更改以帮助编译器优化:

    • -O2 或更高等级编译,或使用-fvectorize 编译
    • 在循环之前声明#pragma clang loop vectorize(enable) 提示编译器
    • 请注意,在此过程中,我们不会修改向量,因此添加restrict关键字不会执行任何操作,输入数组是否重叠并不重要
    • SLP 矢量优化会导致代码增加,这可能是可以接受的,具体取决于硬件限制和期望的输入数组长度

    以下是优化后的源代码:

    float vec_dot(float *vec_A, float *vec_B, int len_vec) 
    {
        float ret = 0;
        int i;
        #pragma clang loop vectorize(enable)
        for (i=0; i<len_vec; i++) {
              ret += vec_A[i]*vec_B[i];
        }
        return ret;
    }
    

    相关信息

    以下是与本文相关的一些资源:

    • Arm Compiler 6 documentation提供有关裸机编译器的信息
    • Arm C/C++ Compiler documentation提供有关 Linux 用户空间编译器的信息
    • LLVM-clang documentation提供有关基于 LLVM 开源工具链的信息
    • GCC documentation提供有关开源 GNU 工具链的信息
    • The Architecture Exporation Tools可以了解有关 Advanced SIMD 指令集的更多信息
    • The Arm Architecture Reference Manual Armv8, for Armv8-A architecture profile提供 Advanced SIMD 指令集的完整规范
    • Optimizing C Code with Neon Intrinsics guide介绍了如何在 C 或 C++ 代码中使用 Neon instrinsics,以利用 Armv8 架构 Advanced SIMD 技术

欢迎关注“安全有理”微信公众号。

安全有理

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

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

相关文章

ArcGIS如何计算地级市间的距离

一、数据准备 加载配套实验数据包中的地级市和行政区划矢量数据(订阅专栏后,从私信查收数据),如下图所示: 二、计算距离 1. 计算邻近表 ArcGIS提供了计算点和另外点之间距离的工具:分析工具→邻域分析→生成临近表。 计算一个或多个要素类或图层中的要素间距离和其他邻…

sCrypt受邀在中国人民大学举办《区块链与数字经济》课程讲座

4月17日&#xff0c;可一科技特邀美国sCrypt公司的开发工程师周全&#xff0c;在中国人民大学的《区块链与数字经济》课程上进行了讲座。周全讲解了区块链的分布式设计、不可篡改特性&#xff0c;以及智能合约的基本原理&#xff0c;利用“智能家居触发机制”等生动比喻&#x…

JS解密之新js加密实战(二)

前言 上次发了一篇关于新加密的&#xff0c;只解了前边两层&#xff0c;这中间家里各种事情因素影响&#xff0c;没有继续进一步研究&#xff0c;今天百忙之中抽空发布第二篇&#xff0c;关于其中的一小段加密片段&#xff0c;我认为分割成多个小片段是更容易被理解的。逻辑相…

ansible——playbook

一、playbook定义 Ansible Playbook是设定自动化任务的一种蓝图&#xff0c;可在无需人工干预或有限干预的前提下执行复杂的IT操作。Ansible Playbook对一组或一类共同构成 Ansible 清单的主机执行。 Ansible Playbook本质上是一些框架&#xff0c;是一些预先编写的代码&#x…

OrangePi Zero2 全志H616开发学习文档、基础IO蜂鸣器、超声波测距、舵机PWM基础开发

一.平台介绍 OrangePi开发板不仅仅是一款消费品&#xff0c;同时也是给任何想用技术来进行创作创新的人设计的。它是一款简单、有趣、实用的工具&#xff0c;你可以用它去打造你身边的世界。 特性 CPU 全志H616四核64位1.5GHz高性能Cortex-A53处理器GPU MaliG31MP2 Supports…

【树】简要理解树的概念

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 目录 1、树的概念2、树的相关概念3、结语 1、树的概念 树是一种非线性的数据结构&#xff0c;它…

[FSCTF 2023]ez_php1

一共有三小关 第一关&#xff1a;md5加密 第二关&#xff1a;反序列化 重点 单个字符串序列化 unserialize($str) "$KEY" <?php $KEY"YES I love";echo serialize($KEY); s:10:"YES I love"; 第三关&#xff1a; 反序列化 把a的地址赋给…

【SpringBoot】 什么是springboot(二)?springboot操作mybatisPlus、swagger、thymeleaf模板

文章目录 SpringBoot第三章1、整合mybatsPlus1-234-67-10问题 2、整合pageHelper分页3、MP代码生成器1、编写yml文件2、导入依赖3、创建mp代码生成器4、生成代码5、编写配置类扫描mapper类6、编写控制器类 4、swagger1、什么是swagger2、作用3、发展历程4、一个简单的swagger项…

Hexo博客重新部署与Git配置

由于电脑重装了一次&#xff0c;发现之前Hexo与NexT主题版本过于落后&#xff0c;重新部署了下。 1 Node.js与git安装 这一块安装就不赘述了。去两个官网找安装文件安装即可。 node.js git 打开git以后配置的几个关键命令行。 git config --global user.name "你的gi…

ArcGIS水文水环境数据编辑、管理、处理与分析;ArcGIS水文分析及流域特征提取;湖泊水库水环境监测及评价;河道水污染预测与水环境容量计算等案例实践

目录 专题一 ArcGIS&#xff1a;数据管理 专题二 ArcGIS&#xff1a;数据转换 专题三 ArcGIS&#xff1a;地图制作 专题四 水文水环境数据编辑与管理 专题五 水文水环境数据处理与分析 专题六 ArcGIS水文分析及流域特征提取 专题七 湖泊水库水环境监测及评价 专题八 河…

java学习之zip炸弹攻击

一、概述 Zip炸弹是一种特殊类型的Zip文件&#xff0c;它包含了大量的无用数据。Zip文件格式允许使用压缩算法来减小文件的大小&#xff0c;但是如果Zip文件中的某些内容被重复压缩&#xff0c;就会导致文件大小急剧增加。Zip炸弹利用这个特性&#xff0c;将一些无用的数据多次…

配置接口的主从IP地址

组网需求 如图1所示&#xff0c;Router上只有一个空闲接口GE1/0/0&#xff0c;但该局域网中的计算机分别属于2个不同的网段10.16.1.0/24和10.16.2.0/24&#xff0c;要求通过Router可以实现一个接口接入两个不同的网段。 图1 配置IP地址示例 配置思路 配置主从IP地址的思路…

【C++】STL-list的使用

目录 1、list的使用 1.1 list的构造 1.2 list的遍历 1.3 list capacity 1.4 list element access 1.5 容量相关 list是一个带头双向循环链表 1、list的使用 1.1 list的构造 1.2 list的遍历 list只有两种遍历方式&#xff0c;因为没有operator[] 因为list的双向链表&am…

Docker复习

文章目录 基础Docker基础命令镜像操作命令容器操作命令 案例:安装MySql案例:查看DockerHub&#xff0c;拉取Nginx镜像&#xff0c;并运行容器 基础 Docker基础命令 启动Docker systemctl start docker镜像操作命令 从远程仓具下载镜像到本地 docker pull 镜像名称 无版本号…

高并发系统设计-系统的“三高“目标

目录 一、高并发 1.高并发相关指标 2.如何提高并发能力 二、高并发的目标 1.高性能 2.高可用 3.高扩展 一、高并发 高并发&#xff08;High Concurrency&#xff09;是互联网分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计保证系统能…

parallelsdesktop19密钥激活 PD19虚拟机完整图文安装教程

Parallels Desktop 19 &#xff08;简称 PD 19)是最新发布的 macOS 平台的 windows 虚拟机&#xff0c;本文是使用 Parallels Desktop 19 虚拟机安装 Windows 的详细图文破解安装教程。 一下载安装 Parallels Desktop 软件下载完成后打开&#xff0c;双击打开 安装.dmg Para…

1070: 邻接矩阵存储简单路径

解法&#xff1a; #include<iostream> #include<vector> using namespace std; int arr[100][100]; int n; int sta, des; vector<int> path; vector<vector<int>> res; void dfs(vector<int> &a,int i) {a[i] 1;path.push_back(i);…

【漏洞复现】osCommerce install.php存在远程代码执行漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

第5章 处理GET请求参数

1 什么是GET请求参数 表单GET请求参数是指在HTML表单中通过GET方法提交表单数据时所附带的参数信息。在HTML表单中&#xff0c;可以通过表单元素的name属性来指定表单字段的名称&#xff0c;通过表单元素的value属性来指定表单字段的值。当用户提交表单时&#xff0c;浏览器会将…

C# WinForm —— 16 MonthCalendar 介绍

1. 简介 可以选择单个日期&#xff0c;也可以选择一段日期&#xff0c;在选择时间范围上 比较适用&#xff0c;但不能跨月份选择日期范围 在直观上&#xff0c;可以快速查看、选择日期/日期范围 2. 常用属性 属性解释(Name)控件ID&#xff0c;在代码里引用的时候会用到,一般…