PTX入门教程与实战

PTX入门教程

官方文档的目录结构

1 PTX指令

官方文档链接

1.1 指令形式

指令的操作数个数从0-4不等,其中d代表的是目的操作数,a,b,c是源操作数

@p   opcode;@p   opcode a;@p   opcode d, a;@p   opcode d, a, b;@p   opcode d, a, b, c;

2 编程模型

2.1 CTA

Cooperative thread arrays (CTAs) implement CUDA thread blocks and clusters implement CUDA thread block clusters.

CTA: A cooperative thread array, or CTA, is an array of threads that execute a kernel concurrently or in parallel.

CTA中的每一个线程都有唯一的一个标识符,tid(tid.x,tid.y,tid.z)
每一个CTA的结构由ntid(ntid.x,ntid.y,ntid.z)标识,其中ntid的每一维度指示了该维度上有多少个线程。
在具体的实施过程中,线程会被组织为warp的形式来执行。warp具体的大小和机器的有关,由WARP_SZ查询。

2.2 Cluster

Cluster is a group of CTAs that run concurrently or in parallel and can synchronize and communicate with each other via shared memory.

cluster只能在sm_90或者更高的设备上使用

可以完成cluster内的所有线程同步。此外,在一个cluster中,不同CTA上的线程能够通过共享内存进行同步。

2.3 Grid

在一个grid中,不同cluster中的线程是不能通信的

在grid中,每一个cluster有一个唯一的编号clusterid,一个grid中的cluster数量由ncluster进行标识。

在grid中,每一个CTA有唯一的编号ctaid,一个grid中的CTA数量由nctid进行标识
在这里插入图片描述
在这里插入图片描述

cluster是一个优化的结构,只有在sm_90及以上的设备上才会被使用。

3 内存结构

每一个线程有自己的本地内存,每一个CTA有共享内存,该共享内存被CTA中的每一个线程共享,同时也对同一集群中不同CTA的线程开放。所有的线程都有权利访问全局内存。

所有线程都可以访问其他状态空间:常量、参数、纹理和表面状态空间。常量和纹理内存是只读的;表面内存是可读写的。

在这里插入图片描述

4 机器模型

微处理器(multiprocessors)包括多个SP(scalar processor core)核,一个多线程指令单元(multithread instruction unit)和片上的共享内存。

每一个线程会被映射到一个SP上。SIMT(single-instruction,multiple-thread) unit 将多个线程组织为warp的形式。每次发出指令时,SIMT unit都会选择一个已准备好执行的 Warp,并向该 Warp 中的活动线程发出下一条指令。线程分支(thread divergence)仅仅只是在一个warp中发生,不同warp的执行路径是互相独立的。

SM中的能够并发的block数量是由SM的资源数量(寄存器、共享内存)以及一个block所需要的资源数量所共同决定的。

SIMD和SIMT的区别:SIMD将数据处理的宽度暴露给编程着,而SIMT提供了线程级别的并行和数据级别的并行。
A key difference is that SIMD vector organizations expose the SIMD width to the software, whereas SIMT instructions specify the execution and branching behavior of a single thread. In contrast with SIMD vector machines, SIMT enables programmers to write thread-level parallel code for independent, scalar threads, as well as data-parallel code for coordinated threads.

5 实战1

目的:寻找PTX是如何带来性能提升的

C代码如下

#include <stdio.h>
#include <stdint.h>
#include <mma.h>
#include <stdlib.h> 
#include <time.h>using namespace nvcuda;
#define Q_N653 16777153
#define NUMBER_ACONFI 3221491777__device__ int32_t montgomery_reduce_n653_cuda(int64_t a)
{int32_t t;t = (int32_t)a * NUMBER_ACONFI;//t是低32位置t = (a - (int64_t)t * Q_N653) >> 32;return t;
}/*2024-7-11
蒙格玛丽约减法的ptx版本:
*/
__device__ int32_t montgomery_reduce_n653_ptx(int64_t a){int64_t res;asm("{\n\t"".reg .s64 b;\n\t""mul.lo.s64 %0,%1,%2;\n\t""and.b64 %0,%0,0xffffffff;\n\t""mul.lo.s64 %0,%0,%3;\n\t""sub.s64 %0,%1,%0;\n\t""shr.s64 %0,%0,32;\n\t" //"add.s64 %0,%0,%3;\n\t"       "}":"=l"(res):"l"(a),"n"(NUMBER_ACONFI),"n"(Q_N653));return (int32_t)res;
}#define NUM_TESTS 1000__global__ void test_montgomery_reduce(int64_t* inputs, int32_t* outputs_ptx, int32_t* outputs_cuda, int num_tests) {int idx = threadIdx.x + blockIdx.x * blockDim.x;if (idx < num_tests) {int64_t a = inputs[idx];outputs_ptx[idx] = montgomery_reduce_n653_ptx(a);outputs_cuda[idx] = montgomery_reduce_n653_cuda(a);}
}
int test_montgomery() {int64_t* h_inputs = (int64_t*)malloc(NUM_TESTS * sizeof(int64_t));int32_t* h_outputs_ptx = (int32_t*)malloc(NUM_TESTS * sizeof(int32_t));int32_t* h_outputs_cuda = (int32_t*)malloc(NUM_TESTS * sizeof(int32_t));for (int i = 0; i < NUM_TESTS; ++i) {h_inputs[i] = rand() % UINT64_MAX;}int64_t* d_inputs;int32_t* d_outputs_ptx;int32_t* d_outputs_cuda;cudaMalloc(&d_inputs, NUM_TESTS * sizeof(int64_t));cudaMalloc(&d_outputs_ptx, NUM_TESTS * sizeof(int32_t));cudaMalloc(&d_outputs_cuda, NUM_TESTS * sizeof(int32_t));cudaMemcpy(d_inputs, h_inputs, NUM_TESTS * sizeof(int64_t), cudaMemcpyHostToDevice);test_montgomery_reduce<<<(NUM_TESTS + 255) / 256, 256>>>(d_inputs, d_outputs_ptx, d_outputs_cuda, NUM_TESTS);cudaMemcpy(h_outputs_ptx, d_outputs_ptx, NUM_TESTS * sizeof(int32_t), cudaMemcpyDeviceToHost);cudaMemcpy(h_outputs_cuda, d_outputs_cuda, NUM_TESTS * sizeof(int32_t), cudaMemcpyDeviceToHost);bool correct = true;for (int i = 0; i < NUM_TESTS; ++i) {if (h_outputs_ptx[i] != h_outputs_cuda[i]) {//if(h_outputs_cuda[i] - h_outputs_ptx[i] != Q_N653){printf("Mismatch at index %d: PTX = %d, CUDA = %d\n", i, h_outputs_ptx[i], h_outputs_cuda[i]);correct = false;//}}}if (correct) {printf("All tests passed!\n");}free(h_inputs);free(h_outputs_ptx);free(h_outputs_cuda);cudaFree(d_inputs);cudaFree(d_outputs_ptx);cudaFree(d_outputs_cuda);return 0;
}
/***********************************************************************/int main(){test_montgomery();
}

汇编结束后的ptx代码如下所示,在CUDA中,__device__通常是内联的,因此其不会单独对应一个ptx的函数,而是被嵌入到了调用__device__函数的__global__中。

//
// Generated by NVIDIA NVVM Compiler
//
// Compiler Build ID: CL-30672275
// Cuda compilation tools, release 11.5, V11.5.119
// Based on NVVM 7.0.1
//.version 7.5
.target sm_86
.address_size 64// .globl	_Z22test_montgomery_reducePlPiS0_i.visible .entry _Z22test_montgomery_reducePlPiS0_i(.param .u64 _Z22test_montgomery_reducePlPiS0_i_param_0,.param .u64 _Z22test_montgomery_reducePlPiS0_i_param_1,.param .u64 _Z22test_montgomery_reducePlPiS0_i_param_2,.param .u32 _Z22test_montgomery_reducePlPiS0_i_param_3
)
{.reg .pred 	%p<2>;.reg .b32 	%r<6>;.reg .b64 	%rd<19>;ld.param.u64 	%rd1, [_Z22test_montgomery_reducePlPiS0_i_param_0];ld.param.u64 	%rd2, [_Z22test_montgomery_reducePlPiS0_i_param_1];ld.param.u64 	%rd3, [_Z22test_montgomery_reducePlPiS0_i_param_2];ld.param.u32 	%r2, [_Z22test_montgomery_reducePlPiS0_i_param_3];mov.u32 	%r3, %tid.x;mov.u32 	%r4, %ntid.x;mov.u32 	%r5, %ctaid.x;mad.lo.s32 	%r1, %r5, %r4, %r3;setp.ge.s32 	%p1, %r1, %r2;@%p1 bra 	$L__BB0_2;cvta.to.global.u64 	%rd6, %rd1;mul.wide.s32 	%rd7, %r1, 8;add.s64 	%rd8, %rd6, %rd7;ld.global.u64 	%rd5, [%rd8];// begin inline asm{.reg .s64 b;mul.lo.s64 %rd4,%rd5,3221491777;and.b64 %rd4,%rd4,0xffffffff;mul.lo.s64 %rd4,%rd4,16777153;sub.s64 %rd4,%rd5,%rd4;shr.s64 %rd4,%rd4,32;}// end inline asmcvta.to.global.u64 	%rd9, %rd2;mul.wide.s32 	%rd10, %r1, 4;add.s64 	%rd11, %rd9, %rd10;st.global.u32 	[%rd11], %rd4;mul.lo.s64 	%rd12, %rd5, -4610542247161626624;shr.s64 	%rd13, %rd12, 32;mul.lo.s64 	%rd14, %rd13, -16777153;add.s64 	%rd15, %rd14, %rd5;shr.u64 	%rd16, %rd15, 32;cvta.to.global.u64 	%rd17, %rd3;add.s64 	%rd18, %rd17, %rd10;st.global.u32 	[%rd18], %rd16;$L__BB0_2:ret;}

下面,我们将C代码实现和PTX实现一一对应,并详细地解析ptx代码

5.1 函数声明与下标计算

CUDA 在处理 64 位架构(例如 sm_86)的 GPU 时,所有指针类型(无论是 int32_t* 还是 int64_t*)都被视为 64 位宽的地址。因此,在 PTX 代码中,指针类型的参数被表示为 u64,即 64 位无符号整数。这是为了与硬件的地址宽度对齐。
因此int32_t类型的指针是u64类型的。
指令关键词 .param代表的是参数,其对应着一种状态空间。由于test_montgomery_reduce是一个global函数,因此.param是read-only的。

test_montgomery_reduce(int64_t* inputs, int32_t* outputs_ptx, int32_t* outputs_cuda, int num_tests).visible .entry _Z22test_montgomery_reducePlPiS0_i(.param .u64 _Z22test_montgomery_reducePlPiS0_i_param_0,.param .u64 _Z22test_montgomery_reducePlPiS0_i_param_1,.param .u64 _Z22test_montgomery_reducePlPiS0_i_param_2,.param .u32 _Z22test_montgomery_reducePlPiS0_i_param_3
)

mad指令用于将两个数相乘后(选取高位或者低位),再加上另一个数。mad.lo.s32 %r1, %r5, %r4, %r3; 实现了int idx = threadIdx.x + blockIdx.x * blockDim.x;

int idx = threadIdx.x + blockIdx.x * blockDim.x;if (idx < num_tests) {mov.u32 	%r3, %tid.x;
mov.u32 	%r4, %ntid.x;
mov.u32 	%r5, %ctaid.x;
mad.lo.s32 	%r1, %r5, %r4, %r3;
setp.ge.s32 	%p1, %r1, %r2;
@%p1 bra 	$L__BB0_2;

setp指令通过应用布尔运算符比较两个值并将结果与​​另一个谓词值相结合。

bra用于控制分支语句的执行,@指令用于预测执行,因此@%p1 bra $L__BB0_2; 代表根据%p1判断是否执行相应的L__BB0_2语句。

5.2 montgomery_reduce_n653_ptx

outputs_ptx[idx] = montgomery_reduce_n653_ptx(a);__device__ int32_t montgomery_reduce_n653_ptx(int64_t a){int64_t res;asm("{\n\t"//".reg .s64 b;\n\t" //这句话可以直接删除了"mul.lo.s64 %0,%1,%2;\n\t""and.b64 %0,%0,0xffffffff;\n\t""mul.lo.s64 %0,%0,%3;\n\t""sub.s64 %0,%1,%0;\n\t""shr.s64 %0,%0,32;\n\t" //"add.s64 %0,%0,%3;\n\t"       "}":"=l"(res):"l"(a),"n"(NUMBER_ACONFI),"n"(Q_N653));return (int32_t)res;
}cvta.to.global.u64 	%rd6, %rd1; //%rd6代表的是inputs的地址mul.wide.s32 	%rd7, %r1, 8; //%r1代表的是idx的值,这里把地址乘上一个8add.s64 	%rd8, %rd6, %rd7;//加上inputs最初的地址ld.global.u64 	%rd5, [%rd8]; //%rd5中存放的即为a的值// begin inline asm{.reg .s64 b; //声明一个寄存器bmul.lo.s64 %rd4,%rd5,3221491777; //%rd4即为resand.b64 %rd4,%rd4,0xffffffff;mul.lo.s64 %rd4,%rd4,16777153;sub.s64 %rd4,%rd5,%rd4;shr.s64 %rd4,%rd4,32;}// end inline asmcvta.to.global.u64 	%rd9, %rd2; //把outputs_ptx指针重新读一下mul.wide.s32 	%rd10, %r1, 4; //因为outputs_ptx是32bit长的数组,又以idx作为下标,因此这里需要乘上4add.s64 	%rd11, %rd9, %rd10; //%rd11指向了outputs_ptxst.global.u32 	[%rd11], %rd4;//把计算得到的%rd4的值,放到outputs_ptx[idx]中

cvta指令

cvta.to.global.u64 %rd6, %rd1;代表的是地址转化指令,将 %rd1 寄存器中的地址转换为全局地址空间的 64 位地址,并将转换后的地址存储在 %rd6 寄存器中。结合C代码,该操作将inputs的指针放到寄存器%rd6里面。下面,将idx乘上8再加上inputs的地址,即得到了真实的地址%rd8,此时,将值使用ld指令加载到%rd5寄存器中(即%rd5中存储的即为c代码中的a的值)。//begin inline asm和//end inline asm给出了montgomery_reduce_n653_ptx中所内嵌的汇编代码。在完成计算后,使用类似的方法,计算outputs_ptx对应地址的指针值,并将计算结果%rd4存储进去。

5.3 montgomery_reduce_n653_cuda

outputs_cuda[idx] = montgomery_reduce_n653_cuda(a);__device__ int32_t montgomery_reduce_n653_cuda(int64_t a)
{int32_t t;t = (int32_t)a * NUMBER_ACONFI;//t是低32位t = (a - (int64_t)t * Q_N653) >> 32;return t;
}mul.lo.s64 	%rd12, %rd5, -4610542247161626624; //%rd5为a的值shr.s64 	%rd13, %rd12, 32;mul.lo.s64 	%rd14, %rd13, -16777153;add.s64 	%rd15, %rd14, %rd5;shr.u64 	%rd16, %rd15, 32;cvta.to.global.u64 	%rd17, %rd3;add.s64 	%rd18, %rd17, %rd10;st.global.u32 	[%rd18], %rd16;

强制类型转化会改变数据在内存中的存储的结构(float和整型之间),但是如果都是整型之间,那么进行的即为截断操作,例如,下面代码输出123456789abcdef0 9abcdef。

int main(){//test_montgomery();int64_t a = 0x123456789ABCDEF0;int32_t b = (int32_t)a;printf("%llx %x",a,b);
}

-4610542247161626624为int64_t类型的数,其二进制表示为0xc004104100000000,NUMBER_ACONFI=3221491777=0xc0041041,因此-4610542247161626624 = NUMBER_ACONFI << 32。因此 mul.lo.s64 %rd12, %rd5, -4610542247161626624; //%rd5为a的值
shr.s64 %rd13, %rd12, 32;用于计算t = (int32_t)a * NUMBER_ACONFI;//t是低32位
需要注意的是int32_t首先作用在了a上,从而使得运算本质上是在int32_t上进行的,为了能够在int64_t上模拟在int32_t上运算得到的溢出和截断等,需要先左移32位,这步操作,一方面完成了a的截断操作,另一方面也实现了运算的模拟。

6 实战2

本文借助一个实例,展示使用ptx代码带来性能提升的原因

注意,在global函数中加入下述语句,能够避免函数被编译器优化,从而使得能够显示出编译后正确的ptx代码

asm volatile ("" : "+r"(t));
#define DILITHIUM_Q 8380417
#define QINV 123__global__ void gpu_montgomery_multiply_ptx(int32_t x, int32_t y) {//printf("ptx\n");int32_t t;asm volatile("{\n\t"" .reg .s32 a_hi, a_lo;\n\t"" mul.hi.s32 a_hi, %1, %2;\n\t"" mul.lo.s32 a_lo, %1, %2;\n\t"" mul.lo.s32 %0, a_lo, %4;\n\t"" mul.hi.s32 %0, %0, %3;\n\t"" sub.s32 %0, a_hi, %0;\n\t""}": "=r"(t): "r"(x), "r"(y), "r"(DILITHIUM_Q), "r"(QINV));asm volatile ("" : "+r"(t));//防止该段代码被编译器优化掉
}__global__ void gpu_montgomery_multiply_cuda(int32_t x, int32_t y) {//printf("cuda\n");int32_t t;int64_t a = (int64_t) x * y;t = (int64_t) (int32_t) a * QINV;t = (a - (int64_t) t * DILITHIUM_Q) >> 32;asm volatile ("" : "+r"(t)); //防止该段代码被编译器优化掉}int test_for_dili(){printf("test for dili\n");gpu_montgomery_multiply_ptx<<<1,1>>>(1002,1213);gpu_montgomery_multiply_cuda<<<1,1>>>(1002,1213);cudaDeviceSynchronize();
}/***********************************************************************/int main(){//test_montgomery();//single_1_kernel();/*int64_t a = 0x123456789ABCDEF0;int32_t b = (int32_t)a;printf("%llx %x",a,b);*/test_for_dili();
}

其对应的ptx代码为

//
// Generated by NVIDIA NVVM Compiler
//
// Compiler Build ID: CL-30672275
// Cuda compilation tools, release 11.5, V11.5.119
// Based on NVVM 7.0.1
//.version 7.5
.target sm_86
.address_size 64// .globl	_Z27gpu_montgomery_multiply_ptxii.visible .entry _Z27gpu_montgomery_multiply_ptxii(.param .u32 _Z27gpu_montgomery_multiply_ptxii_param_0,.param .u32 _Z27gpu_montgomery_multiply_ptxii_param_1
)
{.reg .b32 	%r<8>;ld.param.u32 	%r2, [_Z27gpu_montgomery_multiply_ptxii_param_0];ld.param.u32 	%r3, [_Z27gpu_montgomery_multiply_ptxii_param_1];mov.u32 	%r4, 8380417; //dili_qmov.u32 	%r5, 123; //qinv// begin inline asm{.reg .s32 a_hi, a_lo;mul.hi.s32 a_hi, %r2, %r3; //mul a_hi,x,ymul.lo.s32 a_lo, %r2, %r3; //mul a_lo,x,ymul.lo.s32 %r6, a_lo, %r5; //mul t,a_lo,qinvmul.hi.s32 %r6, %r6, %r4; //mul t,t,dili_qsub.s32 %r6, a_hi, %r6; // mul t,a_h,t}// end inline asm// begin inline asm// end inline asmret;}// .globl	_Z28gpu_montgomery_multiply_cudaii
.visible .entry _Z28gpu_montgomery_multiply_cudaii(.param .u32 _Z28gpu_montgomery_multiply_cudaii_param_0,.param .u32 _Z28gpu_montgomery_multiply_cudaii_param_1
)
{.reg .b32 	%r<5>;.reg .b64 	%rd<7>;ld.param.u32 	%r3, [_Z28gpu_montgomery_multiply_cudaii_param_0];//xld.param.u32 	%r4, [_Z28gpu_montgomery_multiply_cudaii_param_1];//ymul.wide.s32 	%rd1, %r4, %r3;//rd1 = x*y amul.lo.s64 	%rd2, %rd1, 528280977408; shr.s64 	%rd3, %rd2, 32;mul.lo.s64 	%rd4, %rd3, -8380417;add.s64 	%rd5, %rd4, %rd1;shr.u64 	%rd6, %rd5, 32;cvt.u32.u64 	%r1, %rd6;// begin inline asm// end inline asmret;}

6.1 gpu_montgomery_multiply_ptx

%r4 = DILITHIUM_Q
%r5 = QINV
_Z27gpu_montgomery_multiply_ptxii_param_0是指向x的指针,因此[_Z27gpu_montgomery_multiply_ptxii_param_0] = x
内联代码部分同asm中的一样

6.2 gpu_montgomery_multiply_cuda

t = (int64_t) (int32_t) a * QINV;被转化为mul.lo.s64 %rd2, %rd1, 528280977408; shr.s64 %rd3, %rd2, 32; 其正确性如下图所示:
528280977408的二进制表示为7b00000000,123的二进制表示为7b,因此528280977408 = 123 << 32。
在这里插入图片描述

强制类型转化测试:输出结果为-80。下面对该结果进行检验。首先(int8_t)a = 0x10 (int16_t)(int8_t)a = 0x0010 = 16 (int16_t)(int8_t)a*123 = 1968 = 0x7b,其表示的补码为-80。因此,由长到短的转化,为截断;由短到长的转化,为保持符号,增添0的数量。

int16_t a = 0x1010;
int8_t t = (int16_t) (int8_t)a * 123;
printf("%d",t);

6.3 两种方法比较

使用nsight comput观察实验结果,ptx的代码用了1.47us,而cuda的代码用了1.44us
在这里插入图片描述

列1ptxcuda
寄存器数量7个32位寄存器3个32位,7个64位
指令数量56
运行时间1.47us1.44us

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

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

相关文章

FullCalendar的使用,react日历组件

1.下载 yarn add fullcalendar/core fullcalendar/react fullcalendar/daygrid 2.运行 import React from react; import FullCalendar from "fullcalendar/react"; import dayGridPlugin from "fullcalendar/daygrid";const ExperimentalSchedule () …

13--memcache与redis

前言&#xff1a;数据库读取速度较慢一直是无法解决的问题&#xff0c;大型网站应对的方式主要是使用缓存服务器来缓解这种情况&#xff0c;减少数据库访问次数&#xff0c;以提高动态Web等应用的速度、提高可扩展性。 1、简介 Memcached/redis是高性能的分布式内存缓存服务器…

paddlepaddle2.6,paddleorc2.8,cuda12,cudnn,nccl,python10环境

1.安装英伟达显卡驱动 首先需要到NAVIDIA官网去查自己的电脑是不是支持GPU运算。 网址是&#xff1a;CUDA GPUs | NVIDIA Developer。打开后的界面大致如下&#xff0c;只要里边有对应的型号就可以用GPU运算&#xff0c;并且每一款设备都列出来相关的计算能力&#xff08;Compu…

C语言 | Leetcode C语言题解之第230题二叉搜索树中第K小的元素

题目&#xff1a; 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/int search_num(struct TreeNode* root, int k, int *result, int num) {if(num k 1){retu…

计算机的错误计算(二十九)

摘要 &#xff08;1&#xff09;讨论近似值的错误数字个数。有时&#xff0c;遇到数字9或0, 不太好确认近似值的错误数字个数。&#xff08;2&#xff09;并进一步解释确认计算机的错误计算&#xff08;二十八&#xff09;中一个函数值的错误数字个数。 理论上&#xff0c;我…

Java数据结构-二叉树

树型结构 概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成的一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上叶朝下的。 树具有以下特点&#xff1a; 有一个特殊结点&…

javaweb个人主页设计(html+css+js)

目录 1 前言和要求 1.1 前言 1.2 设计要求 2 预览 2.1 主页页面 2.2 个人简介 2.3 个人爱好 2.4 个人成绩有代码&#xff0c;但是图片已省略&#xff0c;可以根据自己情况添加 2.5 收藏夹 3 代码实现 3.1 主页 3.2 个人简介 3.3 个人爱好 3.4 个人成绩&#xff…

CSS技巧专栏:一日一例 1.纯CSS实现 会讨好的热情按钮 特效

题外话: 从今天开始,我准备开设一个新的专栏,专门写 使用CSS实现各种酷炫按钮的方法,本专栏目前准备写40篇左右,大概会完成如下按钮效果: 今天,我来介绍第一个按钮的实现方法:会讨好的热情按钮。为什么我给它起这样的名字呢?你看它像不像一个不停摇尾巴的小黄?当你鼠…

SvANet:微小医学目标分割网络,增强早期疾病检测

SvANet&#xff1a;微小医学目标分割网络&#xff0c;增强早期疾病检测 提出背景前人工作医学对象分割微小医学对象分割注意力机制 SvANet 结构图SvANet 解法拆解解法逻辑链 论文&#xff1a;SvANet: A Scale-variant Attention-based Network for Small Medical Object Segmen…

中职网络安全B模块渗透测试server2003

通过本地PC中渗透测试平台Kali对服务器场景Windows进⾏系统服务及版本扫描渗透测 试&#xff0c;并将该操作显示结果中Telnet服务对应的端⼝号作为FLAG提交 使用nmap扫描发现目标靶机开放端口232疑似telnet直接进行连接测试成功 Flag&#xff1a;232 通过本地PC中渗透测试平台…

【Lora模型推荐】Stable Diffusion创作具有玉石翡翠质感的图标设计

站长素材AI教程是站长之家旗下AI绘图教程平台 海量AI免费教程&#xff0c;每日更新干货内容 想要深入学习更多AI绘图教程&#xff0c;请访问站长素材AI教程网&#xff1a; AI教程_深度学习入门指南 - 站长素材 (chinaz.com) logo版权归各公司所有&#xff01;本笔记仅供AIGC…

基于stm32+小程序开发智能家居门禁系统-硬件-软件实现

视频演示&#xff1a; 基于stm32智能家居门禁系统小程序开发项目 视频还有添加删除卡号&#xff0c;添加删除指纹&#xff0c;关闭继电器电源等没有演示。 代码Git&#xff1a; https://github.com/Abear6666/stm32lock 总体功能&#xff1a; 本门禁系统主要解锁功能分别为卡…

android13 设置左右分屏修改为单屏幕,应用分屏改为单屏

总纲 android13 rom 开发总纲说明 目录 1.前言 2.系统设置实现分析 3. 设置修改 4.编译与验证 5.猜测 6.彩蛋 1.前言 android13中,系统设置变成,左边是一级菜单,右侧是二级菜单, 这样跟我们以前android7/8/9的布局是不一样的,我们需要将它修改为一级菜单,点进去…

mysql 5.7.44 32位 zip安装

前言 因为研究别人代码&#xff0c;他使用了5.7的 32位 mysql &#xff0c;同时最新的 8.4 64位 mysql 不能用官方lib连接。所以安装这个版本使用&#xff0c;期间有些坑&#xff0c;在这里记录一下。 下载路径 mysql官方路径&#xff1a;https://downloads.mysql.com/archi…

Unity如何查找两个transform最近的公共parent

查找两个子对象最近的父对象 一、问题背景二、解决方案思路核心算法代码 三、总结 一、问题背景 最近看到个关于Unity的问题&#xff1a;在Hierarchy面板中的游戏对象&#xff0c;给定两个子物体transform对象&#xff0c;如何查找这两个transform最近的公共父级parent。感觉挺…

从 ArcMap 迁移到 ArcGIS Pro

许多 ArcMap 用户正在因 ArcGIS Pro 所具有的现代 GIS 桌面工作流优势而向其迁移。 ArcGIS Pro 与其余 ArcGIS 平台紧密集成&#xff0c;使您可以更有效地共享和使用内容。 它还将 2D 和 3D 组合到一个应用程序中&#xff0c;使您可以在同一工程中使用多个地图和多个布局。 Arc…

Linux桌面溯源

X窗口系统(X Window System) Linux起源于X窗口系统&#xff08;X Window System&#xff09;&#xff0c;亦即常说的X11&#xff0c;因其版本止于11之故。 X窗口系统&#xff08;X Window System&#xff0c;也常称为X11或X&#xff09;是一种以位图方式显示的软件窗口系统。…

保姆级教你如何在大学期间获得自己的一项个人软著(1)

注册与实名认证 1. 注册与实名认证 已注册和实名认证 or 直接使用组织账号 进行软著申请的&#xff0c;可以跳过这部分 1.1 注册 登录中国版权保护中心 中国版权登记业务平台 点击右上角的用户中心 点击立即注册 选择个人身份进行注册 返回登记页面中国版权登记业务平台…

【教程】Hexo 部署到 Github Page 后,自定义域名失效的问题

目录 前言&问题描述解决方案细节 前言&问题描述 近期给 Github Page 上托管的静态网站映射了自定义域名&#xff08;aiproducthome.top&#xff09;&#xff0c;之后发现每次更新并部署 hexo 到 Github Page &#xff08;hexo d&#xff09;后就会出现自定义域名失效的…

FDL与Kettle功能对比分析之定时任务DDL

开发者在进行数据处理任务时&#xff0c; 一旦源数据库的表结构发生变化&#xff0c;而目标数据库没有及时进行同步&#xff0c;就会导致任务执行失败。DDL同步就是用来解决这一问题&#xff0c;它会自动识别源表结构变化&#xff0c;并及时更新到目标数据库中&#xff0c;保障…