【参加CUDA线上训练营】CUDA进阶之路 - Chapter 8 - CUDA流和CUDA工具库

2023-11-04

8.1 CUDA Stream

前面的章节只介绍了核函数在GPU内部的执行流程,忽略了CPU与GPU之间的交互过程。可以看出,CPU与GPU之间的交互涉及两个操作:数据传输和核函数执行。CPU将任务添加到不同的队列中,GPU驱动程序则负责执行队列中的任务。这两个任务是相互独立的,可以并发运行,即GPU在执行核函数时,可以同时进行CPU与GPU之间的数据传输,这就是所谓的计算与传输重叠(overlap)。

在程序中实现计算与传输重叠功能,需要使用CUDA流(stream)。流可以理解为一系列异步的GPU指令队列,这些操作按照主机代码确定的顺序在GPU上执行。流内各个指令的执行顺序是严格固定的,多个流之间则是可以异步/并行的(下图:CUDA 流)。
请添加图片描述

抽象出CUDA流这一概念在加速应用程序方面起到重要作用,它提供了一种更高的并行层级。以前提到的并行层级从细到粗:Thread、Thread Block、Grid,它们都是在Kernel Launch当中发生的,而Kernel Launch加上数据传输现在还可以打包到CUDA 流当中,再加上了一个粗粒度的并行层级。

CUDA流除了提供了一种更高层级的并行方式,更多情况下,我们使用CUDA 流是为了隐藏其中的一些开销。例如,流1在执行数据拷贝操作时,数据传输引擎的带宽是占满了,但是流2正在执行的Kernel引擎却并不受到影响。如果没有流的概念,流2的kernel只能等流1数据拷贝结束之后才能进行(上图)。

CUDA流还可以分为两类:

  • 非空流(NULL Stream):隐式定义(implicitly declared)的流。之前在没有介绍CUDA 流时,我们程序中实际上都是存在一个这样的非空流的。
  • 空流(non-NULL Stream):显式定义(explicitly declared)的流。
总结

基于流的异步内核启动(Kernel Launch)和数据传输支持以下类型的粗粒度并发

  • 重叠主机和设备计算;
  • 重叠主机计算和设备数据传输;
  • 重叠主机设备数据传输和设备计算;
  • 并发设备计算(多个设备)

当然也有不支持并发的情况:

  • 主机上page-locked内存的分配;
  • 设备内存的分配;
  • 设备内存的设置(Memeset());
  • 同一个设备上内存的复制;

请添加图片描述

流的创建与销毁

特别注意,内存的复制使用下面的异步函数:

cudaError_t cudaMemcpyAsync(void* dst, const void* src, size_t count, cudaMemcpyKind kind, cudaStream_t stream=0)

使用这个API才可以在最后的参数里面指定CUDA 流。

以及Kernel Launch函数的执行配置当中也是可以指定CUDA流的:

kernel_name<<<grid, block, sharedMemSize, stream>>>(argument list);
    // 创建流
    cudaStream_t stream_0, stream_1;
    CHECK(cudaStreamCreate(&stream_0));
    CHECK(cudaStreamCreate(&stream_1));

    ...
    // 数据转移
    CHECK(cudaMemcpyAsync(d_a_0, h_a+i, sizeof(int) * N, cudaMemcpyHostToDevice, stream_0));
    
    ...
    // 流的同步,保证流执行完毕。以前都是使用|\textsf{cudaDeviceSynchronize()}|来进行同步的,而流的层级明显高于Device。
    CHECK(cudaStreamSynchronize(stream_0));

    ...
    // 流销毁
    CHECK( cudaStreamDestroy( stream_0 ) );
CUDA 流加速程序运行:以向量加法为例
for (int i = 0; i < nstreams; i++){
    int offset = i * eles_per_stream; 
    cudaMemcpyAsync(&d_A[offset], &h_A[offset], eles_per_stream * sizeof(int), cudaMemcpyHostToDevice, streams[i]); 
    
    cudaMemcpyAsync(&d_B[offset], &h_B[offset], eles_per_stream * sizeof(int), cudaMemcpyHostToDevice, streams[i]); 
    |$\ldots$|

    vector_sum<<<|$\ldots$|, streams[i]>>>(d_A + offset, d_B + offset, d_C + offset);
    
    cudaMemcpyAsync(&h_C[offset], &d_C[offset], eles_per_stream * sizeof(int), cudaMemcpyDeviceToHost, streams[i]);
}
    for (int i = 0; i < nstreams; i++) 
        cudaStreamSynchronize(streams[i]); // 一定要记得同步!

请添加图片描述

当for循环结束时,队列中应当包含了许多等待GPU执行的工作。如果想确保GPU执行完了计算与内存复制等操作,那么就需要等GPU与主机同步。也就是说,主机在继续执行之前,需要首先等待GPU执行完成cudaStreamSynchronize()

同时调度某个流的所有操作可能无意中阻塞另一个流的复制操作或者核函数的执行操作。解决方案是:在将操作放入流的队列时采用宽度优先的方式而非深度优先的方式。

请添加图片描述

如果数据传输量特别小,kernel里计算的时间特别少,那么CUDA流加速的效果不明显。具体申请多少个CUDA 流最合适这都是依据实时硬件资源而说不准的,但申请太多会是得不偿失的。

8.2 CUDA Libraries

CUDA自2007年问世到现在,已经拥有100多个库了,之前我们手动实现的一些方法,例如矩阵乘法等,实际上都已经有库为我们准备好相应的API了。NVIDIA提供这么多库的目的就是为了让开发者更好地使用GPU。

CUDA库和CPU编程所用到的库没有什么区别,都是一系列接口的集合,主要好处是,只需要编写host代码,调用相应API即可,可以节约很多开发时间,而且我们完全可以信任这些库能够达到很好的性能。当然,完全依赖于这些库而对CUDA性能优化一无所知也是不行的,我们依然需要手动做一些改进来挖掘出更好的性能。

目前CUDA库主要由以下六个部分组成

  • Math Libraries
  • Image and Video Libraries
  • Deep Learning
  • Parallel Algorithms
  • Communication Libraries
  • Partner Libraries(OpenCV、FFmpeg…)

8.2.1 A Common Library Workflow

下面是一个使用CUDA库的具体步骤,当然,各个库的使用可能不尽相同,但是不会逃脱下面的几个步骤,差异基本上就是少了哪几步而已。

  1. 创建一个库的句柄来管理上下文信息;
  2. 数据准备:
    • 分配device存储空间给输入输出;
    • 如果输入的格式并不是库中API支持的需要做一下转换:例如精度对齐、数组是行主序还是列主序、是否有特殊的结构体。
    • 填充device Memory数据:类似cudaMemcpy的初始化函数,大部分库都有自己的API来实现这个功能,例如当使用cuBLAS的时候,我们要将一个vector传送到device,使用的就是cubalsSetVector()
  3. 配置执行:
    • 配置library computation以便执行:通过Handle配置计算参数。
    • 调用库函数来让GPU工作;
  4. 后处理:
    • 取回device Memory中的结果:这一步将计算结果从device送回host,是上面填充device Memory数据的反过程。
    • 如果取回的结果不是APP的原始格式,就做一次转换:是上面如果输入的格式并不是库中API支持的需要做一下转换的反过程。
    • 释放CUDA资源;
  5. 继续其它的工作。

其中,关于一个库的句柄Handle,其包含了该库的一些上下文信息,比如数据格式、device的使用等。我们可以认为这是一个存放在host对程序员透明的描述性文件object,这个object包含了跟这个库相关联的一些信息。例如,我们可定希望所有的库的操作运行在一个特别的CUDA stream,尽管不同的库使用不同函数名字,但是大多数都会规定所有的库操作以一定的stream发生(比如cuSPARSE使用cusparseSetSStream、cuBLAS使用cublasSetStream、cuFFT使用cufftSetStream)。stream的信息就会保存在这个handle中。

8.2.2 cuBLAS库

cuBLAS库是基于NVIDIA CUDA运行时的BLAS(Basic Linear Algebra Subprograms)实现。cuBLAS level1是专门的vector之间操作。level2是矩阵和向量之间的操作。level3是矩阵和矩阵之间的操作。相对于cuSPARSE,cuBLAS不支持稀疏矩阵数据格式,它只支持而且善于稠密矩阵和向量的使用。

请添加图片描述

以下是使用cuBLAS库实现矩阵乘法的代码:

#include <cstdio>
#include "../matmul/error.cuh"
#include |\colorbox{OrangeRed!40}{<cublas\_v2.h>}|

void print_matrix(int R, int C, double* A, const char* name)
{
    printf("%s = \n", name);
    for (int r = 0; r < R; ++r)
    {
        for (int c = 0; c < C; ++c)
        {
            printf("%10.6f", A[c * R + r]);
        }
        printf("\n");
    }
}

int main(void){
    int m = 2, n = 2, k = 3;
    int mn = m * n, mk = m * k, nk = n * k;

    double *h_a, *h_b, *h_c;
    CHECK(cudaHostAlloc((void **)&h_a, sizeof(double)*mn, cudaHostAllocDefault)); // 用cudaHostAlloc会出错
    CHECK(cudaHostAlloc((void **)&h_b, sizeof(double)*nk, cudaHostAllocDefault));
    CHECK(cudaHostAlloc((void **)&h_c, sizeof(double)*mk, cudaHostAllocDefault));

    for(int i=0; i<mn; i++){
        h_a[i] = i;
    }
    print_matrix(m, n, h_a, "A");

    for(int i=0; i<nk; i++){
        h_b[i] = i;
    }
    print_matrix(n, k, h_b, "B");

    for(int i=0; i<mk; i++){
        h_c[i] = 0;
    }

    double *d_a, *d_b, *d_c;
    CHECK(cudaMalloc((void **)&d_a, sizeof(double)*mn));
    CHECK(cudaMalloc((void **)&d_b, sizeof(double)*nk));
    CHECK(cudaMalloc((void **)&d_c, sizeof(double)*mk));

    |\colorbox{OrangeRed!40}{cublasSetVector}|(mn, sizeof(double), h_a, 1, d_a, 1);
    |\colorbox{OrangeRed!40}{cublasSetVector}|(nk, sizeof(double), h_b, 1, d_b, 1);
    |\colorbox{OrangeRed!40}{cublasSetVector}|(mk, sizeof(double), h_c, 1, d_c, 1);

    |\colorbox{OrangeRed!40}{cublasHandle\_t}| handle;
    |\colorbox{OrangeRed!40}{cublasCreate}|(&handle);
    double alpha = 1.0;
    double beta = 0.0;
    |\colorbox{OrangeRed!40}{cublasDgemm}|(handle, CUBLAS_OP_N, CUBLAS_OP_N,
                m, k, n, &alpha, d_a, m, d_b, n, &beta, d_c, m);
    |\colorbox{OrangeRed!40}{cublasDestroy}|(handle);
    |\colorbox{OrangeRed!40}{cublasGetVector}|(mk, sizeof(double), d_c, 1, h_c, 1);

    print_matrix(m, k, h_c, "C = A x B");

    CHECK(cudaFreeHost(h_a));
    CHECK(cudaFreeHost(h_b));
    CHECK(cudaFreeHost(h_c));
    CHECK(cudaFree(d_a));
    CHECK(cudaFree(d_b));
    CHECK(cudaFree(d_c));
    return 0;
}    

8.2.3 CV-CUDA

在以往的视觉模型开发与应用中,我们往往更重视模型本身的优化,提升其速度与效果。相反,却忽视了图像的预处理与后处理阶段的优化,当模型计算效率越来越高时,它们最终可能会变成整个图像任务的瓶颈。

为了解决这样的瓶颈,NVIDIA和字节跳动机器学习团队开源了包含众多图像预处理算子库CV_CUDA,它能够运行在GPU上,算子速度能够达到OpenCV在CPU上运行速度的百倍左右。如果使用CV-CUDA作为后端替换OpenCV和TorchVision,整个推理的吞吐量能达到原来的二十多倍。此外,不仅仅是速度的提升,在效果上CV-CUDA的计算精度上已经对齐OpenCV,因此训练推理能无缝衔接。

为什么OpenCV仍然不够好?Torchvision呢?
在CV领域,应用最广泛的图像处理库当然是长久维护的OpenCV了,其广泛的图像处理操作能基本满足视觉任务的预/后处理。但是随着图像任务负载的加大,其速度已经逐渐跟不上,因为OpenCV绝大多数图像操作都是CPU实现,缺少GPU实现。同时,少数有GPU实现的算子仍存在以下三大问题:

  • 部分算子的CPU和GPU计算结果精度无法对齐;
  • 部分算子GPU性能比CPU还弱;
  • 处理流程中既包含CPU算子,也包含GPU算子,这会额外增加内存与显存中的空间申请与数据迁移/数据拷贝。

Torchvision面临和OpenCV一样的问题,除此之外,工程师部署模型为了效率更可能使用C++实现推理过程,因此将没办法使用Torchvision而需要转向OpenCV这样的C++视觉库。

8.2.4 cuDNN库

NVIDIA CUDA® 深度神经网络库 (cuDNN) 是一个 GPU 加速的深度神经网络基元库,能够以高度优化的方式实现标准例程(如前向和反向卷积、池化层、归一化和激活层)。

全球的深度学习研究人员和框架开发者都依赖 cuDNN 来实现高性能 GPU 加速。借助 cuDNN,研究人员和开发者可以专注于训练神经网络及开发软件应用,而不必花时间进行低层级的 GPU 性能调整。cuDNN 可加速广泛应用的深度学习框架,包括 Caffe2、Chainer、Keras、MATLAB、MxNet、PaddlePaddle、PyTorch 和 TensorFlow。

请添加图片描述

主要特性:

  • 为各种常用卷积实现了 Tensor Core 加速,包括 2D 卷积、3D 卷积、分组卷积、深度可分离卷积以及包含 NHWC 和 NCHW 输入及输出的扩张卷积;
  • 为诸多计算机视觉和语音模型优化了内核,包括 ResNet、ResNext、EfficientNet、EfficientDet、SSD、MaskRCNN、Unet、VNet、BERT、GPT-2、Tacotron2 和 WaveGlow;
  • 支持 FP32、FP16、BF16 和 TF32 浮点格式以及 INT8 和 UINT8 整数格式;
    ) 4D 张量的任意维排序、跨步和子区域意味着可轻松集成到任意神经网络实现中;
  • 能为各种 CNN 体系架构上的融合运算提速。

8.3 结束语

  • CUDA编程模型依赖于GPU硬件环境,不同的硬件设备,需要不同的加速手段;
  • 在真正的开发过程中,其实有大量的现成的工具,希望大家能够处理一些通用问题的时候,使用现成的工具库;
  • 进阶之路既有高处,也有细节。关注最新的动态,能让我们更快的掌握更好的解决问题的手段。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【参加CUDA线上训练营】CUDA进阶之路 - Chapter 8 - CUDA流和CUDA工具库 的相关文章

  • 如何从python导入路径中删除当前目录

    我想使用 Mercurial 存储库hg本身 也就是说 我克隆了 Mercurialhttps www mercurial scm org repo hg https www mercurial scm org repo hg并想运行一些h
  • Docker 容器可以访问 DNS,但无法解析主机

    我在运行 docker 容器时遇到一个有趣的问题 突然间 我无法从容器内解析 DNS 这是一个概要 一切都没有解决 apt get pip 一次性 ping 容器等正在运行docker run it dns 8 8 8 8 ubuntu p
  • bash.sh 运行 cron 的权限被拒绝

    如何在这里使用 bash 脚本运行 cron 我做了如下操作 这里有错误 我想知道如何在 ubuntu 中做到这一点 我现在对它感到震惊 bash sh 文件 bin bash cd var www Controller usr bin p
  • 无法使用 tar -cvpzf 解压完整目录

    把我的头敲在这上面 I used tar cvpzf file tar gz压缩一个完整的目录 我将文件移动到另一台服务器 并尝试解压缩复制存档的目录 无法使其发挥作用 bash 3 2 tar xvpzf news tar gz tar
  • Python 线程与 Linux 中的多处理

    基于此question https stackoverflow com questions 807506 threads vs processes in linux我假设创建新流程应该几乎和创造新线程在Linux中 然而 很少的测试显示出截
  • 在linux中使用setcap [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 要将 cap net raw 功能添加到 例如 bin ping 我们使用以下命令 setcap cap net raw ep bin ping ep
  • 查找当前打开的文件句柄数(不是 lsof )

    在 NIX系统上 有没有办法找出当前正在运行的进程中有多少个打开的文件句柄 我正在从正在运行的进程中寻找在 C 中使用的 API 或公式 在某些系统上 见下文 您可以在 proc pid fd 中对它们进行计数 如果不属于其中之一 请参阅下
  • 在Linux中执行jar文件[关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我创建了一个可执行的 Java jar 文件 也就是说 我将 java 程序正确打包到 jar 文件中 包括 META INF MANIFEST 文件
  • 带有接收器的 boost_log 示例无法编译

    我正在考虑将 boost log 用于一个项目 一开始我就遇到了以下问题 我在以下位置找到的升压日志示例 http www boost org doc libs 1 54 0 libs log example doc tutorial fi
  • “以下软件包将被更高优先级的频道取代”是什么意思?

    我正在尝试将 fuzzywuzzy 安装到 64 位 Linux 中的 Anaconda 发行版上 当我这样做时 它试图改变我的conda and conda env to conda forge渠道 如下 我通过以下方式在 anacond
  • 多线程进程的线程ID可以与另一个正在运行的进程的进程ID相同吗?

    我正在尝试找到一种方法来唯一标识多进程环境中的线程 我有一个服务器 它跟踪连接到它的不同进程 其中一些是多线程的 一些不是 为了识别多线程连接中的线程 我使用线程 ID 作为唯一标识符 在任何给定时间最多有 1 个多线程进程连接 我的问题是
  • 如何使用 bash 粘贴来自单独文件的列?

    我想用分隔符 合并不同的列表 第一个列表有 2 个单词 cat first one who 第二个列表有 10000 个单词 cat second languages more simple advanced home expert tes
  • 在 Bash 中使用“$RANDOM”生成随机字符串

    我正在尝试使用 Bash 变量 RANDOM创建一个由包含整数和字母数字的变量中的 8 个字符组成的随机字符串 例如 var abcd1234ABCD 我怎样才能做到这一点 使用参数扩展 chars 是可能的字符数 是模运算符 chars
  • /proc/kmsg 和 dmsg 有什么区别?

    我们通常这样做cat proc kmsg or dmesg从用户空间查看内核日志 我明白了dmesg是一个循环缓冲区 它从kmsg 但是kmsg也不是循环缓冲区 它们之间有什么区别和联系呢 宽松地说 dmesg 是一个转储 proc kms
  • 使用多个 NIC 广播 UDP 数据包

    我正在 Linux 中为相机控制器构建嵌入式系统 非实时 我在让网络做我想做的事情时遇到问题 该系统有 3 个 NIC 1 个 100base T 和 2 个千兆端口 我将较慢的连接到相机 这就是它支持的全部 而较快的连接是与其他机器的点对
  • 未找到 DEADLINE 调度策略

    我想在 C 中实现 DEADLINE 调度策略 我知道该功能已实现Linux 3 14 10我正在使用 Ubuntu 14 04Linux 3 17 0 031700 lowlatency 201410060605 SMP PREEMPT这
  • 在 Windows 下对 Unix 下创建的文件使用 fstream::seekg

    我有一个C 跨平台程序 在Linux下用g 编译 在PC下用Visual Studio编译 该程序将行写入文本文件 使用 lt lt 运算符和std endl 但也可以从生成的文本文件中读回数据 使用std getline 为了优化数据访问
  • 导入错误:没有名为“tensorrt”的模块

    我使用 Debian 安装在我的虚拟机上安装了 TensorRT 如果我运行 dpkg l grep TensorRT 我会得到预期的结果 ii graphsurgeon tf 5 0 2 1 cuda10 0 amd64 GraphSur
  • 将任何当前目录“./”添加到Linux中的搜索路径[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何将任何当前目录 添加到 Linux 中可执行文件的搜索路径中 我知道这是一个旧答案 但如果其他人像我一样通过谷歌偶然发现这个问题 这里
  • Linux 内核中是否使用了扩展指令集(SSE、MMX)?

    好吧 它们带来 至少应该带来 性能的巨大提升 不是吗 所以 我还没有看到任何 Linux 内核源代码 但很想问 它们是否以某种方式被使用 在这种情况下 对于没有此类指令的系统 必须有一些特殊的 代码上限 SSE 和 MMX 指令集在音频 视

随机推荐

  • 【SpringBoot学习】05-自定义LocaleResolver国际化区域语言

    首先配置好i18n文件 以及所对应的语言 在properties中配置 因为默认为 message spring messages basename i18n xxxx 页面内容替换 配置点击 配置一个 LocaleResolver 自定义
  • 十种常用机器学习算法入门

    弱人工智能近几年取得了重大突破 悄然间 已经成为每个人生活中必不可少的一部分 以我们的智能手机为例 看看到底温藏着多少人工智能的神奇魔术 下图是一部典型的智能手机上安装的一些常见应用程序 可能很多人都猜不到 人工智能技术已经是手机上很多应用
  • 【Linux】动静态库

    文章目录 1 动静态库的原理 2 动态库和静态库基础 3 动静态库的实现 3 1设计一个静态库 3 2设计一个动态库 4 动静态库的使用 4 1静态库的使用 4 2动态库的使用 4 3动态库的多进程共享原理 1 动静态库的原理 源文件和头文
  • KAFKA基础操作命令

    1 查看所有的话题 topic kafka topics sh zookeeper 10 10 6 98 2181 kafka list 2 删除 topic kafka topics sh zookeeper 10 10 6 99 218
  • 华为虚拟桌面发放流程【FusionAccess】——详解

    华为FusionAccess作为一个桌面云接入管理系统 以服务器虚拟化为基础 共享CPU 内存 网络连接 存储器等底层物理硬件资源 使用户桌面以虚拟机的形式独立运行 虚拟机彼此隔离 提供给用户使用 那么虚拟机是如何一步步为用户所用呢 众所周
  • Leetcode 347. 前 K 个高频元素(堆实现)

    前 K 个高频元素 堆实现 给定一个非空的整数数组 返回其中出现频率前 k 高的元素 示例 1 输入 nums 1 1 1 2 2 3 k 2 输出 1 2 示例 2 输入 nums 1 k 1 输出 1 class Solution pu
  • 初学者使用R语言读取excel/csv/txt的注意事项

    本文首发于 医学和生信笔记 完美观看体验请至公众号查看本文 文章目录 把数据读入R语言 Excel csv txt 其他 写出文件 从R语言另存为其他格式 本文面向R语言初学者 尤其是生物医药领域的初学者 大佬勿喷 在之前的推文中 我们用两
  • java设置断点,在Java中设置断点

    How does setting breakpoints in Java work Is it just based on the source file name and line number Does the class or met
  • 编写自动化软件+python

    前言 本文分为代码篇和实操篇 代码篇以 不高兴就喝水 的代码为原版和其他改版做对比 帮助学习了解 实操部分也分为原版的实操和改版的实操 学前准备 pyautogui库用法 https blog csdn net qingfengxd1 ar
  • Cgroups使用

    Cgroups使用 一 Cgroups介绍 linux内核提供了cgroups控制组 controlgroups 的功能 最初由google的工程师提出 后来被整合进Linux内核 Cgroups也是LXC LinuxContainer容器
  • SQL中group by的用法总结

    一 简介SQL语言 SQL语言 是结构化查询语言的简称 SQL语言是一种数据库查询和程序设计语言 用于存取数据以及查询 更新和管理关系数据库系统 同时也是数据库脚本文件的扩展名 SQL语言 是高级的非过程化编程语言 允许用户在高层数据结构上
  • Linux(CentOS7)安装docker

    CentOS7 安装Docker教程 docker官网安装步骤 1 卸载旧版本 sudo yum remove docker docker client docker client latest docker common docker l
  • css 划对号,css3画个圆圈里的对号

    效果如图 image png 基本思路 1先画一个圆 2画两个位于圆中间的小矩形 3旋转矩形 一个左旋45度 一个右旋45度 4利用absolute进行定位 demo2 width 40px height 40px border radiu
  • 爬虫 — 验证码反爬

    目录 一 超级鹰 二 图片验证模拟登录 1 页面分析 1 1 模拟用户正常登录流程 1 2 识别图片里面的文字 2 代码实现 三 滑块模拟登录 1 页面分析 2 代码实现 通过对比像素获取缺口位置 四 openCV 1 简介 2 代码 3
  • 目标检测实战项目『体验篇』

    本文建议阅读时间 8 min 什么是目标检测 目标检测 Object Detection 的任务是找出图像中所有感兴趣的目标 物体 确定它们的类别和位置 是计算机视觉领域的核心问题之一 由于各类物体有不同的外观 形状和姿态 加上成像时光照
  • 二维多孔介质图像的粒度分布研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 使用流域分割算法对岩石二维二值图像进行粒度
  • C # HTTP支持跨域请求

    修改响应的请求头 具体修改详见代码 private void httpPostRequestHandle while true try 等待请求连接 没有请求则GetContext处于阻塞状态 HttpListenerContext req
  • VS2022下载安装与基本使用(写C语言)

    最近遇到一种问题 就是想要写一写C语言的代码 但是网页编辑器功能不全 GCC需要安装Liunx系统 VS又体量太大过于复杂 用keil又需要连接硬件 所以比较纠结 工作中通常使用的是Keil 但是如果有时不方便使用硬件 怎么办呢 所以就想着
  • vue3无限轮播案例

  • 【参加CUDA线上训练营】CUDA进阶之路 - Chapter 8 - CUDA流和CUDA工具库

    8 1 CUDA Stream 前面的章节只介绍了核函数在GPU内部的执行流程 忽略了CPU与GPU之间的交互过程 可以看出 CPU与GPU之间的交互涉及两个操作 数据传输和核函数执行 CPU将任务添加到不同的队列中 GPU驱动程序则负责执