NIVIDIA高性能计算CUDA笔记(四)cuDNN深度神经网络计算库简介及卷积操作示例
NIVIDIA高性能计算CUDA笔记(四)
cuDNN深度神经网络计算库简介及卷积操作示例
\(cuDNN\),全称为NIVIDIA CUDA Deep Neural Network Library,是深度神经网络算子层级GPU加速库集合,提供了深度学习算法中常见算子的高效实现,专门为深度学习框架(如\(TensorFlow\),\(PyTorch\),\(Caffe\), \(MXNet\)等)实现常见的神经网络层提供极致的优化的实现 ,所以也直接成为了很多上层推理引擎底层调优的算子备选实现,比如\(TensorRT\),比如\(TVM\)。再换个角度理解,之前在该系列笔记的第一篇提到宗旨“解读介绍英伟达软件生态之CUDA”中介绍了GPU的编程语言\(\text{CUDA}\) C“,调用\(\text{CUDA}\) C其实就是为了写一些kernel,而这里的\(cuDNN\) 则是提供一些写好的高效的CUDA C kernel的集合。正如,该系列笔记介绍的\(cuFFT\),\(cuBLAS\) ,区别就在于它们面向的应用场景不同,这里的\(cuDNN\)就是面向深度神经网络算子的高效实现。
1.cuDNN的句柄与描述子
在\(\text{cuDNN}\)里,几乎所有操作都需要以下几个基本对象,我们这些基本对象进行描述:
A.句柄(cudnnHandle_t)
在使用cuDNN进行任何操作之前,需要初始化cuDNN上下文。可以使用cudnnCreate()函数来创建一个cuDNN上下文句柄,后续的所有cuDNN操作都将基于这个句柄进行。句柄(cudnnHandle_t)是cuDNN中一个非常重要的概念,它本质是一个指向\(cuDNN\)内部状态数据数据结构的指针,句柄的作用类似于“会化ID”,cuDNN通过它识别并管理当前的计算环境,确保所有操作在正确的上下文(如特定GPU设备、资源分配状态等)中执行。
handle是cudnnHandle_t类型的变量的指针。cudnCreate 函数接收这个地址后,就能在函数内部将创建的上下文句柄直接赋值到cudnn变量所在的内存空间,这样在函数调用结束后,外部的handle变量就保存了有效的上下文句柄,供后续操作使用。
cudnnStatus_t cudnnCreate(cudnnHandle_t *handle);
cudnnStatus_t cudnnDestroy(cudnnHandle_t handle);
- 作用:创建\(\text{cuDNN}\)上下文句柄,类似于\(\text{CUDA runtime}\)的\(cudaStream\)。所有操作都依赖它;
- 参数:\(handle\) 是\(\text{cuDNN}\)运行环境的句柄;
- 创建与销毁:句柄通过
cudnnCreate()函数创建,在使用完成后,必须通过cudnnDestroy()函数进行销毁,以释放句柄所占用的资源,避免内存泄漏; - 线性安全性:cuDNN句柄不是线性安全的,每个线程应该使用独立的句柄对象。如果在多个线程环境中使用同一个句柄,可能导致不可预测的错误。因此,在多线程编程时,需要为每个线程单独创建和管理句柄。
- 与CUDA上下文的关联:cuDNN句柄通常是管理GPU资源的基础,cuDNN句柄通过关联的CUDA上下文来访问GPU硬件资源,进行数据传输和计算操作。
cudnnStatus_t是cuDNN所有函数调用的返回状态类型,理解它非常重要,因为它决定了你能否成功调用\(\text{cuDNN API}\) 。
typedef enum {
CUDNN_STATUS_SUCCESS = 0,
CUDNN_STATUS_NOT_INITIALIZED = 1,
CUDNN_STATUS_ALLOC_FAILED = 2,
CUDNN_STATUS_BAD_PARAM = 3,
CUDNN_STATUS_INTERNAL_ERROR = 4,
CUDNN_STATUS_INVALID_VALUE = 5,
CUDNN_STATUS_ARCH_MISMATCH = 6,
CUDNN_STATUS_MAPPING_ERROR = 7,
CUDNN_STATUS_EXECUTION_FAILED = 8,
CUDNN_STATUS_NOT_SUPPORTED = 9,
CUDNN_STATUS_LICENSE_ERROR = 10,
CUDNN_STATUS_RUNTIME_PREREQUISITE_MISSING = 11
} cudnnStatus_t;
| 枚举值 | 含义 | 说明 |
|---|---|---|
| CUDNN_STATUS_SUCCESS | 成功 | 调用成功,没有错误 |
| CUDNN_STATUS_NOT_INITIALIZED | 未初始化 | cuDNN handle 未创建或初始化失败 |
| CUDNN_STATUS_ALLOC_FAILED | 内存分配失败 | GPU 内存不足,cudaMalloc 失败 |
| CUDNN_STATUS_BAD_PARAM | 参数错误 | 函数参数有误,类型或维度不匹配 |
| CUDNN_STATUS_INTERNAL_ERROR | 内部错误 | cuDNN 内部执行错误,不是你传的参数问题 |
| CUDNN_STATUS_INVALID_VALUE | 数值错误 | 数值超出范围,例如 stride、padding 不合理 |
| CUDNN_STATUS_ARCH_MISMATCH | 架构不匹配 | 当前 GPU 架构不支持所选算法 |
| CUDNN_STATUS_MAPPING_ERROR | 内存映射错误 | GPU 内存映射出错 |
| CUDNN_STATUS_EXECUTION_FAILED | 执行失败 | kernel 执行失败,例如溢出或 cuda kernel 错误 |
| CUDNN_STATUS_NOT_SUPPORTED | 不支持 | 该功能或数据类型不被支持 |
| CUDNN_STATUS_LICENSE_ERROR | 许可错误 | 需要授权许可,但未满足条件 |
| CUDNN_STATUS_RUNTIME_PREREQUISITE_MISSING | 依赖缺失 | 运行时依赖库缺失或版本不匹配 |
描述符是\(cuDNN\) 中用于描述各种对象属性的数据结构,它为cuDNN函数提供了执行操作所需的元数据信息。cuDNN中存在多种类型的描述符,如张量描述符(cudnnTensorDescriptor_t),卷积描述符(cudnnConvolutionDescriptor_t),池化描述符(cudnnPoolingDescriptor_t)等不同类型的描述符用于描述不同的操作。
B.张量描述子(cudnnTensorDescriptor_t)
张量描述符(cudnnTensorDescriptor_t) 用于描述各种对象属性的数据结构,它为cuDNN函数提供了执行操作所需的元数据信息。包括数据张量的(如批量大小、通道数、高度、宽度等)、数据类型(如CUDNN_DATA_FLOAT表示32位浮点数)、张量布局(如CUDNN_TENSOR_NCHW表示数据批量-通道-高度-宽度的布局)。通过cudnnCreateTensorDescriptor() 创建,cudnnSetTensor4dDescriptor()等函数设置属性,cudnnDestroyDescriptor()销毁。
cudnnStatus_t cudnnCreateTensorDescriptor(cudnnTensorDescriptor_t *tensorDesc);
cudnnStatus_t cudnnSetTensor4dDescriptor(
cudnnTensorDescriptor_t tensorDesc,
cudnnTensorFormat_t format, // NCHW 或 NHWC
cudnnDataType_t dataType, // float, half, double...
int n, int c, int h, int w // 维度
);
cudnnStatus_t cudnnDestroyTensorDescriptor(cudnnTensorDescriptor_t tensorDesc);
作用:描述输入、输出张量的维度和数据类型。
张量在\(cuDNN\)中通常按照\(\text{4D格式(N,C,H,W)}\)(假设NCHW格式)存储:
- N:batch size,代表一批量输入样本的数量;
- C:通道数(例如\(RGB\)图像就是3个通道;灰度图就是1个通道);
- H:图像的行数,高度
- W: 图像的列数,宽度
cudnnTensorFormat_t代表张量的格式
typedef enum {
CUDNN_TENSOR_NCHW = 0, // [batch, channels, height, width]
CUDNN_TENSOR_NHWC = 1, // [batch, height, width, channels]
CUDNN_TENSOR_NCHW_VECT_C = 2 // 向量化通道格式 (主要用于部分卷积加速)
} cudnnTensorFormat_t;
| 枚举值 | 说明 |
|---|---|
| CUDNN_TENSOR_NCHW | 最常用格式,张量按 [N, C, H, W] 存储,适合大多数卷积、批量计算。 |
| CUDNN_TENSOR_NHWC | 张量按 [N, H, W, C] 存储,在某些 GPU 架构(如 Tensor Core)上可能更快 |
| CUDNN_TENSOR_NCHW_VECT_C | 向量化通道存储格式,一般用于卷积加速,通道数必须是 4 的倍数 |
cudnnDataType_t 是\(\text{cuDNN}\)库中用于指定张量数据类型的枚举类型,它定义了深度学习操作中支持的数据格式,包括精度和存储方式。
typedef enum {
CUDNN_DATA_FLOAT = 0, // 32-bit float
CUDNN_DATA_DOUBLE = 1, // 64-bit double
CUDNN_DATA_HALF = 2, // 16-bit float (FP16)
CUDNN_DATA_INT8 = 3, // 8-bit integer
CUDNN_DATA_INT32 = 4, // 32-bit integer
CUDNN_DATA_INT8x4 = 5, // 8-bit integer pack of 4
CUDNN_DATA_UINT8 = 6, // 8-bit unsigned integer
CUDNN_DATA_INT8x32 = 7 // 8-bit integer pack of 32
} cudnnDataType_t;
其枚举值的含义如下:
| cudnnDataType_t枚举值 | 数据类型 | 参数说明 |
|---|---|---|
| CUDNN_DATA_FLOAT | float32 | 最常用数据类型,精度高 |
| CUDNN_DATA_DOUBLE | float64 | 双精度,GPU 上使用少,性能低 |
| CUDNN_DATA_HALF | float16 | 半精度,适合 Tensor Core 加速 |
| CUDNN_DATA_INT8 | int8 | 量化神经网络使用 |
| CUDNN_DATA_INT32 | int32 | 常用于累加或输出量化后的结果 |
| CUDNN_DATA_INT8x4 / INT8x32 | int8 pack | 8-bit 数据打包存储,加速卷积 |
| CUDNN_DATA_UINT8 | uint8 | 无符号 8-bit 数据 |
C.卷积描述子(cudnnConvolutionDescriptor_t)
卷积描述符(cudaConvolutionDescriptor_t) 用于描述卷积张量操作的参数,如填充大小(pad_h,pad_w),步长(stride_h,stride_w),dilation(用于空洞卷积)、卷积模式(如CUDNN_CROSS_CORRELATION表示互相关模式,在深度学习中通常等同于卷积操作)。由cudnnCreateConvolutionDescriptor()创建、cudnnSetConvolution2dDescriptor()设置属性,cudnnDestroyConvolutionDescriptor() 销毁。
cudnnStatus_t cudnnCreateConvolutionDescriptor(cudnnConvolutionDescriptor_t *convDesc);
cudnnStatus_t cudnnSetConvolution2dDescriptor(
cudnnConvolutionDescriptor_t convDesc,
int pad_h, int pad_w, // 填充
int u, int v, // 步长 (stride)
int dilation_h, int dilation_w,
cudnnConvolutionMode_t mode, // CUDNN_CONVOLUTION or CUDNN_CROSS_CORRELATION
cudnnDataType_t computeType // 计算精度
);
cudnnStatus_t cudnnDestroyConvolutionDescriptor(cudnnConvolutionDescriptor_t convDesc);
cudnnConvolutionMode_t 是cuDNN库中用于指定卷积操作类型的枚举类型,主要定义两种模式:标准卷积和互相关。
typedef enum {
CUDNN_CONVOLUTION = 0, // 标准卷积
CUDNN_CROSS_CORRELATION = 1 // 互相关 (默认)
} cudnnConvolutionMode_t;
其枚举值含义:
| 枚举值 | 含义 | 说明 |
|---|---|---|
| CUDNN_CONVOLUTION | 卷积 | 卷积核会翻转 180° 再做滑动窗口求和。数学上是标准卷积公式。 |
| CUDNN_CROSS_CORRELATION | 互相关 | 卷积核不翻转,直接在输入上做滑动窗口求和。cuDNN 默认使用这个模式。 |
注意:大多数深度学习框架(PyTorch、TensorFlow)默认使用互相关,所以\(\text{cuDNN}\)也默认使用CUDNN_CROSS_CORRELATION 。
过滤器(卷积核)描述子(cudnnFilterDescriptor_t) 是cuDNN中用于描述卷积滤波器(权重)张量的核心数据类型。它是cuDNN卷积操作中不同或缺的组成部分。由cudnnCreateConvolutionDescriptor()来创建,cudnnSetFilter4dDescriptor()来设置卷积算子,cudnnDestroyFilterDescriptor() 销毁;
cudnnStatus_t cudnnCreateFilterDescriptor(cudnnFilterDescriptor_t *filterDesc);
cudnnStatus_t cudnnSetFilter4dDescriptor(
cudnnFilterDescriptor_t filterDesc,
cudnnDataType_t dataType, cudnnTensorFormat_t format,
int k, int c, int h, int w
);
cudnnStatus_t cudnnDestroyFilterDescriptor(cudnnFilterDescriptor_t filterDesc);
D. 卷积操作算子(cudnnConvolutionForward)
计算卷积算子的输出尺寸;
cudnnStatus_t cudnnGetConvolution2dForwardOutputDim(
const cudnnConvolutionDescriptor_t convDesc, // 卷积描述子
const cudnnTensorDescriptor_t inputTensorDesc, // 输入张量描述子
const cudnnFilterDescriptor_t filterDesc, // 卷积核描述子
int* n, int* c, int* h, int* w // 输出张量尺寸
);
| 参数 | 类型 | 说明 |
|---|---|---|
| convDesc | cudnnConvolutionDescriptor_t |
卷积参数描述子,包括 padding、stride、dilation、卷积模式等 |
| inputTensorDesc | cudnnTensorDescriptor_t |
输入张量描述子,包含 N、C、H、W 以及数据类型、存储格式 |
| filterDesc | cudnnFilterDescriptor_t |
卷积核描述子,包含 K、C、R、S |
| n, c, h, w | int* |
输出张量的 N、C、H、W,由函数计算后返回 |
对于输入尺寸\(H_{in}\times W_{in}\) 和卷积核\(R\times{S}\):
选择卷积算法:
调用 cuDNN 核心 API,在 GPU 上执行 3D 卷积计算,结果存储到 GPU 输出内存 d_output 中。
float alpha = 1.0f, beta = 0.0f;
CHECK_CUDNN(cudnnConvolutionForward(cudnn, &alpha, inputDesc, d_input, filterDesc, d_filter,convDesc, perfResults.algo, d_workspace, workspace_bytes, &beta, outputDesc, d_output));
alpha=1.0f、beta=0.0f:卷积计算的系数,对应公式:output=α×conv(input,kernel)+β×output
cudnnConvolutionForward:cuDNN 前向卷积核心 API,参数依次是:
cuDNN 句柄 → 系数 α → 输入描述符 → GPU 输入数据 → 卷积核描述符 → GPU 卷积核 → 卷积规则描述符 → 最优算法 → 工作空间 → 工作空间大小 → 系数 β → 输出描述符 → GPU 输出数据。
2.卷积案例
在 CUDA 上通过 cuDNN 进行 3D 卷积计算一般需要做以下几步工作:
- 初始化 cuDNN 句柄;创建 cuDNN 的核心句柄,作为后续所有 cuDNN 操作的基础(
cudnnCreate()接口)。 - 构造输入张量描述符;定义输入张量的维度(NCHW 格式,包含批次、通道、深度 / 高度 / 宽度)、数据类型和内存步长,步长通过自定义
compute_stride函数计算。涉及cudnnCreateTensorDescriptor()、cudnnSetTensorNdDescriptor()接口。 - 构造卷积核(滤波器)描述符;定义卷积核的维度(输出通道、输入通道、核深度 / 高度 / 宽度)、数据类型和存储格式。涉及
cudnnCreateFilterDescriptor()、cudnnSetFilterNdDescriptor()接口。 - 计算 SAME 填充并构造卷积描述符;
- 先计算 SAME 卷积模式下的填充值,再设置卷积的步长、膨胀系数,指定卷积计算模式(如互相关)。
- 涉及
cudnnCreateConvolutionDescriptor()、cudnnSetConvolutionNdDescriptor()接口。
- 计算输出张量尺寸并构造输出张量描述符;通过 cuDNN 接口自动计算卷积输出的张量尺寸,再定义输出张量的维度、数据类型和内存步长。涉及
cudnnGetConvolutionNdForwardOutputDim()、cudnnCreateTensorDescriptor()、cudnnSetTensorNdDescriptor()接口。 - 选取最优卷积算法并计算工作空间大小;
- 获取 cuDNN 推荐的最优前向卷积算法,同时计算该算法所需的 GPU 工作空间大小。
- 涉及
cudnnGetConvolutionForwardAlgorithm_v7()、cudnnGetConvolutionForwardWorkspaceSize()接口。
- 分配 GPU 设备内存并拷贝数据;在设备端为输入、卷积核、输出张量及工作空间分配内存,将主机端的输入数据和卷积核数据拷贝到设备端(
cudaMalloc、cudaMemcpy(cudaMemcpyHostToDevice)接口)。 - 执行 3D 卷积前向计算;调用 cuDNN 卷积前向计算接口,使用选定的最优算法完成 3D 卷积核心计算。涉及
cudnnConvolutionForward()接口。 - 拷贝输出数据到主机端;将设备端完成卷积计算后的输出数据拷贝回主机端(
cudaMemcpy(cudaMemcpyDeviceToHost)接口)。 - 资源释放;若不再需要相关资源,释放设备端分配的内存,销毁 cuDNN 的各类描述符(张量、卷积核、卷积)和 cuDNN 句柄。涉及
cudaFree()、cudnnDestroyTensorDescriptor()、cudnnDestroyFilterDescriptor()、cudnnDestroyConvolutionDescriptor()、cudnnDestroy()接口。
void compute_stride(const int* size, int* stride) {
for (int i = 4; i >= 0; i--)
stride[i] = (i == 4) ? 1 : size[i + 1] * stride[i + 1];
}
void cudnn_conv_3d(const float* input, const float* kernel,
float* output, const int input_dims[3],
const int kernel_dims[3]) {
// -------------------------- 1. 初始化CUDNN句柄 --------------------------
cudnnHandle_t cudnn;
CHECK_CUDNN(cudnnCreate(&cudnn));
// -------------------------- 2. 构造输入张量描述符 --------------------------
int inputDims[5] = { 1, 1, input_dims[0], input_dims[1], input_dims[2] }; // N=1, C=1, D/H/W
int input_stride[5];
compute_stride(inputDims, input_stride);
cudnnTensorDescriptor_t inputDesc;
CHECK_CUDNN(cudnnCreateTensorDescriptor(&inputDesc));
CHECK_CUDNN(cudnnSetTensorNdDescriptor(inputDesc, CUDNN_DATA_FLOAT, 5, inputDims, input_stride));
// -------------------------- 3. 构造卷积核描述符 --------------------------
int filterDims[5] = { 1, 1, kernel_dims[0], kernel_dims[1], kernel_dims[2] }; // 输出通道=1, 输入通道=1, 核尺寸
cudnnFilterDescriptor_t filterDesc;
CHECK_CUDNN(cudnnCreateFilterDescriptor(&filterDesc));
CHECK_CUDNN(cudnnSetFilterNdDescriptor(filterDesc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, 5, filterDims));
// -------------------------- 4. 计算SAME填充并构造卷积描述符 --------------------------
int conmv_padA[3];
for (int i = 0; i < 3; i++) {
conmv_padA[i] = (kernel_dims[i] - 1) / 2; // 计算SAME填充
}
int conv_filterStrideA[3] = { 1, 1, 1 }; // 步长=1
int conv_dilationA[3] = { 1, 1, 1 }; // 膨胀=1
cudnnConvolutionDescriptor_t convDesc;
CHECK_CUDNN(cudnnCreateConvolutionDescriptor(&convDesc));
CHECK_CUDNN(cudnnSetConvolutionNdDescriptor(convDesc, 3, conmv_padA, conv_filterStrideA,
conv_dilationA, CUDNN_CROSS_CORRELATION, CUDNN_DATA_FLOAT));
// -------------------------- 5. 计算输出张量尺寸并构造描述符 --------------------------
int outputDims[5];
CHECK_CUDNN(cudnnGetConvolutionNdForwardOutputDim(convDesc, inputDesc, filterDesc, 5, outputDims));
int output_stride[5];
compute_stride(outputDims, output_stride);
cudnnTensorDescriptor_t outputDesc;
CHECK_CUDNN(cudnnCreateTensorDescriptor(&outputDesc));
CHECK_CUDNN(cudnnSetTensorNdDescriptor(outputDesc, CUDNN_DATA_FLOAT, 5, outputDims, output_stride));
// -------------------------- 6. 选取最优卷积算法 --------------------------
size_t in_bytes, out_bytes;
CHECK_CUDNN(cudnnGetTensorSizeInBytes(inputDesc, &in_bytes));
CHECK_CUDNN(cudnnGetTensorSizeInBytes(outputDesc, &out_bytes));
size_t filt_bytes = 1;
for (int i = 0; i < 5; i++) filt_bytes *= filterDims[i];
filt_bytes *= sizeof(float);
// 获取最优卷积算法
int returnedAlgoCount = 0;
cudnnConvolutionFwdAlgoPerf_t perfResults;
CHECK_CUDNN(cudnnGetConvolutionForwardAlgorithm_v7(cudnn, inputDesc, filterDesc, convDesc, outputDesc,
1, &returnedAlgoCount, &perfResults));
// 计算工作空间大小
size_t workspace_bytes = 0;
CHECK_CUDNN(cudnnGetConvolutionForwardWorkspaceSize(cudnn, inputDesc, filterDesc, convDesc, outputDesc,
perfResults.algo, &workspace_bytes));
// -------------------------- 7. 分配设备内存并拷贝数据 --------------------------
float* d_input = nullptr, * d_filter = nullptr, * d_output = nullptr;
void* d_workspace = nullptr;
CHECK_CUDA(cudaMalloc((void**)&d_input, in_bytes));
CHECK_CUDA(cudaMalloc((void**)&d_filter, filt_bytes));
CHECK_CUDA(cudaMalloc((void**)&d_output, out_bytes));
if (workspace_bytes > 0) {
CHECK_CUDA(cudaMalloc((void**)&d_workspace, workspace_bytes));
}
// 主机到设备拷贝
CHECK_CUDA(cudaMemcpy(d_input, input, in_bytes, cudaMemcpyHostToDevice));
CHECK_CUDA(cudaMemcpy(d_filter, kernel, filt_bytes, cudaMemcpyHostToDevice));
// -------------------------- 8. 执行卷积计算 --------------------------
float alpha = 1.0f, beta = 0.0f;
CHECK_CUDNN(cudnnConvolutionForward(cudnn, &alpha, inputDesc, d_input, filterDesc, d_filter,
convDesc, perfResults.algo, d_workspace, workspace_bytes,
&beta, outputDesc, d_output));
// -------------------------- 9. 设备到主机拷贝输出 --------------------------
CHECK_CUDA(cudaMemcpy(output, d_output, out_bytes, cudaMemcpyDeviceToHost));
// -------------------------- 10. 资源释放 --------------------------
CHECK_CUDA(cudaFree(d_input));
CHECK_CUDA(cudaFree(d_filter));
CHECK_CUDA(cudaFree(d_output));
CHECK_CUDA(cudaFree(d_workspace));
CHECK_CUDNN(cudnnDestroyTensorDescriptor(outputDesc));
CHECK_CUDNN(cudnnDestroyConvolutionDescriptor(convDesc));
CHECK_CUDNN(cudnnDestroyFilterDescriptor(filterDesc));
CHECK_CUDNN(cudnnDestroyTensorDescriptor(inputDesc));
CHECK_CUDNN(cudnnDestroy(cudnn));
}
#define _CRT_SECURE_NO_WARNINGS
#include "rscudaalgo.h"
#include <cstdio>
#include <cstdlib>
#include <chrono>
#include <cmath>
#include <vector>
#include "fundamental.h"
#include "segy.h"
#include "alloc.h"
#include "qdebug.h"
//测试三维卷积
void test_conv3d() {
const char* filename_input = "smallCube.sgy"; //输入文件名
const char* filename_output = "conv_small.sgy"; //输出文件名
FILE* fp_input = nullptr; //输入文件的文件指针
FILE* fp_output = nullptr; //输出文件的文件指针
bhed fileheader; //定义文件头
segy* traceheader_array = nullptr; //道头数组指针
float* datacube_input = nullptr; //输入地震数据的三维指针
float* datacube_output = nullptr; //输出地震数据的三维指针
unsigned int size_fileheader = sizeof(fileheader);
unsigned int size_traceheader = sizeof(segy);
unsigned int nline = 0; //三维地震数据的测线数
unsigned int ncdp = 0; //三维地震数据的CDP数
unsigned int nt = 0; //三维地震数据的时间采样数
long long ntrace = 0;
long long size_inputcube = 0;
long long size_trace = 0;
nline = 100;
ncdp = 500;
fp_input = fopen(filename_input, "rb");
if (fp_input == nullptr) {
printf("Cannot open this input file!!!\n");
qDebug() << "Cannot open this input file!!!\n";
system("pause");
return;
}//end if(fp_input==nullptr)
fp_output = fopen(filename_output, "wb");
if (fp_output == nullptr) {
printf("Cannot open\create this output file!!!\n");
qDebug() << "Cannot open\create this output file!!!\n";
system("pause");
return;
}//end if(fp_output==nullptr)
/*判断地震数据是否为规则的*/
fread(&fileheader, size_fileheader, 1, fp_input);
nt = fileheader.hns;
size_trace = size_traceheader + nt * sizeof(float);
_fseeki64(fp_input, 0, SEEK_END);
size_inputcube = _ftelli64(fp_input);
ntrace = (size_inputcube - size_fileheader) / (size_trace);
_fseeki64(fp_input, size_fileheader, SEEK_SET);
if (ntrace != (nline * ncdp)) {
printf("Cube is not regularized!!!\n");
return;
}
/*读取规则的地震数据体及处理,输出*/
int input_dims[3] = { nline, ncdp, nt };
int nAllSize = input_dims[0] * input_dims[1] * input_dims[2];
datacube_input = new float[nAllSize];
memset(datacube_input, 0, nAllSize * sizeof(float));
traceheader_array = (segy*)calloc(nline * ncdp, size_traceheader);
datacube_output = new float[nAllSize];
memset(datacube_output, 0, nline * ncdp * nt * sizeof(float));
for (int iline = 0; iline < nline; iline++) { //读取三维地震属性体
for (int icdp = 0; icdp < ncdp; icdp++) {
int nIndex = (iline * ncdp + icdp) * nt;
//printf("iline=%d,icdp=%d,nIndex=%d\n", iline, icdp, nIndex);
fread(&traceheader_array[iline * ncdp + icdp], size_traceheader, 1, fp_input);
fread(&datacube_input[nIndex], nt * sizeof(float), 1, fp_input);
}//end for(int icdp = 0; icdp < ncdp;icdp++)
}//end for(int iline = 0; iline < nline; iline++)
int cube2_dims[3] = { 21,21,21 };
int nCube2Size = cube2_dims[0] * cube2_dims[1] * cube2_dims[2];
float* pCube2 = new float[nCube2Size];
memset(pCube2, 0, sizeof(float) * nCube2Size);
getCube2(cube2_dims[0], cube2_dims[1], cube2_dims[2], pCube2);
//开始计时
clock_t start_clock, end_clock;
start_clock = clock();
// 初始化输入数据(主机到设备)
// -------------------------- 8. 执行卷积并计时 --------------------------
cudnn_conv_3d(datacube_input, pCube2, datacube_output, input_dims, cube2_dims);
//结束计时
end_clock = clock();
double endtime = (double)(end_clock - start_clock) / CLOCKS_PER_SEC;
std::cout << "Total time:" << endtime << " s" << std::endl; //ms为单位
qDebug() << "Total time:" << endtime << " s" << endl;
fwrite(&fileheader, size_fileheader, 1, fp_output);
for (int iline = 0; iline < nline; iline++) { //写出三维地震属性体
for (int icdp = 0; icdp < ncdp; icdp++) {
int nIndex = (iline * ncdp + icdp) * nt;
fwrite(&traceheader_array[iline * ncdp + icdp], size_traceheader, 1, fp_output);
fwrite(&datacube_output[nIndex], nt * sizeof(float), 1, fp_output);
}//end for(int icdp = 0; icdp < ncdp;icdp++)
}//end for(int iline = 0; iline < nline; iline++)
// -------------------------- 9. 释放资源 --------------------------
fclose(fp_input);
fclose(fp_output);
delete[]datacube_input;
delete[]datacube_output;
free(traceheader_array);
}
int main() {
test_conv3d();
return 0;
}

浙公网安备 33010602011771号