DeepSeek协助优化-GTX750Ti文物显卡0.65秒卷完400MB float 音频512阶时域FIR

文章目录

  • 1. 学习目的
  • 2. 阶段成果
    • 2.1 NVVP 性能探查
    • 2.2 测试编译环境
    • 2.3 测试样例
  • 3 学习过程
    • 3.1 提问DeepSeek
    • 3.2 最终代码
  • 4. 体会

1. 学习目的

最近在学习cuda,准备给我的taskBus SDR添加CUDA的模块支持,以便可以用PC机压榨山寨 B210那56M的带宽。
因为年纪比较大了,所以不打算看书了,直接DeepSeek安排上,结果发现学习效率杠杠的!

2. 阶段成果

我用自己的文物显卡 NVIDIA GeForce GTX 750 Ti (Compute Capability 5.0),只有2G的显存,1G的主频,做时域512点fir卷积,竟然只需0.65秒就跑完100M float数据(411MB文件大小)。

关键技术是使用了三个线程进行读写处理分离。

2.1 NVVP 性能探查

使用NVVP,可以看到GPU的占空比还是很紧凑。
NVCCASYNC主要时间花在了fir_kernel,这是我们希望的。同时,部分时间在进行GPU内部的memCpy,这也是值得进一步优化的方向。

由于磁盘IO完全和CPU计算独立,所以两波计算之间几乎没有空闲。

2.2 测试编译环境

Manjaro 20250321,CUDA12

Intel Core i7 6500K @ 4.0GHz

编译命令:

nvcc -arch=sm_50 cuda_filter.cu -o cuda_filter

注意要使用 -arch=sm_50 选择老的计算兼容选项,否则CUDA启动后会报错。

2.3 测试样例

测试样例是用 AudaCity录制的音频,411MB大小,100MB左右的float32样点(单声道)

对比
滤波器对数据(轨道1)做512点的平均(192KHz采样率下,大概是2秒多)。

3 学习过程

3.1 提问DeepSeek

提问:

使用C语言调用CUDA实现一个连续FIR滤波算法。用户使用该算法的步骤是:

  1. 打开文件 test.pcm。
  2. 循环每次读取一定个数(N个)float值,直到文件尾部。
  3. 对每次读取的float值,调用 push_and_filter() 方法。该方法是你需要着重考虑设计、实现的,作用是接着上次的状态,继续执行连续滤波。滤波器是一个提前定义好的数组,有M个元素,float类型,M目前设置合理范围是16-2048。
  4. 每次滤波的输出,存储在 out.pcm,连续写入。
  5. 滤波缓存一开始为全0,尾部也全部清零后结束。
    请根据CUDA的特性,分析并设计每次读取的N的个数,让计算尽可能快,并给出全部代码。

回答:

#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>#define MAX_FILTER_LENGTH 2048
#define OPTIMAL_N 4096  // 根据GPU架构优化的读取块大小
#define BLOCK_SIZE 256   // CUDA线程块大小typedef struct {float* buffer;int size;
} CircularBuffer;__constant__ float d_filter[MAX_FILTER_LENGTH];  // 滤波器系数存储在常量内存// CUDA核函数,使用共享内存优化
__global__ void fir_kernel(const float* input, float* output, int N, int M) {extern __shared__ float s_window[];const int tid = threadIdx.x;const int bid = blockIdx.x;const int gid = bid * blockDim.x + tid;// 每个块加载自己需要的输入窗口const int window_size = blockDim.x + MAX_FILTER_LENGTH - 1;const int input_offset = bid * blockDim.x;// 协作加载数据到共享内存for(int i = tid; i < window_size; i += blockDim.x) {const int load_pos = input_offset + i;s_window[i] = (load_pos < N + M - 1) ? input[load_pos] : 0.0f;}__syncthreads();if(gid < N) {  // 确保不越界float sum = 0.0f;for(int j = 0; j < M; j++) {sum += s_window[tid + j] * d_filter[j];}output[gid] = sum;}
}void push_and_filter(CircularBuffer* state, const float* input, int input_len,float** output, int* output_len, int M, cudaStream_t stream) {// 准备扩展输入缓冲区(历史数据 + 新输入)const int total_len = state->size + input_len;float* extended_input = (float*)malloc(total_len * sizeof(float));// 合并历史数据和当前输入memcpy(extended_input, state->buffer, state->size * sizeof(float));memcpy(extended_input + state->size, input, input_len * sizeof(float));// 准备设备内存float *d_input, *d_output;const int output_size = input_len;cudaMalloc(&d_input, total_len * sizeof(float));cudaMalloc(&d_output, output_size * sizeof(float));// 异步内存拷贝和计算cudaMemcpyAsync(d_input, extended_input, total_len * sizeof(float),cudaMemcpyHostToDevice, stream);const dim3 grid_dim((output_size + BLOCK_SIZE - 1) / BLOCK_SIZE);const dim3 block_dim(BLOCK_SIZE);const size_t shared_mem = (BLOCK_SIZE + MAX_FILTER_LENGTH - 1) * sizeof(float);fir_kernel<<<grid_dim, block_dim, shared_mem, stream>>>(d_input, d_output, output_size, M);// 回收历史数据(保留最后M-1个样本)free(state->buffer);state->size = M - 1;state->buffer = (float*)malloc(state->size * sizeof(float));memcpy(state->buffer, extended_input + input_len, state->size * sizeof(float));// 异步获取结果*output = (float*)malloc(output_size * sizeof(float));cudaMemcpyAsync(*output, d_output, output_size * sizeof(float),cudaMemcpyDeviceToHost, stream);cudaStreamSynchronize(stream);*output_len = output_size;free(extended_input);cudaFree(d_input);cudaFree(d_output);
}int main() {// 初始化滤波器系数(示例值)const int M = 512;float h_filter[M];for(int i = 0; i < M; i++) h_filter[i] = 1.0f/M;cudaMemcpyToSymbol(d_filter, h_filter, M * sizeof(float));// 创建CUDA流和状态缓冲区cudaStream_t stream;cudaStreamCreate(&stream);CircularBuffer state = {NULL, 0};FILE *fin = fopen("test.pcm", "rb");FILE *fout = fopen("out.pcm", "wb");float *input_buf = (float*)malloc(OPTIMAL_N * sizeof(float));while(1) {const size_t read_cnt = fread(input_buf, sizeof(float), OPTIMAL_N, fin);if(read_cnt == 0) break;float* output_buf;int output_len;push_and_filter(&state, input_buf, read_cnt, &output_buf, &output_len, M, stream);fwrite(output_buf, sizeof(float), output_len, fout);free(output_buf);} // 清理资源free(input_buf);free(flush_buf);free(state.buffer);fclose(fin);fclose(fout);cudaStreamDestroy(stream);return 0;
}

而后,利用多线程修改,多次互动后,得到最终代码:

3.2 最终代码

#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>
#include <atomic>
#include <chrono>
#include <thread>
#define MAX_FILTER_LENGTH 2048
#define OPTIMAL_N 1024*1024  // Optimal read block size based on GPU architecture
#define BLOCK_SIZE 256       // CUDA thread block size
#define NUM_STREAMS 4
#define CHECK_CUDA(call) { \cudaError_t err = call; \if (err != cudaSuccess) { \fprintf(stderr, "CUDA error at %s:%d: %s\n", __FILE__, __LINE__, cudaGetErrorString(err)); \exit(EXIT_FAILURE); \} \
}typedef struct {float* buffer;int size;int max_size;
} CircularBuffer;__constant__ float d_filter[MAX_FILTER_LENGTH];  // Filter coefficients in constant memory// CUDA kernel with shared memory optimization
__global__ void fir_kernel(const float* input, float* output, int N, int M) {extern __shared__ float s_window[];const int tid = threadIdx.x;const int bid = blockIdx.x;const int gid = bid * blockDim.x + tid;// Each block loads its needed input windowconst int window_size = blockDim.x + MAX_FILTER_LENGTH - 1;const int input_offset = bid * blockDim.x;// Cooperative data loading into shared memoryfor(int i = tid; i < window_size; i += blockDim.x) {const int load_pos = input_offset + i;s_window[i] = (load_pos < N + M - 1) ? input[load_pos] : 0.0f;}__syncthreads();if(gid < N) {  // Ensure we don't go out of boundsfloat sum = 0.0f;for(int j = 0; j < M; j++) {sum += s_window[tid + j] * d_filter[j];}output[gid] = sum;}
}
//用于拼接前序状态的输入缓存
int mem_ext_input_size = 0;
float* mem_ext_input = 0;
//用于CUDA设备侧的输入输出缓存
float *d_input = 0, *d_output = 0;
int d_inputlen = 0, d_outputlen = 0;std::atomic<bool> read_finished(false),deal_finished(false);
std::atomic<long long> read_pos(0);
std::atomic<long long> deal_pos(0);
std::atomic<long long> write_pos(0);//连续滤波函数
void push_and_filter(CircularBuffer* state, const float* input, int input_len,float* output, int * outlen, int M, cudaStream_t stream) {// Prepare extended input buffer (history data + new input)int ext_input_len = state->size + input_len;if (mem_ext_input_size < ext_input_len) {if (mem_ext_input)free(mem_ext_input);mem_ext_input = (float*)malloc(ext_input_len * sizeof(float));mem_ext_input_size = ext_input_len;}// Combine history data with current inputmemcpy(mem_ext_input, state->buffer, state->size * sizeof(float));memcpy(mem_ext_input + state->size, input, input_len * sizeof(float));// Prepare device memoryconst int output_size = input_len;if (d_inputlen < ext_input_len) {if (d_input)cudaFree(d_input);CHECK_CUDA(cudaMalloc(&d_input, ext_input_len * sizeof(float)));d_inputlen = ext_input_len;}if (d_outputlen < output_size) {if (d_output)cudaFree(d_output);CHECK_CUDA(cudaMalloc(&d_output, output_size * sizeof(float)));d_outputlen = output_size;}// Synchronous memory copy//CHECK_CUDA(cudaMemcpy(d_input, mem_ext_input, ext_input_len * sizeof(float), cudaMemcpyHostToDevice));// Async memory operations with streamCHECK_CUDA(cudaMemcpyAsync(d_input, mem_ext_input, ext_input_len * sizeof(float), cudaMemcpyHostToDevice, stream));const dim3 grid_dim((output_size + BLOCK_SIZE - 1) / BLOCK_SIZE);const dim3 block_dim(BLOCK_SIZE);const size_t shared_mem = (BLOCK_SIZE + MAX_FILTER_LENGTH - 1) * sizeof(float);// Kernel launch with default streamfir_kernel<<<grid_dim, block_dim, shared_mem ,stream>>>(d_input, d_output, output_size, M);// Recycle history data (keep last M-1 samples)state->size = M - 1;if (state->max_size < state->size){if (state->buffer )free (state->buffer );state->buffer = (float*)malloc(state->size * sizeof(float));state->max_size = state->size;}memcpy(state->buffer, mem_ext_input + input_len, state->size * sizeof(float));//CHECK_CUDA(cudaMemcpy(output, d_output, output_size * sizeof(float), cudaMemcpyDeviceToHost));CHECK_CUDA(cudaMemcpyAsync(output, d_output, output_size * sizeof(float), cudaMemcpyDeviceToHost, stream));*outlen = output_size;CHECK_CUDA(cudaDeviceSynchronize());return;
}int main() {// Initialize filter coefficients (example values)const int M = 512;float h_filter[M];for(int i = 0; i < M; i++) h_filter[i] = 1.0f/M;CHECK_CUDA(cudaMemcpyToSymbol(d_filter, h_filter, M * sizeof(float)));// Initialize state bufferCircularBuffer state = {NULL, 0 , 0};const int buf_stage = 8;float (*input_buf)[OPTIMAL_N] = new float [buf_stage][OPTIMAL_N];float (*output_buf)[OPTIMAL_N] = new float [buf_stage][OPTIMAL_N];size_t (*input_len)  = new size_t [buf_stage];int (*output_len) = new int [buf_stage];//Read threadstd::thread read_thread([&]()->void{FILE *fin = fopen("test.pcm", "rb");while(!read_finished) {if (deal_pos + buf_stage - 2 <= read_pos){//Sleepstd::chrono::milliseconds dura(1);std::this_thread::sleep_for(dura);continue;}const long long pos = read_pos % buf_stage;//Readinput_len[pos] = fread(input_buf[pos], sizeof(float), OPTIMAL_N, fin);if(input_len[pos] < OPTIMAL_N) read_finished = true;if(++read_pos % 10 ==0)	fprintf(stderr,"ReadPos = %lld\n",(long long)read_pos);}		fclose(fin);});//write threadstd::thread write_thread([&]()->void{FILE *fout = fopen("out.pcm", "wb");while((!deal_finished) ||  write_pos < deal_pos) {if (write_pos >= deal_pos){//Sleepstd::chrono::milliseconds dura(1);std::this_thread::sleep_for(dura);continue;}const long long pos = write_pos % buf_stage;if (output_len[pos])fwrite(output_buf[pos], sizeof(float), output_len[pos], fout);if (++write_pos % 10 ==0)fprintf(stderr,"WritePos = %lld\n",(long long)write_pos);}		fclose(fout);});cudaStream_t streams[buf_stage];for (int i = 0; i < buf_stage; i++) {cudaStreamCreate(&streams[i]);}//Deal in main Threadwhile ((!read_finished) || deal_pos < read_pos ){if (deal_pos >= read_pos){//Sleepstd::chrono::milliseconds dura(1);std::this_thread::sleep_for(dura);continue;}const long long pos = deal_pos % buf_stage;cudaStream_t stream = streams[pos % NUM_STREAMS];push_and_filter(&state, input_buf[pos], input_len[pos], output_buf[pos], &(output_len[pos]), M,stream);		if (++deal_pos % 10==0)fprintf(stderr,"DealPos = %lld\n",(long long)deal_pos);}deal_finished = true;read_thread.join();write_thread.join();fprintf(stderr,"Deal Finished.\n");if (state.buffer)   	free(state.buffer);if (mem_ext_input) 	free(mem_ext_input);if (d_input) 			CHECK_CUDA(cudaFree(d_input));if (d_output) 		CHECK_CUDA(cudaFree(d_output));delete []input_buf;delete []output_buf;delete []input_len;delete []output_len;for (int i = 0; i < NUM_STREAMS; i++) {CHECK_CUDA(cudaStreamDestroy(streams[i]));}return 0;
}

4. 体会

从不熟悉CUDA,到使用CUDA完成了性能还乏善可陈的滤波器,用了4个小时。同时,通过询问DeepSeek如何理解stream流式处理、如何使用Nvidia Visual Profiler的优化指南,了解到很多CUDA相关的周边知识。可见,AI已经使得我这个50岁的人都能学到新东西,更别提年轻人了。现在是只有想不到,没有做不到。

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

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

相关文章

RabbitMQ高级特性--TTL和死信队列

目录 1.TTL 1.1设置消息的TTL 1.1.1配置交换机&队列 1.1.2发送消息 1.1.3运行程序观察结果 1.2设置队列的TTL 1.2.1配置队列和交换机的绑定关系 1.2.2发送消息 1.2.3运行程序观察结果 1.3两者区别 2.死信队列 2.1 声名队列和交换机 2.2正常队列绑定死信交换机 …

【JavaEE】UDP数据报套接字编程

目录 网络编程基础 基本概念 发送端和接收端 请求和响应 客户端和服务端 常见的客户端服务端模型 Socket套接字 TCP/UDP特点 Java数据报套接字通信模型(UDP通信) UDP数据报套接字编程 DatagramSocket 1.类定义 2.构造方法 3.核心方法 4.特性说明 DatagramPacke…

Spring Boot 3.4.3 基于 SpringDoc 2 和 Swagger 3 实现项目接口文档管理

在现代企业级应用开发中&#xff0c;前后端分离已成为主流模式&#xff0c;前端负责界面呈现&#xff0c;后端专注提供 RESTful API 接口。然而&#xff0c;接口文档的编写和维护往往是开发过程中的痛点。Spring Boot 3.4.3 结合 SpringDoc 2 和 Swagger 3&#xff0c;为开发者…

构建大语言模型应用:数据准备(第二部分)

本专栏通过检索增强生成&#xff08;RAG&#xff09;应用的视角来学习大语言模型&#xff08;LLM&#xff09;。 本系列文章 简介数据准备&#xff08;本文&#xff09;句子转换器向量数据库搜索与检索大语言模型开源检索增强生成评估大语言模型服务高级检索增强生成 RAG 如上…

Linux 随机数据生成

目录 一. /dev/urandom1.1 dd 命令1.2 head命令1.3 随机字母 二. openssl 命令三. yes命令 一. /dev/urandom ⏹/dev/urandom 是 Linux 和 Unix 系统中的一个特殊文件&#xff0c;它是一个伪随机数生成器&#xff0c;用于提供高吞吐量的随机数据。 1.1 dd 命令 bs1M count10…

项目如何安装本地tgz包并配置局部registry

一、判断包来源是否正确 1. 检查url curl <registry_url>2. 查看包是否存在 npm view <package_name> --registry<registry_url>二、局部registry配置步骤&#xff1a; 1. 全局配置 如果你希望对所有项目生效&#xff0c;可以将这行配置添加到全局.npmr…

QCustomPlot入门

QCustomPlot 是一个基于 Qt 的 C++ 绘图库,专注于高效、美观的 2D 数据可视化。进入QCustomPlot下载页,下载最新的完整包(包含:源码、文档、示例)。 一、核心架构设计 1. 分层架构模型 层级主要组件职责说明用户接口层QCustomPlot 类提供顶层API,管理所有子组件逻辑控制…

C语言快速入门-C语言基础知识

这个c语言入门&#xff0c;目标人群是有代码基础的&#xff0c;例如你之前学过javaSE&#xff0c;看此文章可能是更有帮助&#xff0c;会让你快速掌握他们之间的差异&#xff0c;文章内容大部分都是泛谈&#xff0c;详细的部分我会在之后时间发布&#xff0c;我也在慢慢学习&am…

【商城实战(91)】安全审计与日志管理:为电商平台筑牢安全防线

【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…

信息安全工程师第 1 章

《信息安全工程师教程(第2版)》第一章 一、网络信息安全基本概念与重要性 网络信息安全定义 狭义:保障信息系统的机密性(C)、完整性(I)、可用性(A)——CIA三性。广义:涵盖国家安全、经济安全、社会安全等的“大安全”。法律依据:《网络安全法》定义网络安全为防范攻…

为什么视频文件需要压缩?怎样压缩视频体积即小又清晰?

在日常生活中&#xff0c;无论是为了节省存储空间、便于分享还是提升上传速度&#xff0c;我们常常会遇到需要压缩视频的情况。本文将介绍为什么视频需要压缩&#xff0c;压缩视频的好处与坏处&#xff0c;并教你如何使用简鹿视频格式转换器轻松完成MP4视频文件的压缩。 为什么…

网络空间安全(45)PHP入门学习

一、PHP文件与结构 PHP文件扩展名&#xff1a;PHP文件通常以.php作为扩展名&#xff0c;例如index.php。 PHP代码嵌入&#xff1a;PHP代码可以嵌入到HTML文件中&#xff0c;通常使用<?php ... ?>标签包围PHP代码。短标签<? ... ?>在某些配置下也可以使用&…

深入 OpenPDF:高级 PDF 生成与操作技巧

1 引言 1.1 项目背景 在许多企业级应用中,生成和操作 PDF 文档是一个常见的需求。PDF(Portable Document Format)因其格式统一、易于打印和分发而被广泛使用。本文将介绍如何使用 OpenPDF 库在 Java 项目中生成和操作 PDF 文档。 1.2 技术选型理由 OpenPDF:OpenPDF 是一…

力扣hot100——最长连续序列(哈希unordered_set)

题目链接&#xff1a;最长连续序列 1、错解&#xff1a;数组做哈希表&#xff08;内存超出限制&#xff09; int longestConsecutive(vector<int>& nums) {vector<bool> hash(20000000010, false);for(int i0; i<nums.size();i){hash[1000000000nums[i]]t…

Qt中信号带参传值

在我们的Qt信号中是可以进行参数的传递的&#xff0c;不过格式上与写普通函数不同。 这是头文件中定义一个含参信号和一个含参槽函数 我们再来看它们两个的绑定 。第一行的clicked()和on_btn_clicked()就是普通无参信号和槽的绑定&#xff1b;第二行就是上图中两个带参信号和槽…

CSS3学习教程,从入门到精通, CSS3 列表控制详解语法知识点及案例代码(24)

CSS3 列表控制详解 CSS 列表控制的语法知识点及案例代码的详细说明&#xff0c;包括 list-style-type、list-style-image、list-style-position 和 list-style 的用法。 1. list-style-type 属性 list-style-type 属性用于设置列表项标记的类型。 语法 list-style-type: v…

用Deepseek写扫雷uniapp小游戏

扫雷作为Windows系统自带的经典小游戏&#xff0c;承载了许多人的童年回忆。本文将详细介绍如何使用Uniapp框架从零开始实现一个完整的扫雷游戏&#xff0c;包含核心算法、交互设计和状态管理。无论你是Uniapp初学者还是有一定经验的开发者&#xff0c;都能从本文中获得启发。 …

Dust3r、Mast3r、Fast3r

目录 一.Dust3r 1.简述 2.PointMap与ConfidenceMap 3.模型结构 4.损失函数 5.全局对齐 二.Mast3r 1.简述 2.MASt3R matching 3.MASt3R sfm 匹配与标准点图 BA优化 三.Fast3r 1.简述 2.模型结构 3.损失函数 三维重建是计算机视觉中的一个高层任务&#xff0c;包…

学习不同电脑cpu分类及选购指南

学习不同电脑cpu分类及选购指南 关于电脑cpu 学习不同电脑cpu分类及选购指南一、CPU型号的核心组成与命名规则Intel命名规则:AMD命名规则:代数:具体型号:cpu后缀:Intel常见后缀及其含义:AMD后缀常见后缀及其含义:二、主流品牌CPU的分类与性能差异三、区分CPU型号的实用方…

【身份安全】零信任安全框架梳理(一)

目录 零信任网络安全防护理念一、定义零信任原则 二、零信任实现方式三、零信任的核心机制和思想1. 持续验证&#xff08;Continuous Verification&#xff09;2. 多因素认证&#xff08;MFA&#xff09;与强身份验证3. 细粒度权限控制&#xff08;最小权限原则&#xff09;4. …