当前位置: 首页 > news >正文

CUDA中的底层驱动API

在这里插入图片描述

文章目录

  • CUDA底层驱动API
    • 1. Context
    • 2. Module
    • 3. Kernel Execution
    • 4. Interoperability between Runtime and Driver APIs
    • 5. Driver Entry Point Access
      • 5.1. Introduction
      • 5.2. Driver Function Typedefs
      • 5.3. Driver Function Retrieval
        • 5.3.1. Using the driver API
        • 5.3.2. Using the runtime API
        • 5.3.3. Retrieve per-thread default stream versions
        • 5.3.4. Access new CUDA features

CUDA底层驱动API

本附录假定您了解 CUDA 运行时中描述的概念。

驱动程序 API 在 cuda 动态库(cuda.dllcuda.so)中实现,该库在安装设备驱动程序期间复制到系统上。 它的所有入口点都以 cu 为前缀。

它是一个基于句柄的命令式 API:大多数对象都由不透明的句柄引用,这些句柄可以指定给函数来操作对象。

驱动程序 API 中可用的对象汇总在下表中。

Table 16. Objects Available in the CUDA Driver API
ObjectHandleDescription
DeviceCUdeviceCUDA-enabled device
ContextCUcontextRoughly equivalent to a CPU process
ModuleCUmoduleRoughly equivalent to a dynamic library
FunctionCUfunctionKernel
Heap memoryCUdeviceptrPointer to device memory
CUDA arrayCUarrayOpaque container for one-dimensional or two-dimensional data on the device, readable via texture or surface references
Texture referenceCUtexrefObject that describes how to interpret texture memory data
Surface referenceCUsurfrefObject that describes how to read or write CUDA arrays
StreamCUstreamObject that describes a CUDA stream
EventCUeventObject that describes a CUDA event

在调用驱动程序 API 的任何函数之前,必须使用 cuInit() 初始化驱动程序 API。 然后必须创建一个附加到特定设备的 CUDA 上下文,并使其成为当前调用主机线程,如上下文中所述。

在 CUDA 上下文中,内核作为 PTX 或二进制对象由主机代码显式加载,如模块中所述。 因此,用 C++ 编写的内核必须单独编译成 PTX 或二进制对象。 内核使用 API 入口点启动,如内核执行中所述。

任何想要在未来设备架构上运行的应用程序都必须加载 PTX,而不是二进制代码。 这是因为二进制代码是特定于体系结构的,因此与未来的体系结构不兼容,而 PTX 代码在加载时由设备驱动程序编译为二进制代码。

以下是使用驱动程序 API 编写的内核示例的主机代码:

int main()
{int N = ...;size_t size = N * sizeof(float);// Allocate input vectors h_A and h_B in host memoryfloat* h_A = (float*)malloc(size);float* h_B = (float*)malloc(size);// Initialize input vectors...// InitializecuInit(0);// Get number of devices supporting CUDAint deviceCount = 0;cuDeviceGetCount(&deviceCount);if (deviceCount == 0) {printf("There is no device supporting CUDA.\n");exit (0);}// Get handle for device 0CUdevice cuDevice;cuDeviceGet(&cuDevice, 0);// Create contextCUcontext cuContext;cuCtxCreate(&cuContext, 0, cuDevice);// Create module from binary fileCUmodule cuModule;cuModuleLoad(&cuModule, "VecAdd.ptx");// Allocate vectors in device memoryCUdeviceptr d_A;cuMemAlloc(&d_A, size);CUdeviceptr d_B;cuMemAlloc(&d_B, size);CUdeviceptr d_C;cuMemAlloc(&d_C, size);// Copy vectors from host memory to device memorycuMemcpyHtoD(d_A, h_A, size);cuMemcpyHtoD(d_B, h_B, size);// Get function handle from moduleCUfunction vecAdd;cuModuleGetFunction(&vecAdd, cuModule, "VecAdd");// Invoke kernelint threadsPerBlock = 256;int blocksPerGrid =(N + threadsPerBlock - 1) / threadsPerBlock;void* args[] = { &d_A, &d_B, &d_C, &N };cuLaunchKernel(vecAdd,blocksPerGrid, 1, 1, threadsPerBlock, 1, 1,0, 0, args, 0);...
}

完整的代码可以在 vectorAddDrv CUDA 示例中找到。

1. Context

CUDA 上下文类似于 CPU 进程。驱动 API 中执行的所有资源和操作都封装在 CUDA 上下文中,当上下文被销毁时,系统会自动清理这些资源。除了模块和纹理或表面引用等对象外,每个上下文都有自己独特的地址空间。因此,来自不同上下文的 CUdeviceptr 值引用不同的内存位置。

主机线程一次可能只有一个设备上下文当前。当使用 cuCtxCreate() 创建上下文时,它对调用主机线程是当前的。如果有效上下文不是线程当前的,则在上下文中操作的 CUDA 函数(大多数不涉及设备枚举或上下文管理的函数)将返回 CUDA_ERROR_INVALID_CONTEXT

每个主机线程都有一堆当前上下文。 cuCtxCreate() 将新上下文推送到堆栈顶部。可以调用 cuCtxPopCurrent() 将上下文与主机线程分离。然后上下文是“浮动的”,并且可以作为任何主机线程的当前上下文推送。 cuCtxPopCurrent() 还会恢复先前的当前上下文(如果有)。

还为每个上下文维护使用计数。 cuCtxCreate() 创建使用计数为 1 的上下文。cuCtxAttach() 增加使用计数,而 cuCtxDetach() 减少使用计数。当调用 cuCtxDetach()cuCtxDestroy() 时使用计数变为 0,上下文将被销毁。

驱动程序 API 可与运行时互操作,并且可以通过 cuDevicePrimaryCtxRetain() 从驱动程序 API 访问由运行时管理的主上下文(参见初始化)。

使用计数有助于在相同上下文中运行的第三方编写的代码之间的互操作性。例如,如果加载三个库以使用相同的上下文,则每个库将调用 cuCtxAttach() 来增加使用计数,并在库使用上下文完成时调用 cuCtxDetach() 来减少使用计数。对于大多数库,预计应用程序会在加载或初始化库之前创建上下文;这样,应用程序可以使用自己的启发式方法创建上下文,并且库只需对传递给它的上下文进行操作。希望创建自己的上下文的库(可能会或可能没有创建自己的上下文的 API 客户端不知道)将使用 cuCtxPushCurrent()cuCtxPopCurrent(),如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKzKH14l-1676116626089)(library-context-management.png)]

2. Module

模块是设备代码和数据的动态可加载包,类似于 Windows 中的 DLL,由 nvcc 输出(请参阅使用 NVCC 编译)。 所有符号的名称,包括函数、全局变量和纹理或表面引用,都在模块范围内维护,以便独立第三方编写的模块可以在相同的 CUDA 上下文中互操作。

此代码示例加载一个模块并检索某个内核的句柄:

CUmodule cuModule;
cuModuleLoad(&cuModule, "myModule.ptx");
CUfunction myKernel;
cuModuleGetFunction(&myKernel, cuModule, "MyKernel");

此代码示例从 PTX 代码编译和加载新模块并解析编译错误:

#define BUFFER_SIZE 8192
CUmodule cuModule;
CUjit_option options[3];
void* values[3];
char* PTXCode = "some PTX code";
char error_log[BUFFER_SIZE];
int err;
options[0] = CU_JIT_ERROR_LOG_BUFFER;
values[0]  = (void*)error_log;
options[1] = CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES;
values[1]  = (void*)BUFFER_SIZE;
options[2] = CU_JIT_TARGET_FROM_CUCONTEXT;
values[2]  = 0;
err = cuModuleLoadDataEx(&cuModule, PTXCode, 3, options, values);
if (err != CUDA_SUCCESS)printf("Link error:\n%s\n", error_log);

此代码示例从多个 PTX 代码编译、链接和加载新模块,并解析链接和编译错误:

#define BUFFER_SIZE 8192
CUmodule cuModule;
CUjit_option options[6];
void* values[6];
float walltime;
char error_log[BUFFER_SIZE], info_log[BUFFER_SIZE];
char* PTXCode0 = "some PTX code";
char* PTXCode1 = "some other PTX code";
CUlinkState linkState;
int err;
void* cubin;
size_t cubinSize;
options[0] = CU_JIT_WALL_TIME;
values[0] = (void*)&walltime;
options[1] = CU_JIT_INFO_LOG_BUFFER;
values[1] = (void*)info_log;
options[2] = CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES;
values[2] = (void*)BUFFER_SIZE;
options[3] = CU_JIT_ERROR_LOG_BUFFER;
values[3] = (void*)error_log;
options[4] = CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES;
values[4] = (void*)BUFFER_SIZE;
options[5] = CU_JIT_LOG_VERBOSE;
values[5] = (void*)1;
cuLinkCreate(6, options, values, &linkState);
err = cuLinkAddData(linkState, CU_JIT_INPUT_PTX,(void*)PTXCode0, strlen(PTXCode0) + 1, 0, 0, 0, 0);
if (err != CUDA_SUCCESS)printf("Link error:\n%s\n", error_log);
err = cuLinkAddData(linkState, CU_JIT_INPUT_PTX,(void*)PTXCode1, strlen(PTXCode1) + 1, 0, 0, 0, 0);
if (err != CUDA_SUCCESS)printf("Link error:\n%s\n", error_log);
cuLinkComplete(linkState, &cubin, &cubinSize);
printf("Link completed in %fms. Linker Output:\n%s\n", walltime, info_log);
cuModuleLoadData(cuModule, cubin);
cuLinkDestroy(linkState);

完整的代码可以在 ptxjit CUDA 示例中找到。

3. Kernel Execution

cuLaunchKernel() 启动具有给定执行配置的内核。

参数可以作为指针数组(在 cuLaunchKernel() 的最后一个参数旁边)传递,其中第 n 个指针对应于第 n 个参数并指向从中复制参数的内存区域,或者作为额外选项之一( cuLaunchKernel()) 的最后一个参数。

当参数作为额外选项(CU_LAUNCH_PARAM_BUFFER_POINTER 选项)传递时,它们作为指向单个缓冲区的指针传递,在该缓冲区中,通过匹配设备代码中每个参数类型的对齐要求,参数被假定为彼此正确偏移。

表 4 列出了内置向量类型的设备代码中的对齐要求。对于所有其他基本类型,设备代码中的对齐要求与主机代码中的对齐要求相匹配,因此可以使用 __alignof() 获得。唯一的例外是当宿主编译器在一个字边界而不是两个字边界上对齐 double 和 long long(在 64 位系统上为 long)(例如,使用 gcc 的编译标志 -mno-align-double ) 因为在设备代码中,这些类型总是在两个字的边界上对齐。

CUdeviceptr是一个整数,但是代表一个指针,所以它的对齐要求是__alignof(void*)

以下代码示例使用宏 (ALIGN_UP()) 调整每个参数的偏移量以满足其对齐要求,并使用另一个宏 (ADD_TO_PARAM_BUFFER()) 将每个参数添加到传递给 CU_LAUNCH_PARAM_BUFFER_POINTER 选项的参数缓冲区。

#define ALIGN_UP(offset, alignment) \(offset) = ((offset) + (alignment) - 1) & ~((alignment) - 1)char paramBuffer[1024];
size_t paramBufferSize = 0;#define ADD_TO_PARAM_BUFFER(value, alignment)                   \do {                                                        \paramBufferSize = ALIGN_UP(paramBufferSize, alignment); \memcpy(paramBuffer + paramBufferSize,                   \&(value), sizeof(value));                        \paramBufferSize += sizeof(value);                       \} while (0)int i;
ADD_TO_PARAM_BUFFER(i, __alignof(i));
float4 f4;
ADD_TO_PARAM_BUFFER(f4, 16); // float4's alignment is 16
char c;
ADD_TO_PARAM_BUFFER(c, __alignof(c));
float f;
ADD_TO_PARAM_BUFFER(f, __alignof(f));
CUdeviceptr devPtr;
ADD_TO_PARAM_BUFFER(devPtr, __alignof(devPtr));
float2 f2;
ADD_TO_PARAM_BUFFER(f2, 8); // float2's alignment is 8void* extra[] = {CU_LAUNCH_PARAM_BUFFER_POINTER, paramBuffer,CU_LAUNCH_PARAM_BUFFER_SIZE,    &paramBufferSize,CU_LAUNCH_PARAM_END
};
cuLaunchKernel(cuFunction,blockWidth, blockHeight, blockDepth,gridWidth, gridHeight, gridDepth,0, 0, 0, extra);

结构的对齐要求等于其字段的对齐要求的最大值。 因此,包含内置向量类型 CUdeviceptr 或未对齐的 doublelong long 的结构的对齐要求可能在设备代码和主机代码之间有所不同。 这种结构也可以用不同的方式填充。 例如,以下结构在主机代码中根本不填充,但在设备代码中填充了字段 f 之后的 12 个字节,因为字段 f4 的对齐要求是 16。

typedef struct {float  f;float4 f4;
} myStruct;

4. Interoperability between Runtime and Driver APIs

应用程序可以将运行时 API 代码与驱动程序 API 代码混合。

如果通过驱动程序 API 创建上下文并使其成为当前上下文,则后续运行时调用将获取此上下文,而不是创建新上下文。

如果运行时已初始化(如 CUDA 运行时中提到的那样),cuCtxGetCurrent() 可用于检索在初始化期间创建的上下文。 后续驱动程序 API 调用可以使用此上下文。

从运行时隐式创建的上下文称为主上下文(请参阅初始化)。 它可以通过具有主要上下文管理功能的驱动程序 API 进行管理。

可以使用任一 API 分配和释放设备内存。 CUdeviceptr 可以转换为常规指针,反之亦然:

CUdeviceptr devPtr;
float* d_data;// Allocation using driver API
cuMemAlloc(&devPtr, size);
d_data = (float*)devPtr;// Allocation using runtime API
cudaMalloc(&d_data, size);
devPtr = (CUdeviceptr)d_data;

特别是,这意味着使用驱动程序 API 编写的应用程序可以调用使用运行时 API 编写的库(例如 cuFFT、cuBLAS…)。

参考手册的设备和版本管理部分的所有功能都可以互换使用。

5. Driver Entry Point Access

5.1. Introduction

驱动程序入口点访问 API 提供了一种检索 CUDA 驱动程序函数地址的方法。 从 CUDA 11.3 开始,用户可以使用从这些 API 获得的函数指针调用可用的 CUDA 驱动程序 API。

这些 API 提供的功能类似于它们的对应物,POSIX 平台上的 dlsym 和 Windows 上的 GetProcAddress。 提供的 API 将允许用户:

  • 使用 CUDA 驱动程序 API 检索驱动程序函数的地址。

  • 使用 CUDA 运行时 API 检索驱动程序函数的地址。

  • 请求 CUDA 驱动程序函数的每线程默认流版本。 有关更多详细信息,请参阅检索每个线程的默认流版本

  • 使用较新的驱动程序访问旧工具包上的新 CUDA 功能。

5.2. Driver Function Typedefs

为了帮助检索 CUDA 驱动程序 API 入口点,CUDA 工具包提供对包含所有 CUDA 驱动程序 API 的函数指针定义的头文件的访问。 这些头文件与 CUDA Toolkit 一起安装,并且在工具包的 include/ 目录中可用。 下表总结了包含每个 CUDA API 头文件的 typedef 的头文件。

Table 17. Typedefs header files for CUDA driver APIs
API header fileAPI Typedef header file
cuda.hcudaTypedefs.h
cudaGL.hcudaGLTypedefs.h
cudaProfiler.hcudaProfilerTypedefs.h
cudaVDPAU.hcudaVDPAUTypedefs.h
cudaEGL.hcudaEGLTypedefs.h
cudaD3D9.hcudaD3D9Typedefs.h
cudaD3D10.hcudaD3D10Typedefs.h
cudaD3D11.hcudaD3D11Typedefs.h

上面的头文件本身并没有定义实际的函数指针; 他们为函数指针定义了 typedef。 例如,cudaTypedefs.h 具有驱动 API cuMemAlloc 的以下 typedef

    typedef CUresult (CUDAAPI *PFN_cuMemAlloc_v3020)(CUdeviceptr_v2 *dptr, size_t bytesize);typedef CUresult (CUDAAPI *PFN_cuMemAlloc_v2000)(CUdeviceptr_v1 *dptr, unsigned int bytesize);

CUDA 驱动程序符号具有基于版本的命名方案,其名称中带有 _v* 扩展名,但第一个版本除外。 当特定 CUDA 驱动程序 API 的签名或语义发生变化时,我们会增加相应驱动程序符号的版本号。 对于 cuMemAlloc 驱动程序 API,第一个驱动程序符号名称是 cuMemAlloc,下一个符号名称是 cuMemAlloc_v2。 CUDA 2.0 (2000) 中引入的第一个版本的 typedefPFN_cuMemAlloc_v2000。 CUDA 3.2 (3020) 中引入的下一个版本的 typedefPFN_cuMemAlloc_v3020

typedef 可用于更轻松地在代码中定义适当类型的函数指针:

    PFN_cuMemAlloc_v3020 pfn_cuMemAlloc_v2;PFN_cuMemAlloc_v2000 pfn_cuMemAlloc_v1;

如果用户对 API 的特定版本感兴趣,则上述方法更可取。 此外,头文件中包含所有驱动程序符号的最新版本的预定义宏,这些驱动程序符号在安装的 CUDA 工具包发布时可用; 这些 typedef 没有 _v* 后缀。 对于 CUDA 11.3 工具包,cuMemAlloc_v2 是最新版本,所以我们也可以定义它的函数指针如下:

  PFN_cuMemAlloc pfn_cuMemAlloc;

5.3. Driver Function Retrieval

使用驱动程序入口点访问 API 和适当的 typedef,我们可以获得指向任何 CUDA 驱动程序 API 的函数指针。

5.3.1. Using the driver API

驱动程序 API 需要 CUDA 版本作为参数来获取请求的驱动程序符号的 ABI 兼容版本。 CUDA 驱动程序 API 有一个以 _v* 扩展名表示的按功能 ABI。 例如,考虑 cudaTypedefs.hcuStreamBeginCapture 的版本及其对应的 typedef

    // cuda.hCUresult CUDAAPI cuStreamBeginCapture(CUstream hStream);CUresult CUDAAPI cuStreamBeginCapture_v2(CUstream hStream, CUstreamCaptureMode mode);// cudaTypedefs.htypedef CUresult (CUDAAPI *PFN_cuStreamBeginCapture_v10000)(CUstream hStream);typedef CUresult (CUDAAPI *PFN_cuStreamBeginCapture_v10010)(CUstream hStream, CUstreamCaptureMode mode);

从上述代码片段中的typedefs,版本后缀_v10000_v10010表示上述API分别在CUDA 10.0CUDA 10.1中引入。

    #include <cudaTypedefs.h>// Declare the entry points for cuStreamBeginCapturePFN_cuStreamBeginCapture_v10000 pfn_cuStreamBeginCapture_v1;PFN_cuStreamBeginCapture_v10010 pfn_cuStreamBeginCapture_v2;// Get the function pointer to the cuStreamBeginCapture driver symbolcuGetProcAddress("cuStreamBeginCapture", &pfn_cuStreamBeginCapture_v1, 10000, CU_GET_PROC_ADDRESS_DEFAULT);// Get the function pointer to the cuStreamBeginCapture_v2 driver symbolcuGetProcAddress("cuStreamBeginCapture", &pfn_cuStreamBeginCapture_v2, 10010, CU_GET_PROC_ADDRESS_DEFAULT);

参考上面的代码片段,要检索到驱动程序 API cuStreamBeginCapture 的 _v1 版本的地址,CUDA 版本参数应该正好是 10.0 (10000)。同样,用于检索 _v2 版本 API 的地址的 CUDA 版本应该是 10.1 (10010)。为检索特定版本的驱动程序 API 指定更高的 CUDA 版本可能并不总是可移植的。例如,在此处使用 11030 仍会返回 _v2 符号,但如果在 CUDA 11.3 中发布假设的 _v3 版本,则当与 CUDA 11.3 驱动程序配对时,cuGetProcAddress API 将开始返回较新的 _v3 符号。由于 _v2 _v3 符号的 ABI 和函数签名可能不同,使用用于 _v2 符号的 _v10010 typedef 调用 _v3 函数将表现出未定义的行为。

要检索给定 CUDA 工具包的驱动程序 API 的最新版本,我们还可以指定 CUDA_VERSION 作为版本参数,并使用未版本化的 typedef 来定义函数指针。由于 _v2 是 CUDA 11.3 中驱动程序 API cuStreamBeginCapture 的最新版本,因此下面的代码片段显示了检索它的不同方法。

    // Assuming we are using CUDA 11.3 Toolkit#include <cudaTypedefs.h>// Declare the entry pointPFN_cuStreamBeginCapture pfn_cuStreamBeginCapture_latest;// Intialize the entry point. Specifying CUDA_VERSION will give the function pointer to the// cuStreamBeginCapture_v2 symbol since it is latest version on CUDA 11.3.cuGetProcAddress("cuStreamBeginCapture", &pfn_cuStreamBeginCapture_latest, CUDA_VERSION, CU_GET_PROC_ADDRESS_DEFAULT);

请注意,请求具有无效 CUDA 版本的驱动程序 API 将返回错误 CUDA_ERROR_NOT_FOUND。 在上面的代码示例中,传入小于 10000 (CUDA 10.0) 的版本将是无效的。

5.3.2. Using the runtime API

运行时 API 使用 CUDA 运行时版本来获取请求的驱动程序符号的 ABI 兼容版本。 在下面的代码片段中,所需的最低 CUDA 运行时版本将是 CUDA 11.2,因为当时引入了 cuMemAllocAsync

    #include <cudaTypedefs.h>// Declare the entry pointPFN_cuMemAllocAsync pfn_cuMemAllocAsync;// Intialize the entry point. Assuming CUDA runtime version >= 11.2cudaGetDriverEntryPoint("cuMemAllocAsync", &pfn_cuMemAllocAsync, cudaEnableDefault);// Call the entry pointpfn_cuMemAllocAsync(...);

5.3.3. Retrieve per-thread default stream versions

一些 CUDA 驱动程序 API 可以配置为具有默认流或每线程默认流语义。具有每个线程默认流语义的驱动程序 API 在其名称中以 _ptsz_ptds 为后缀。例如,cuLaunchKernel 有一个名为 cuLaunchKernel_ptsz 的每线程默认流变体。使用驱动程序入口点访问 API,用户可以请求驱动程序 API cuLaunchKernel 的每线程默认流版本,而不是默认流版本。为默认流或每线程默认流语义配置 CUDA 驱动程序 API 会影响同步行为。更多详细信息可以在这里找到。

驱动API的默认流或每线程默认流版本可以通过以下方式之一获得:

  • 使用编译标志 --default-stream per-thread 或定义宏 CUDA_API_PER_THREAD_DEFAULT_STREAM 以获取每个线程的默认流行为。
  • 分别使用标志 CU_GET_PROC_ADDRESS_LEGACY_STREAM/cudaEnableLegacyStreamCU_GET_PROC_ADDRESS_PER_THREAD_DEFAULT_STREAM/cudaEnablePerThreadDefaultStream 强制默认流或每个线程的默认流行为。

5.3.4. Access new CUDA features

始终建议安装最新的 CUDA 工具包以访问新的 CUDA 驱动程序功能,但如果出于某种原因,用户不想更新或无法访问最新的工具包,则可以使用 API 来访问新的 CUDA 功能 只有更新的 CUDA 驱动程序。 为了讨论,让我们假设用户使用 CUDA 11.3,并希望使用 CUDA 12.0 驱动程序中提供的新驱动程序 API cuFoo。 下面的代码片段说明了这个用例:

    int main(){// Assuming we have CUDA 12.0 driver installed.// Manually define the prototype as cudaTypedefs.h in CUDA 11.3 does not have the cuFoo typedeftypedef CUresult (CUDAAPI *PFN_cuFoo)(...);PFN_cuFoo pfn_cuFoo = NULL;// Get the address for cuFoo API using cuGetProcAddress. Specify CUDA version as// 12000 since cuFoo was introduced then or get the driver version dynamically// using cuDriverGetVersion int driverVersion;cuDriverGetVersion(&driverVersion);cuGetProcAddress("cuFoo", &pfn_cuFoo, driverVersion, CU_GET_PROC_ADDRESS_DEFAULT);if (pfn_cuFoo) {pfn_cuFoo(...);}else {printf("Cannot retrieve the address to cuFoo. Check if the latest driver for CUDA 12.0 is installed.\n");assert(0);}// rest of code here}

更多精彩内容:
https://www.nvidia.cn/gtc-global/?ncid=ref-dev-876561

相关文章:

CUDA中的底层驱动API

文章目录CUDA底层驱动API1. Context2. Module3. Kernel Execution4. Interoperability between Runtime and Driver APIs5. Driver Entry Point Access5.1. Introduction5.2. Driver Function Typedefs5.3. Driver Function Retrieval5.3.1. Using the driver API5.3.2. Using …...

【博客616】prometheus staleness对PromQL查询的影响

prometheus staleness对PromQL查询的影响 1、prometheus staleness 官方文档的解释&#xff1a; 概括&#xff1a; 运行查询时&#xff0c;将独立于实际的当前时间序列数据选择采样数据的时间戳。这主要是为了支持聚合&#xff08;sum、avg 等&#xff09;等情况&#xff0c…...

多传感器融合定位十三-基于图优化的建图方法其二

多传感器融合定位十二-基于图优化的建图方法其二3.4 预积分方差计算3.4.1 核心思路3.4.2 连续时间下的微分方程3.4.3 离散时间下的传递方程3.5 预积分更新4. 典型方案介绍4.1 LIO-SAM介绍5. 融合编码器的优化方案5.1 整体思路介绍5.2 预积分模型设计Reference: 深蓝学院-多传感…...

linux 服务器线上问题故障排查

一 线上故障排查概述 1.1 概述 线上故障排查一般从cpu,磁盘,内存,网络这4个方面入手; 二 磁盘的排查 2.1 磁盘排查 1.使用 df -hl 命令来查看磁盘使用情况 2.从读写性能排查:iostat -d -k -x命令来进行分析 最后一列%util可以看到每块磁盘写入的程度,而rrqpm/s以及…...

Sandman:一款基于NTP协议的红队后门研究工具

关于Sandman Sandman是一款基于NTP的强大后门工具&#xff0c;该工具可以帮助广大研究人员在一个安全增强型网络系统中执行红队任务。 Sandman可以充当Stager使用&#xff0c;该工具利用了NTP&#xff08;一个用于计算机时间/日期同步协议&#xff09;从预定义的服务器获取并…...

【SSL/TLS】准备工作:HTTPS服务器部署:Nginx部署

HTTPS服务器部署&#xff1a;Nginx部署1. 准备工作2. Nginx服务器YUM部署2.1 直接安装2.2 验证3. Nginx服务器源码部署3.1 下载源码包3.2 部署过程4. Nginx基本操作4.1 nginx常用命令行4.2 nginx重要目录1. 准备工作 1. Linux版本 [rootlocalhost ~]# cat /proc/version Li…...

微搭低代码从入门到精通11-数据模型

学习微搭低代码&#xff0c;先学习基本操作&#xff0c;然后学习组件的基本使用。解决了前端的问题&#xff0c;我们就需要深入学习后端的功能。后端一般包括两部分&#xff0c;第一部分是常规的数据库的操作&#xff0c;包括增删改查。第二部分是业务逻辑的编写&#xff0c;在…...

【算法基础】前缀和与差分

&#x1f63d;PREFACE&#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐ 评论&#x1f4dd;&#x1f4e2;系列专栏&#xff1a;算法&#x1f4aa;种一棵树最好是十年前其次是现在1.什么是前缀和前缀和指一个数组的某下标之前的所有数组元素的和&#xff08;包含其自身&#x…...

LTD212次升级 | 官网社区支持PC端展示 • 官网新增证件查询应用,支持条形码扫码查询

1、新增证件查询应用&#xff0c;支持条形码扫码查询&#xff1b; 2、新增用户社区PC端功能&#xff1b; 01证件查询应用 1、新增证件查询应用功能 支持证件信息录入、打印功能&#xff0c;支持条形码扫码识别。 后台管理操作路径&#xff1a;官微中心 - 应用 - 证件查询 …...

【安全】nginx反向代理+负载均衡上传webshell

目录 一、负载均衡反向代理下上传webshell Ⅰ、环境搭建 ①下载蚁剑&#xff0c;于github获取官方版&#xff1a; ②下载docker&docker-compose ③结合前面启动环境 ④验证 负载均衡下webshell上传 一、负载均衡反向代理下上传webshell 什么是反向代理&#xff1f; 通常的代…...

线程池框架

这是之前有做的一个可以接受用户传入任意类型的任务函数和任意参数&#xff0c;并且能拿到任务对应返回值的一个线程池框架&#xff0c;可以链接成动态库&#xff0c;用在相关项目里面。一共实现了两版&#xff0c;都是支持fixed和cached模式的&#xff0c;半同步半异步的&…...

【TCP的拥塞控制】基于窗口的拥塞控制

TCP的拥塞窗口CWND大小和传输轮次n的关系如下所示。&#xff08;本题10分&#xff09; cwnd12481632333435363738394041422122232425261248N1234567891011121314151617181920212223242526 问题&#xff1a; &#xff08;1&#xff09;慢开始阶段的时间间隔&#xff1f;&#…...

STP协议基础

STP协议技术来源二层环路及危害二层交换机网络的冗余性与环路人为错误导致的二层环路二层环路带来的问题STP生成树协议STP概述STP基本概念桥ID根桥COSTRPC&#xff08;Root Path Cost&#xff09;根路径开销PORT ID端口IDBPDU桥协议数据单元STP的计算过程&#xff08;1&#xf…...

Linux上面配置Apache2支持Https(ssl)具体方案实现

虽然Nginx比较流行&#xff0c;但是由于一些老项目用到了Apache2来支持Web服务&#xff0c;最近想给服务上一个Https支持&#xff0c;虽然看似教程简单&#xff0c;但是也遇到一些特殊情况&#xff0c;经历了一番折腾也算是解决了所有问题&#xff0c;将过程记录如下。演示是基…...

[Linux]进程替换

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【LINUX】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文…...

常见的锁策略面试题

你是怎么理解乐观锁和悲观锁的&#xff0c;具体怎么实现呢&#xff1f; 悲观锁认为多个线程访问同一个共享变量冲突的概率较大, 会在每次访问共享变量之前都去真正加锁 乐观锁认为多个线程访问同一个共享变量冲突的概率不大. 并不会真的加锁, 而是直接尝试访问数据. 在访问的…...

设计师一定要知道这几个网站,解决你80%的设计素材。

本期推荐一波设计师必备的设计素材网站&#xff0c;设计党赶紧马住&#xff01;能解决你日常设计中80%的素材。 1、菜鸟图库 菜鸟图库-免费设计素材下载 这是一个为新手设计师提供免费素材的设计网站&#xff0c;站内有超多平面模板、海报、UI设计、电商设计等相关素材&#x…...

QT基础入门

学习视频&#xff1a;QT开发概述_哔哩哔哩_bilibili 1.QT开发概述 1.什么是QT QT是一个1991年由Qt Company开发的跨平台C图形用户界面应用程序开发框架。它既可以开发GUI程序&#xff0c;也可用于开发非GUI程序&#xff0c;比如控制台工具和服务器。Qt是面向对象的框架&#…...

高数不定积分72题解答

题目来源&#xff1a;这72道积分题目会积了&#xff0c;绝对是高高手 作者&#xff1a; 湖心亭看雪 第一题 原式∫15x3dx15∫15x3d(5x3)15ln(5x3)C\begin{aligned} \text{原式}&\int \frac{1}{5x3}dx \\ &\frac{1}{5} \int\frac{1}{5x3}d(5x3) \\ &\frac{1}{5} ln…...

基于北方苍鹰算法优化LSTM(NGO-LSTM)研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Linux内核启动(理论,0.11版本)分段与分页

为什么要虚拟内存 我们知道&#xff0c;在之前上微机原理时&#xff0c;我们的程序是可以直接访问内存的&#xff0c;而且访问的是直接的物理内存&#xff0c;在实模式下&#xff0c;寄存器是16位的&#xff0c;数组总线&#xff08;data bus&#xff09;是16位的&#xff0c;…...

数据与C(字符串)

目录 一.概念引入 二.字符串&#xff08;数组存储&#xff0c;必须以\0结尾&#xff09; 三.错误示范 四.strlen&#xff08;&#xff09;和sizeof()相对于字符串的不同 一.概念引入 “a”,a哪个是字符哪个又是字符串&#xff0c;嘿嘿不用猜了 我们在上一章中说过&#x…...

Python+Go实践(电商架构三)

文章目录服务发现集成consul负载均衡负载均衡算法实现配置中心nacos服务发现 我们之前的架构是通过ipport来调用的python的API&#xff0c;这样做的弊端是 如果新加一个服务&#xff0c;就要到某个服务改web&#xff08;go&#xff09;层的调用代码&#xff0c;配置IP/Port并发…...

基于 MySQL 排它锁实现分布式可重入锁解决方案

一、MySQL 排它锁和共享锁 在进行实验前&#xff0c;先来了解下MySQL 的排它锁和共享锁&#xff0c;在 MySQL 中的锁分为表锁和行锁&#xff0c;在行锁中锁又分成了排它锁和共享锁两种类型。 1. 排它锁 排他锁又称为写锁&#xff0c;简称X锁&#xff0c;是一种悲观锁&#x…...

【大数据】Hadoop-HA-Federation-3.3.1集群高可用联邦安装部署文档(建议收藏哦)

背景概述 单 NameNode 的架构使得 HDFS 在集群扩展性和性能上都有潜在的问题&#xff0c;当集群大到一定程度后&#xff0c;NameNode 进程使用的内存可能会达到上百 G&#xff0c;NameNode 成为了性能的瓶颈。因而提出了 namenode 水平扩展方案-- Federation。 Federation 中…...

【设计模式之美 设计原则与思想:面向对象】14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?

在上一节课中&#xff0c;针对接口鉴权功能的开发&#xff0c;我们讲了如何进行面向对象分析&#xff08;OOA&#xff09;&#xff0c;也就是需求分析。实际上&#xff0c;需求定义清楚之后&#xff0c;这个问题就已经解决了一大半&#xff0c;这也是为什么我花了那么多篇幅来讲…...

工作技术小结

2023/1/31 关于后端接口编写小结 1&#xff0c;了解小程序原型图流程和细节性的东西 2&#xff0c;数据库关联结构仔细分析&#xff0c;找到最容易查询的关键字段&#xff0c;标语表之间靠什么关联 2023/2/10 在web抓包过程中&#xff0c;如果要实现批量抓取&#xff0c;必须解…...

无重复字符的最长子串-力扣3-java

一、题目描述给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入: s "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2:输入: s "bbbbb"输出: 1解释: 因为…...

java ssm高校教材管理平台 idea maven

设计并且实现一个基于JSP技术的高校教材管理平台的设计与实现。采用MYSQL为数据库开发平台&#xff0c;SSM框架&#xff0c;Tomcat网络信息服务作为应用服务器。高校教材管理平台的设计与实现的功能已基本实现&#xff0c;主要学生、教材管理、学习教材、教材入库、教材领取、缴…...

【Python学习笔记】25.Python3 输入和输出(1)

前言 在前面几个章节中&#xff0c;我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python两种输出值的方式: 表达式语句和 print() 函数。 第三种方式是使用文件对象的 write() 方法&#xff0c;标准输出文件可以用…...