1. 实例编译                运行

main.cu

//nvcc -g -lineinfo -std=c++17 -arch=native main.cu -o main#include <iostream>
#include <thrust/device_vector.h>/*
ldmatrix.sync.aligned.shape.num{.trans}{.ss}.type r, [p];.shape  = {.m8n8};
.num    = {.x1, .x2, .x4};
.ss     = {.shared{::cta}};
.type   = {.b16};
*/__device__ 
void ldmatrix_x2(unsigned int (&x)[2], const void* ptr){ asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0, %1}, [%2];" : "=r"(x[0]), "=r"(x[1]): "l"(__cvta_generic_to_shared(ptr))); 
}__global__
void mykernel(const int* loadOffsets, bool print){alignas(16) __shared__ half A[128 * 16];for(int i = threadIdx.x; i < 128*16; i += blockDim.x){A[i] = i;}__syncthreads();const int lane = threadIdx.x % 32;unsigned int result[2];const int offset = loadOffsets[lane];ldmatrix_x2(result, &A[offset]);half2 loaded[2];memcpy(&loaded[0], &result[0], sizeof(half2) * 2);if(print){for(int m = 0; m < 2; m++){for(int t = 0; t < 32; t++){if(lane == t){printf("%4d %4d ", int(loaded[m].x), int(loaded[m].y));if(lane % 4 == 3){printf("\n");}}__syncwarp();}if(lane == 0){printf("\n");}__syncwarp();}}
}int main(){thrust::device_vector<int> d_loadOffsets(32, 0);for(int i = 0; i < 16; i++){const int row = i % 8;const int matrix = i / 8;d_loadOffsets[i] = row * 16 + matrix * 8;}mykernel<<<1,32>>>(d_loadOffsets.data().get(), true);cudaDeviceSynchronize();// Shared Load Matrix: Requests 16.384, Wavefronts 33.393, Bank Conflicts 0for(int i = 0; i < 16; i++){const int row = i / 2;const int matrix = i % 2;d_loadOffsets[i] = row * 16 + matrix * 8;}std::cout << "offsets: ";for(int i = 0; i < 16; i++){std::cout << d_loadOffsets[i] << " ";}std::cout << "\n";mykernel<<<1024,512>>>(d_loadOffsets.data().get(), false);cudaDeviceSynchronize();// Shared Load Matrix: Requests 16.384, Wavefronts 131.674, Bank Conflicts 98.304for(int i = 0; i < 16; i++){const int row = i / 2;const int matrix = i % 2;d_loadOffsets[i] = (4*row) * 16 + matrix * 8;}std::cout << "offsets: ";for(int i = 0; i < 16; i++){std::cout << d_loadOffsets[i] << " ";}std::cout << "\n";mykernel<<<1024,512>>>(d_loadOffsets.data().get(), false);cudaDeviceSynchronize();// Shared Load Matrix: Requests 16.384, Wavefronts 66.488, Bank Conflicts 32.768for(int i = 0; i < 16; i++){const int row = i % 8;const int matrix = i / 8;d_loadOffsets[i] = row * 16 + matrix * 8;}std::cout << "offsets: ";for(int i = 0; i < 16; i++){std::cout << d_loadOffsets[i] << " ";}std::cout << "\n";mykernel<<<1024,512>>>(d_loadOffsets.data().get(), false);cudaDeviceSynchronize();// Shared Load Matrix: Requests 16.384, Wavefronts 263.070, Bank Conflicts 229.376for(int i = 0; i < 16; i++){const int row = i % 8;const int matrix = i / 8;d_loadOffsets[i] = (4*row) * 16 + matrix * 8;}std::cout << "offsets: ";for(int i = 0; i < 16; i++){std::cout << d_loadOffsets[i] << " ";}std::cout << "\n";mykernel<<<1024,512>>>(d_loadOffsets.data().get(), false);cudaDeviceSynchronize();
}

编译运行:

nvcc -g -lineinfo -std=c++17 -arch=native main.cu -o main

或者 device 代码 debug版:

$ nvcc -g -G -std=c++17 -arch=native main.cu -o main

修改程序后,

//nvcc -g -lineinfo -std=c++17 -arch=native main.cu -o main#include <iostream>
#include <thrust/device_vector.h>/*
ldmatrix.sync.aligned.shape.num{.trans}{.ss}.type r, [p];.shape  = {.m8n8};
.num    = {.x1, .x2, .x4};
.ss     = {.shared{::cta}};
.type   = {.b16};
*/__device__
void ldmatrix_x2(unsigned int (&x)[2], const void* ptr){asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0, %1}, [%2];": "=r"(x[0]), "=r"(x[1]): "l"(__cvta_generic_to_shared(ptr)));
}__global__
void mykernel(const int* loadOffsets, bool print){alignas(16) __shared__ half A[128 * 16];for(int i = threadIdx.x; i < 128*16; i += blockDim.x){A[i] = i;}__syncthreads();const int lane = threadIdx.x % 32;unsigned int result[2];//one int, 2 half mem spaceconst int offset = loadOffsets[lane];ldmatrix_x2(result, &A[offset]);half2 loaded[2];memcpy(&loaded[0], &result[0], sizeof(half2) * 2);if(print){for(int m = 0; m < 2; m++){for(int t = 0; t < 32; t++){//if(lane == t){if(lane == t){printf("[%2d]%4d %4d ",lane, int(loaded[m].x), int(loaded[m].y));if(lane % 4 == 3){printf("\n");}}__syncwarp();}if(lane == 0){printf("\n");}__syncwarp();}}
}int main(){thrust::device_vector<int> d_loadOffsets(32, 0);for(int i = 0; i < 16; i++){const int row = i % 8;const int matrix = i / 8;d_loadOffsets[i] = row * 16 + matrix * 8;printf("%d ", row*16 + matrix*8);}
printf("\n\n");//thrust::host_vector<int> h_loadOffsets(32, 0);
int * hld = nullptr;
hld = (int*)malloc(32*sizeof(int));
cudaMemcpy(hld, d_loadOffsets.data().get(), sizeof(int)*32, cudaMemcpyDeviceToHost);for(int i=0; i<32; i++)printf("%d, ", hld[i]);
printf("\n");mykernel<<<1,32>>>(d_loadOffsets.data().get(), true);cudaDeviceSynchronize();}

同上编译方式,输出:

比较容易发现搬运数据映射关系

这里我们先猜一下其数据关系,

        首先,矩阵以m8n8为一个小矩阵加载进warp 的 32个 lane中,每个lane 从这个小矩阵中拿到两个地址连续的变量;x2,是说一次 load 两个8x8小矩阵,这样的话,每个lane 会得到4个变量;x4的话,就是4*2=8个变量。

        每个小矩阵需要提供8个行的起始地址,第一个小矩阵的8个行起始地址填写在0~7号 lane 的寄存器中;第二个小矩阵的8个行起始地址填写在 8~15号lane的寄存器中,各个lane中同名寄存器作为 ldmatrix 的参数。即,代码中的 const void* ptr 。

        可以推得,如果是x4,4个8x8 的小矩阵,那么需要提供4组8个行的起始地址,这样,32个 lane 每个都持有一个小矩阵的行起始地址。

        第二节第三节再深入系统地分析。

2. 实例功能解析

        进一步详细解析这个执行了 ldmatrix 的 CUDA Device 函数,这是一个非常经典且高效的用法。

2.1. 函数签名解析

__device__ void ldmatrix_x2(unsigned int (&x)[2], const void* ptr)

__device__

                cuda 语法,声明这是一个在 GPU 上执行的函数。

unsigned int (&x)[2]

                这是一个对包含 2 个 unsigned int 的数组的引用,C++ 语法。使用引用 (&) 允许函数直接修改调用者传入的数组元素,避免了传值拷贝。这个数组的两个元素 x[0] 和 x[1] 将被用作内联汇编中的目标寄存器

const void* ptr

                这是一个指向共享内存中某个数据的通用指针(generic)const 表示函数不会通过这个指针修改数据,void* 提供了灵活性,可以指向任何类型的数据。

2.2. 内联汇编详解

asm volatile("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0, %1}, [%2];": "=r"(x[0]), "=r"(x[1])  // Output operands: "l"(__cvta_generic_to_shared(ptr))); // Input operand

ptx ISA ldmatrix spec

我们一点一点地分解:

2.2.1. 汇编模板字符串

("ldmatrix.sync.aligned.m8n8.x2.shared.b16 {%0, %1}, [%2];")

这是要执行的 PTX 指令。

    ldmatrix: 指令本身,用来加载矩阵。

    .sync: Warp 级同步指令,确保 Warp 内所有活跃线程协同执行。

    .aligned强制要求源内存地址 (ptr) 必须是 16 字节对齐的。

    .m8n8: 指定从内存中加载的数据布局对应于一个 8 行 x 8 列的矩阵。这个形状的矩阵数据元素只能是 16bit的。还可以有 .m16n16、.m8n16,这是对应 8bit/6bit/4bit 矩阵元素。

    .x2: 指定一次执行 ldmatrix 时,加载 2  个 m8n8 的小矩阵。

                  The values .x1.x2 and .x4 for .num indicate one, two or four matrices respectively. When .shape is .m16n16, only .x1 and .x2 are valid values for .num.

    .shared: 明确指定源数据位于共享内存(Shared Memory) 中。

    .b16: 指定内存访问模式。.b16 表示这是一次 16 字节的访问。这与共享内存的 bank 宽度和高效访问模式有关。

    {%0, %1}: 这是目标操作数列表。占位符 %0 和 %1 将会被编译器替换为后面约束列表中找到的实际寄存器。这里它要求 2 个 32 位的寄存器。

                为什么是 2 个 一个 8x8 的矩阵,每个元素 2 字节,总大小为 8 * 8 * 2 = 128 字节。一个 Warp 有 32 个线程。ldmatrix 指令将这 128 字节的数据转置后,分布到整个 Warp 的线程寄存器中。每个线程负责 128/32 = 4 字节的数据。一个 32 位寄存器是 4 字节,所以每个线程需要 1 个寄存器来存储它的那部分数据。那么为什么这里列表里有 2 个?实际上,这条指令是在加载2个这样的 8x8 矩阵。这里的关键在于指令的变体。在 SM_70+ 上,ldmatrix 可以加载 1、2 或 4 个矩阵。

    [%2]: 这是源操作数。它是一个包含共享内存地址的寄存器,地址为16bit aligned。%2 将被替换为输入操作数提供的值。

2.2.2. 输出操作数

(: "=r"(x[0]), "=r"(x[1]))

    "=r": 约束修饰符。

      = 表示这是一个只写的输出操作数。

      r register 之意,表示要求编译器分配一个32 位通用寄存器来保存这个值。

    (x[0]), (x[1]): 对应的 C++ 变量。指令执行后,目标寄存器 %0 和 %1 中的值会被写回到数组 x 的这两个元素中。

          作用:告诉编译器:“请为 x[0] 和 x[1] 分配两个寄存器。执行汇编指令后,结果将在这两个寄存器中,请将它们写回 x[0] 和 x[1]。”

2.2.3. 输入操作数

(: "l"(__cvta_generic_to_shared(ptr)))

这是最精妙和关键的部分。

  __cvta_generic_to_shared(ptr): 这是一个 CUDA 内部函数。

            作用:它将一个通用指针 (ptr) 转换为其对应的共享内存空间下的地址值

            原理:在 PTX 中,不同的内存空间(全局、共享、本地等)有独立的地址空间。一个通用(generic)的 void* 指针不能直接用于 ldmatrix 的 shared 操作。这个函数执行必要的位操作,提取出专用于共享内存地址空间的地址比特位。

  "l": 这是一个约束修饰符

            l location register 之意。表示一个 32 位的专用寄存器,通常用于存储地址**。这与通用寄存器 r 略有不同,编译器知道这个寄存器将用于寻址。

    作用:告诉编译器:“计算 __cvta_generic_to_shared(ptr) 这个表达式的值,并将其放入一个专用的地址寄存器中,然后在汇编模板中用 %2 来引用这个寄存器。”

2.2.4. volatile 关键字

    防止编译器优化掉这条汇编指令(例如,因为它看起来没有使用输出 x),或者将其移出循环。确保指令严格按照代码中的位置和执行次数运行。

2.3. 函数功能总结

这个 ldmatrix_x2 函数的功能是:

        让一个 Warp(32 个线程)协同工作,从共享内存中 ptr 所指的、16 字节对齐的地址开始,加载 2 个连续的 8x8 矩阵(每个元素 2 字节)。数据在加载过程中会被重新排列(转置)。加载完成后,每个线程会获得 8 字节(2 个 unsigned int)的数据,存储在其 x[0] 和 x[1] 中。

        这些数据通常是更大矩阵乘法操作中的一个小块(Tile)。每个线程持有的 x[0] 和 x[1] 是转置后矩阵的一小部分,它们的形式非常适合直接作为输入喂给后续的 mma(矩阵乘加)指令,从而实现极其高效的矩阵计算。

注意事项:

  1. 调用约定:这个函数必须由整个 Warp 的线程同时调用,且 ptr 的值在 Warp 内必须一致(通常是通过广播获得)。

  2. 对齐ptr 必须是 16 字节对齐的,否则行为未定义。

  3. 数据布局:共享内存中的数据必须按照 ldmatrix 指令所期望的布局进行排列,这通常由之前的数据存储步骤(例如使用 st.shared.v2.b32 之类的指令)来保证。

这个函数是手动优化 CUDA 核函数、充分发挥 Tensor Core 性能的典型代表。

3. ldmatrix 功能系统解析

    CUDA PTX 中的 ldmatrix 指令是高效利用 Tensor Cores(张量核心)进行矩阵计算的关键所在。接着前面的具体实例,这里更为系统第介绍一下 ldmatrix 指令的原理用法。

3.1. 指令概述与原理

目的

    ldmatrix(Load Matrix)指令用于从一个线程束(Warp)内线程协同访问的连续共享内存区域中,高效地加载一个小的、密集的矩阵块(如 8x8),并将其转置后分布到该 Warp 中多个线程的寄存器中。

核心思想

        Tensor Cores 执行的是 D = A * B + C 操作,其中 A、B、C、D 都是小矩阵。然而,全局内存或共享内存中的数据通常按行主序或列主序存储。ldmatrix 指令在数据从共享内存加载到寄存器的过程中,巧妙地完成了数据重排(转置),使得数据在寄存器中的布局恰好符合 Tensor Cores 所期望的输入格式,从而避免了显式的转置操作,极大提升了效率。

工作原理

        一个 Warp(32 个线程)共同协作,从共享内存中读取一片连续的数据。每个线程负责读取数据的一部分。指令会自动地将这些数据重新组织(转置),并存入指定线程的指定寄存器。最终,整个 Warp 的寄存器合在一起,就构成了一个完整的、经过转置的矩阵。

3.2. 指令语法格式

完整的 PTX 语法如下:

ldmatrix.sync.aligned.{num}{.trans}{.ss}.type [rd1, rd2, ...], [rs1, rs2]; 
// 或者更常见的格式,指定矩阵形状:
ldmatrix.sync.aligned.shape.{num}{.trans}{.ss}.rspace [rd1, rd2, ...], [rs];

3.3. 指令中各域详解

.sync (Synchronization)

        作用

                指定这是一个Warp-level 同步指令。指令的执行会涉及 Warp 中所有活跃线程的协同操作。.sync 后缀确保所有线程在逻辑上同时参与此次加载。

        可选值

                在较新的架构中,可以指定 .sync.syncid 以实现更细粒度的同步,但通常直接使用 .sync

.aligned (Alignment)

        作用

                指定共享内存的源地址必须是 16 字节对齐的。这是为了满足内存子系统的高效访问要求。如果地址未对齐,执行结果将是未定义的。

        注意

                这是一个强制要求,不是可选项。你必须确保传入的共享内存指针是 16 字节对齐的。

.{num} (Number of Matrices)

        作用

                指定一次指令调用要加载的矩阵数量。

        可选值

                .1:加载 1 个矩阵;

                .2:加载 2 个矩阵;.4:加载 4 个矩阵;

        影响

                加载的矩阵数量直接决定了目标寄存器的数量。例如,加载一个 8x8x16 的矩阵(.m8n8 + .x2)需要 4 个寄存器(8*8*2/32/1?更正:通常加载 1 个 .m8n8.x4 矩阵需要 8 个寄存器)。加载 .4 个矩阵就需要 4 倍数量的寄存器。

.{trans} (Transposition)

        作用

                指定是否对加载的矩阵进行转置

        可选值

                (空):不进行转置,按原样加载;

                .trans:对加载的矩阵进行转置;

        这是关键

                这个功能是为了适配 Tensor Cores 的输入。例如,在计算 A * B 时,可能需要将 B 矩阵转置后再输入给 Tensor Core。使用 .trans 可以在加载时一步完成,无需后续单独的转置指令。

.{ss} (Element Size / Storage Spacing)

        作用

                指定源数据中每个矩阵元素的大小和存储间隔

        可选值

                .x1:8 位元素(如 charuint8_t);.x2:16 位元素(如 half__halfshort)。这是用于 FP16 张量计算最常见的大小;

                .x4:32 位元素(如 floatint);

.{type} / .{rspace} (Type / Resource Space)

        作用

                指定源数据所在的内存空间

        可选值

                .shared:源数据位于共享内存中。这是 ldmatrix 最常用、最主要的使用场景;

                .global:源数据位于全局内存中。(在某些架构上支持,但不如从共享内存加载高效);

.[rd1, rd2, ...] (Destination Registers)

        作用

                目标操作数,是一个寄存器列表,用于接收加载来的矩阵数据。

        要求

                寄存器的数量取决于 {num}{ss} 和矩阵形状。例如,加载 1 个 8x8 的矩阵(.m8n8),每个元素是 32位.x4),则需要 (8 * 8 * 4) / 32 = 8 个 32 位寄存器;

                寄存器必须是 32 位宽的(例如 %r0%f1);

                列表中的寄存器必须是连续的;

.[rs1, rs2] / [rs] (Source Address)

        作用

                源操作数,是包含共享内存地址的寄存器

        要求

                通常是一个包含 32 位地址的寄存器(例如 %r0);

                该地址必须指向共享内存,并且必须是 16 字节对齐的(由 .aligned 保证);

.{shape} (Matrix Shape - 替代方案)

        作用

                另一种语法是明确指定矩阵的形状,这通常更直观。

        可选值

        .m8n8:加载一个 8x8 的矩阵。这是最常用的形状;

        .m8n8k4 等:用于更复杂的加载模式,但 .m8n8 是基础;

3.4. 用法示例与解释

        假设我们要从共享内存加载一个 8x8 的 FP16 矩阵,并对其进行转置,然后分布到寄存器中。

PTX 代码:

ldmatrix.sync.aligned.m8n8.x2.trans.shared.b16 {%0, %1, %2, %3}, [%4];

分解

        .sync.aligned:Warp 同步且地址对齐;

        .m8n8:加载 8x8 的矩阵;

        .x2:源元素是 16 位(FP16);

        .trans:加载时进行转置;

        .shared.b16:从共享内存以 16 字节的访问模式读取;

        {%0, %1, %2, %3}:需要 4 个 32 位目标寄存器;

                *计算:一个 8x8 FP16 矩阵总大小 = 8 * 8 * 2字节 = 128 字节。一个 Warp 有 32 个线程,每个线程负责 128 / 32 = 4 字节的数据。一个 32 位寄存器正好是 4 字节,所以每个线程需要 1 个寄存器。但为什么这里有 4 个?实际上,ldmatrix 指令的寄存器列表是每个线程持有的寄存器数量?不,更准确的说法是:这条指令为整个 Warp 指定了 4 个连续的寄存器,但每个线程看到的是这些寄存器中的不同部分。通常,加载一个 .m8n8.x2 矩阵需要 4 个目标寄存器。

        [%4]:源地址寄存器,其值是一个 16 字节对齐的共享内存地址;

在 CUDA C++ 中的内联汇编用法:

__shared__ half smem_buffer[64]; // 8x8 FP16 矩阵asm volatile ("ldmatrix.sync.aligned.m8n8.x2.trans.shared.b16 {%0, %1, %2, %3}, [%4];": "=r"(reg0), "=r"(reg1), "=r"(reg2), "=r"(reg3) // 4个输出寄存器: "r"(smem_buffer) // 输入:共享内存地址// 可能还需要 clobber 列表,但有时可省略
);

3.5. 小结

    ldmatrix 是一条极其强大的指令,它将数据加载数据重排(转置) 两个耗时的操作合并为一条高效的硬件指令。它的设计完美契合了 Tensor Cores 的工作方式,是实现高性能矩阵乘法(尤其是深度学习推理和训练)的核心原语之一。理解其各个参数的含义对于在 PTX 或 CUDA 内联汇编中正确使用它至关重要。

4. 附录

4.1. 示例的 ptx生成

生成 ptx 文件:

nvcc -ptx -lineinfo -std=c++17 -arch=native main.cu -o main.ptx

或者不带源码行号

$ nvcc -ptx --gpu-architecture=sm_120 main.cu -o main_sm_120.ptx

4.2. m8n8.x4 的示例

//nvcc -g -lineinfo -std=c++17 -arch=native main.cu -o main#include <iostream>
#include <thrust/device_vector.h>/*
ldmatrix.sync.aligned.shape.num{.trans}{.ss}.type r, [p];.shape  = {.m8n8};
.num    = {.x1, .x2, .x4};
.ss     = {.shared{::cta}};
.type   = {.b16};
*/__device__
void ldmatrix_x2(unsigned int (&x)[4], const void* ptr){asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0, %1, %2, %3}, [%4];": "=r"(x[0]), "=r"(x[1]), "=r"(x[2]), "=r"(x[3]): "l"(__cvta_generic_to_shared(ptr)));
}__global__
void mykernel(const int* loadOffsets, bool print){alignas(16) __shared__ half A[128 * 16];for(int i = threadIdx.x; i < 128*16; i += blockDim.x){A[i] = i;}__syncthreads();const int lane = threadIdx.x % 32;unsigned int result[4];//one int, 2 half mem spaceconst int offset = loadOffsets[lane];ldmatrix_x2(result, &A[offset]);half2 loaded[4];memcpy(&loaded[0], &result[0], sizeof(half2) * 4);if(print){for(int m = 0; m < 4; m++){for(int t = 0; t < 32; t++){//if(lane == t){if(lane == t){printf("[%2d]%4d %4d ",lane, int(loaded[m].x), int(loaded[m].y));if(lane % 4 == 3){printf("\n");}}__syncwarp();}if(lane == 0){printf("\n");}__syncwarp();}}
}int main(){thrust::device_vector<int> d_loadOffsets(32, 0);for(int i = 0; i < 32; i++){const int row = i % 8 + (i/16)*8; // row of m8n8const int matrix = (i%16) / 8;    // colum of m8n8d_loadOffsets[i] = row * 16 + matrix * 8;printf("%d ", row*16 + matrix*8);}
printf("\n\n");//thrust::host_vector<int> h_loadOffsets(32, 0);
int * hld = nullptr;
hld = (int*)malloc(32*sizeof(int));
cudaMemcpy(hld, d_loadOffsets.data().get(), sizeof(int)*32, cudaMemcpyDeviceToHost);for(int i=0; i<32; i++)printf("%d, ", hld[i]);
printf("\n");mykernel<<<1,32>>>(d_loadOffsets.data().get(), true);cudaDeviceSynchronize();}

运行:

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

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

相关文章

PostgreSQL 的核心优势数据库优化与面试问题解析

Part0: PostgreSQL 的核心优势PostgreSQL 的核心优势可以总结为&#xff1a;它不仅仅是一个关系型数据库&#xff0c;更是一个功能极其强大、设计高度严谨、且具有无限扩展潜力的数据平台。其核心优势主要体现在以下几个方面&#xff1a;1. 高度符合 SQL 标准与可靠性&#xff…

牛客周赛 Round 109 (小红的直角三角形

小红的直角三角形思路&#xff1a;当作向量来求&#xff0c;向量乘为0&#xff1b;#include<bits/stdc.h> #define ll long long #define endl "\n" using namespace std; typedef pair<ll, ll> pll; int n; vector<pll> u; void solve() {int x,…

efcore 对象内容相同 提交MSSQL后数据库没有更新

一、efcore 对象内容相同 提交MSSQL后数据库没有更新在.net6EFcore6环境&#xff0c;遇到一个问题&#xff0c;当界面UI传给EF的对象值没有变化&#xff0c;它居然不去更新数据库。那我有2个EFcore实例都在更新数据库&#xff0c;值一直不变&#xff0c;程序就更新不到数据库中…

DockerComposeUI+cpolar:容器管理的远程可视化方案

前言&#xff1a;DockerComposeUI作为Docker容器的可视化管理工具&#xff0c;通过直观的Web界面实现容器的启动、暂停、终止等操作&#xff0c;支持镜像管理和Compose文件编辑。特别适合开发团队和运维人员&#xff0c;其图形化操作简化了复杂的命令行操作&#xff0c;状态面板…

H5 页面与 Web 页面的制作方法

1. H5 页面制作使用 HTML5、CSS3 和 JavaScript 技术&#xff1a;这些技术支持创建交互式和响应式 H5 页面。使用 H5 编辑器或框架&#xff1a;如 Adobe Dreamweaver、Brackets 或 Ionic&#xff0c;这些工具提供了预先构建的模板和组件&#xff0c;简化了开发过程。考虑移动设…

1.6、机器学习-决策树模型(金融实战)

决策树是一种基于特征分割的监督学习算法,通过递归分割数据空间来构建预测模型。 1.1、决策树模型基本原理 决策树思想的来源朴素,程序设计中的条件分支结构就是 if-then结构,最早的决策树就是利用这类结构分割数据的一种分类学习方法。为了更好理解决策树具体怎么分类的,…

常见中间件的同步算法、CAP 默认倾向及自定义支持情况

文章目录CAP 概念1、比较2、关键说明&#xff1a;CAP 概念 CAP 定理指分布式系统无法同时满足​​一致性&#xff08;C​​onsistency&#xff09;、​​可用性&#xff08;​​A​​vailability&#xff09;、​​分区容错性&#xff08;​​P​​artition Tolerance&#xf…

Spring 中处理 HTTP 请求参数注解全解析

在 Spring 框架的 Web 开发中&#xff0c;处理 HTTP 请求参数是一项基础且重要的工作。除了 PathVariable、RequestParam 和 Valid RequestBody 外&#xff0c;还有一些其他注解也用于此目的。本文将对这些注解进行全面的区分和解析&#xff0c;帮助开发者在实际项目中更准确地…

【代码随想录算法训练营——Day11】栈与队列——150.逆波兰表达式求值、239.滑动窗口最大值、347.前K个高频元素

LeetCode题目链接 https://leetcode.cn/problems/evaluate-reverse-polish-notation/ https://leetcode.cn/problems/sliding-window-maximum/ https://leetcode.cn/problems/top-k-frequent-elements/ 题解 150.逆波兰表达式求值、 不能用tokens[i] > "0" &&…

Docker 容器化部署核心实战——镜像仓库管理与容器多参数运行详解

摘要&#xff1a; 在当今云原生技术迅速发展的背景下&#xff0c;Docker 已成为应用容器化的首选工具。本文作为“Docker 容器化部署核心实战&#xff1a;从镜像仓库管理、容器多参数运行到 Nginx 服务配置与正反向代理原理解析”系列的第一篇&#xff0c;将深入探讨 Docker 镜…

ESP8266无法连接Jio路由器分析

我查了一下关于这些 Jio 路由器型号&#xff08;尤其是 JCOW414 和 JIDU6801&#xff09;的公开资料&#xff0c;下面是我能拿到的内容 对比这些型号可能带来的问题&#xff0c;以及对你排障的补充建议。 路由器型号 & 公开已知特性 型号已知 / 可查特性和 ESP8266 的潜在…

传智播客--MySQL

DAY01 MySQL入门 第一章 数据库介绍 1.1 什么是数据库 数据存储的仓库&#xff0c;本质上是一个文件系统&#xff0c;作用&#xff1a;方便管理数据的。 1.2 数据库管理系统 数据库管理系统&#xff08;DataBase Management System, DBMS&#xff09;&#xff1a;指一种操作和管…

[Dify] 实现“多知识库切换”功能的最佳实践

在构建知识驱动的问答系统或 AI 助手时,一个常见需求是:根据用户问题所属领域或上下文,切换使用不同的知识库(Knowledge Base, KB)进行检索。这样可以提升回答的准确性、减少无关内容干扰,在多业务线或多主题应用中尤其有用。 本文将介绍: 为什么要做知识库切换 Dify …

Jenkins运维之路(Jenkins流水线改造Day02-2-容器项目)

上篇文章中已经将绝大部分&#xff0c;Jenkins容器项目打包的相关功能改造完成了&#xff0c;这里在对构建部署后的告警类操作进行一些补充1.流水线告警1.1 安装钉钉插件image-202509151111086851.2 配置钉钉插件image-20250915111235865image-202509151115328291.3 Pipeline钉…

64_基于深度学习的蝴蝶种类检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)

目录 项目介绍&#x1f3af; 功能展示&#x1f31f; 一、环境安装&#x1f386; 环境配置说明&#x1f4d8; 安装指南说明&#x1f3a5; 环境安装教学视频 &#x1f31f; 二、数据集介绍&#x1f31f; 三、系统环境&#xff08;框架/依赖库&#xff09;说明&#x1f9f1; 系统环…

N1ctf-2025-PWN-ez_heap近队容器的礼仪

ez_heap 保护全开 程序逻辑&#xff1a; 读入0x30的字符串&#xff0c;进行字符串校验&#xff1a;以冒号为标志split&#xff0c;分成四份。最后输入字符串形如&#xff1a; xor 0x111111111111111 validate badmin:p64(xor)b:Junior:111111创建0x180的chunk存放note 结构体…

纵深防御实践:东方隐侠CI/CD安全体系构建全解析

前言:CI/CD安全的必要性 企业上云是近些年的潮流,但是风险如影随形。之前有家电商平台出了个大岔子——半夜自动发新版本的时候,因为流程里没做安全检查,直接导致系统故障,一天就损失了300多万。这还不算完,某银行测试人员通过未授权的自动发布流程把代码推到了生产环境…

2025年渗透测试面试题总结-71(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 2. 渗透测试流程 & 内网渗透经验 3. SQL注入报错利用 4. XSS利用&#xff08;反射型/DOM型&#xff0…

基于Echarts+HTML5可视化数据大屏展示-茶园大数据平台指挥舱

效果展示&#xff1a;代码结构&#xff1a;主要代码实现 index.html布局 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&quo…

华为网路设备学习-33(BGP协议 八)BGP路由 选路规则

一、目标与背景BGP路由特性&#xff1a;支持丰富的路径属性选路规则多样注&#xff1a;在BGP路由表中最优选&#xff0c;不一定是路由表中的最优选。有可能存在静态路由或者ospf路由等&#xff0c;其优先级高于BGP路由。二、选路规则概述从1到12&#xff0c;依次对比优先级。一…