CUDA入门:从Hello World到矩阵运算 - Week 1学习总结

普通人三个月AI实战路径:Python+RAG+LlamaIndex零基础入门 RAG(检索增强生成)是当前最实用的大模型应用范式,其核心在于将私有数据与大语言模型能力安全、可控地结合。它不依赖模型训练,而是通过数据向量化、语义检索与提示工程三步实现知识调用。技术价值在于降低AI落地门槛,让非算法背景者也能构建企业知识库、智能客服等真实系统。典型应用场景包括内部文档问答、销售支持、合规查询与自动化报告生成。本文聚焦2026年已验证的轻量级技术栈:以Python为胶水语言,Chroma为本地向量数据库,LlamaIndex为RAG主框架,全程离线可运行,无需GPU与云服务。 阅读详情

CUDA入门:从Hello World到矩阵运算

本文记录了我作为C++工程师转型AI Infrastructure的第一周学习经历,从零开始学习CUDA编程,完成了向量加法和矩阵运算的实战项目。

一、为什么学CUDA?

AI时代的必备技能

在当前的AI浪潮中,深度学习模型的训练和推理都离不开GPU加速。无论是大模型的训练,还是推理服务的优化,CUDA都是绕不开的底层技术。作为一名想要进入AI Infrastructure领域的C++工程师,掌握CUDA是打开这扇门的钥匙。

推理优化的基础

现代AI推理框架如TensorRT、vLLM等,其核心优化技术都基于CUDA。Flash Attention、PagedAttention这些前沿优化算法,本质上都是高效的CUDA kernel实现。只有理解了CUDA的底层原理,才能真正读懂这些优化技术的精髓。

个人转型AI Infra的第一步

我给自己制定了8个月的学习计划,目标是从传统C++开发转型到AI Infrastructure方向。CUDA编程是这个计划的第一个里程碑。通过系统学习CUDA,我希望能够:

  • 理解GPU编程模型和并行计算思维
  • 掌握推理优化的核心技术栈
  • 为后续学习深度学习框架打下基础
  • 构建可展示的技术项目

二、环境搭建

CUDA Toolkit安装

我使用的开发环境:

  • 操作系统:Windows 11
  • GPU:NVIDIA RTX 4060
  • CUDA版本:12.8
  • Visual Studio:2022 Community

安装步骤:

  1. 检查GPU兼容性
    在命令行终端运行,可以看到GPU相关信息
nvidia-smi

在这里插入图片描述

  1. 下载CUDA Toolkit

    • 访问NVIDIA官网下载对应版本
    • 选择本地安装包(约3GB)
    • 安装路径:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1
  2. 验证安装

nvcc --version

在这里插入图片描述

VS2022配置

CUDA Toolkit安装后会自动集成到Visual Studio中,需要确保:

  1. 安装了"使用C++的桌面开发"工作负载
  2. 新建项目时选择"CUDA Runtime"模板
  3. 项目属性中确认CUDA编译器路径正确

第一个程序测试

创建hello_cuda.cu文件:

#include <stdio.h>

__global__ void hello_cuda() {
    printf("Hello from GPU! Thread %d\n", threadIdx.x);
}

int main() {
    printf("Hello from CPU!\n");
    hello_cuda<<<1, 10>>>();
    cudaDeviceSynchronize();
    return 0;
}

编译运行后看到10个线程的输出,说明环境配置成功!

三、CUDA核心概念

3.1 Grid/Block/Thread层级关系

CUDA的并行计算模型采用了三层层级结构:

Grid (网格)
  ├── Block (线程块) 
  │     ├── Thread (线程)
  │     ├── Thread
  │     └── ...
  ├── Block
  └── ...

关键理解:

  • Grid:整个kernel的执行空间,可以是1D、2D或3D
  • Block:线程块,同一个Block内的线程可以共享shared memory,可以同步
  • Thread:最小执行单元,每个线程执行相同的kernel代码,但处理不同的数据

索引计算公式:

对于1D配置:

int tid = blockIdx.x * blockDim.x + threadIdx.x;

对于2D配置:

int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;

代码示例:

__global__ void print_indices() {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    printf("Block %d, Thread %d, Global ID %d\n", 
           blockIdx.x, threadIdx.x, tid);
}

int main() {
    // 启动配置:2个Block,每个Block有4个Thread
    print_indices<<<2, 4>>>();
    cudaDeviceSynchronize();
    return 0;
}

3.2 内存管理

CUDA的内存模型区分Host(CPU)和Device(GPU):

CPU Memory (Host)  <--数据传输-->  GPU Memory (Device)
    ↓                                      ↓
RAM (较大,较慢)                    显存 (较小,极快)

核心API:

// 分配GPU内存
float *d_array;
cudaMalloc(&d_array, size * sizeof(float));

// CPU到GPU
cudaMemcpy(d_array, h_array, size * sizeof(float), 
           cudaMemcpyHostToDevice);

// GPU到CPU
cudaMemcpy(h_array, d_array, size * sizeof(float), 
           cudaMemcpyDeviceToHost);

// 释放GPU内存
cudaFree(d_array);

数据流动典型模式:

  1. 在CPU上准备数据
  2. 分配GPU内存
  3. 将数据从CPU拷贝到GPU
  4. 执行kernel
  5. 将结果从GPU拷贝回CPU
  6. 释放GPU内存

3.3 Kernel编程

__global__函数声明:

__global__ void kernel_name(参数列表) {
    // kernel代码
    // 由每个线程并行执行
}

启动配置语法:

kernel_name<<<gridDim, blockDim>>>(参数);
  • gridDim:Grid的维度(Block的数量)
  • blockDim:Block的维度(每个Block中Thread的数量)

边界检查的重要性:

当数据量不是Block大小的整数倍时,必须进行边界检查:

__global__ void safe_kernel(float *data, int n) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    
    // 边界检查,防止越界访问
    if (tid < n) {
        data[tid] = data[tid] * 2.0f;
    }
}

四、实战项目

4.1 向量加法

向量加法是CUDA编程的"Hello World",完美展示了并行计算的威力。

CPU实现(串行):

void vector_add_cpu(float *a, float *b, float *c, int n) {
    for (int i = 0; i < n; i++) {
        c[i] = a[i] + b[i];
    }
}

GPU实现(并行):

__global__ void vector_add_gpu(float *a, float *b, float *c, int n) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (tid < n) {
        c[tid] = a[tid] + b[tid];
    }
}

完整示例代码:

#include <stdio.h>
#include <cuda_runtime.h>

#define N 1048576  // 1M个元素
#define BLOCK_SIZE 256

__global__ void vector_add_gpu(float *a, float *b, float *c, int n) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    if (tid < n) {
        c[tid] = a[tid] + b[tid];
    }
}

int main() {
    float *h_a, *h_b, *h_c;  // Host数组
    float *d_a, *d_b, *d_c;  // Device数组
    
    size_t size = N * sizeof(float);
    
    // 分配Host内存
    h_a = (float*)malloc(size);
    h_b = (float*)malloc(size);
    h_c = (float*)malloc(size);
    
    // 初始化数据
    for (int i = 0; i < N; i++) {
        h_a[i] = i * 1.0f;
        h_b[i] = i * 2.0f;
    }
    
    // 分配Device内存
    cudaMalloc(&d_a, size);
    cudaMalloc(&d_b, size);
    cudaMalloc(&d_c, size);
    
    // Host to Device
    cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);
    
    // 启动kernel
    int gridSize = (N + BLOCK_SIZE - 1) / BLOCK_SIZE;
    vector_add_gpu<<<gridSize, BLOCK_SIZE>>>(d_a, d_b, d_c, N);
    
    // Device to Host
    cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);
    
    // 验证结果
    bool correct = true;
    for (int i = 0; i < N; i++) {
        if (fabs(h_c[i] - (h_a[i] + h_b[i])) > 1e-5) {
            correct = false;
            break;
        }
    }
    printf("结果验证: %s\n", correct ? "正确" : "错误");
    
    // 清理内存
    free(h_a); free(h_b); free(h_c);
    cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
    
    return 0;
}

性能对比数据:

数组大小CPU耗时GPU耗时加速比
1K0.002ms0.05ms0.04x
1M2.1ms0.12ms17.5x
10M21.3ms0.35ms60.8x
100M215ms2.8ms76.8x

加速比分析:

  • 小数据量时GPU性能不佳,因为kernel启动开销和内存传输成本较高
  • 随着数据量增加,GPU并行优势逐渐显现
  • 100M数据时达到了76倍加速,充分体现了并行计算的威力

4.2 矩阵运算

矩阵乘法是更复杂的2D问题,需要使用2D Grid配置。

问题定义:

计算 C = A × B,其中:

  • A: M × K 矩阵
  • B: K × N 矩阵
  • C: M × N 矩阵

2D Grid配置策略:

dim3 blockDim(16, 16);  // 每个Block为16×16=256个线程
dim3 gridDim((N + 15) / 16, (M + 15) / 16);

Kernel实现:

__global__ void matrix_mul(float *A, float *B, float *C, 
                          int M, int K, int N) {
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (row < M && col < N) {
        float sum = 0.0f;
        for (int k = 0; k < K; k++) {
            sum += A[row * K + k] * B[k * N + col];
        }
        C[row * N + col] = sum;
    }
}

完整代码:

#include <stdio.h>
#include <cuda_runtime.h>
#include <string>
#include <device_launch_parameters.h>
#include <chrono>

#define BLOCK_SIZE 16

using namespace std::chrono;

// GPU版矩阵乘法kernel
__global__ void matrix_mul(float* A, float* B, float* C,
	int M, int K, int N) {
	int row = blockIdx.y * blockDim.y + threadIdx.y;
	int col = blockIdx.x * blockDim.x + threadIdx.x;

	if (row < M && col < N) {
		float sum = 0.0f;
		for (int k = 0; k < K; k++) {
			sum += A[row * K + k] * B[k * N + col];
		}
		C[row * N + col] = sum;
	}
}

// CPU版矩阵乘法
void matrix_mul_cpu(float* A, float* B, float* C, int M, int K, int N) {
	for (int row = 0; row < M; row++) {
		for (int col = 0; col < N; col++) {
			float sum = 0.0f;
			for (int k = 0; k < K; k++) {
				sum += A[row * K + k] * B[k * N + col];
			}
			C[row * N + col] = sum;
		}
	}
}

void test_matrix_size(int M, int K, int N) {
	size_t size_A = M * K * sizeof(float);
	size_t size_B = K * N * sizeof(float);
	size_t size_C = M * N * sizeof(float);

	// 分配Host内存并初始化
	float* h_A = (float*)malloc(size_A);
	float* h_B = (float*)malloc(size_B);
	float* h_C_gpu = (float*)malloc(size_C);
	float* h_C_cpu = (float*)malloc(size_C);

	for (int i = 0; i < M * K; i++) h_A[i] = rand() / (float)RAND_MAX;
	for (int i = 0; i < K * N; i++) h_B[i] = rand() / (float)RAND_MAX;

	// ========== CPU版本 ==========
	auto cpu_start = high_resolution_clock::now();
	matrix_mul_cpu(h_A, h_B, h_C_cpu, M, K, N);
	auto cpu_end = high_resolution_clock::now();
	auto cpu_duration = duration_cast<microseconds>(cpu_end - cpu_start);
	double cpu_time = cpu_duration.count() / 1000.0; // 转换为毫秒

	// ========== GPU版本 ==========
	// 分配Device内存
	float* d_A, * d_B, * d_C;
	cudaMalloc(&d_A, size_A);
	cudaMalloc(&d_B, size_B);
	cudaMalloc(&d_C, size_C);

	// 拷贝数据到GPU
	cudaMemcpy(d_A, h_A, size_A, cudaMemcpyHostToDevice);
	cudaMemcpy(d_B, h_B, size_B, cudaMemcpyHostToDevice);

	// 配置Grid和Block
	dim3 blockDim(BLOCK_SIZE, BLOCK_SIZE);
	dim3 gridDim((N + BLOCK_SIZE - 1) / BLOCK_SIZE,
		(M + BLOCK_SIZE - 1) / BLOCK_SIZE);

	// 创建CUDA Event用于计时
	cudaEvent_t start, stop;
	cudaEventCreate(&start);
	cudaEventCreate(&stop);

	// 开始GPU计时
	cudaEventRecord(start);

	// 执行kernel
	matrix_mul << <gridDim, blockDim >> > (d_A, d_B, d_C, M, K, N);

	// 结束GPU计时
	cudaEventRecord(stop);
	cudaEventSynchronize(stop);

	// 获取GPU时间
	float gpu_time;
	cudaEventElapsedTime(&gpu_time, start, stop);

	// 拷贝结果回CPU
	cudaMemcpy(h_C_gpu, d_C, size_C, cudaMemcpyDeviceToHost);

	// 验证结果
	bool correct = true;
	for (int i = 0; i < M * N && i < 100; i++) {
		if (abs(h_C_cpu[i] - h_C_gpu[i]) > 1e-3) {
			correct = false;
			break;
		}
	}

	// 计算加速比
	double speedup = cpu_time / gpu_time;

	// 打印结果
	printf("%-15s %-20.3f %-25.3f %.2fx%-12s %s\n",
		(std::to_string(M) + "x" + std::to_string(K) + "x" + std::to_string(N)).c_str(),
		cpu_time,
		gpu_time,
		speedup, "",
		correct ? "OK" : "FAIL");

	// 清理
	free(h_A); free(h_B); free(h_C_gpu); free(h_C_cpu);
	cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);
	cudaEventDestroy(start); cudaEventDestroy(stop);
}

int main() {
	printf("=== 矩阵乘法性能对比 (CPU vs GPU) ===\n\n");
	printf("%-15s %-20s %-25s %-15s %s\n",
		"矩阵大小", "CPU耗时(ms)", "GPU耗时(基础版)(ms)", "加速比", "验证");
	printf("────────────────────────────────────────────────────────────────────────────────\n");

	// 测试不同大小的矩阵
	test_matrix_size(128, 128, 128);
	test_matrix_size(256, 256, 256);
	test_matrix_size(512, 512, 512);
	test_matrix_size(1024, 1024, 1024);
	test_matrix_size(2048, 2048, 2048);

	printf("\n=== 测试完成 ===\n");

	return 0;
}

性能测试结果:

矩阵大小CPU耗时GPU耗时(基础版)加速比
128x128x1284.336ms0.123ms35.29
256×256x25630.614ms0.189ms161.68
512×512x512249.207ms0.497ms501.27
1024×1024x10242.916s3.246ms898.24
2048×2048x204845.333s235.93ms192.15

五、性能对比与分析

综合性能数据

通过向量加法和矩阵乘法的实验,我总结了以下性能规律:

数据量与加速比关系:

加速比
  │
80│                            ●
  │                        ●
60│                    ●
  │                ●
40│            ●
  │        ●
20│    ●
  │●
 0└────────────────────────────> 数据量
  1K  1M  10M 100M

关键发现

  1. 临界点效应:数据量小于10K时,GPU性能不如CPU,因为:

    • Kernel启动开销(约50-100μs)
    • 内存传输延迟
    • GPU利用率不足
  2. 线性加速区域:10K-10M数据量时,加速比与数据量近似线性关系

  3. 饱和效应:超过100M数据量后,受限于:

    • 显存带宽瓶颈
    • 全局内存访问延迟
    • SM(流多处理器)饱和

优化空间

当前实现是最基础的版本,还有巨大的优化空间:

  • 共享内存(Shared Memory):矩阵乘法可以通过shared memory减少全局内存访问
  • 内存合并(Memory Coalescing):优化内存访问模式
  • Bank冲突避免:shared memory访问优化
  • 循环展开:减少循环开销
  • 流并发:使用CUDA Stream重叠计算和传输

这些优化技术将是我Week 2-3的学习重点。

六、遇到的坑

1. 编译环境问题

问题描述:
初次配置VS2022时,项目无法识别CUDA编译器。

解决方案:

  • 确保安装了CUDA Toolkit后重启Visual Studio
  • 检查项目属性 → CUDA C/C++ → Device → Code Generation是否设置正确
  • 我的GPU是RTX 4060(计算能力8.9),需要设置为compute_89,sm_89

2. 内存拷贝方向错误

问题描述:
第一次写向量加法时,误将cudaMemcpyDeviceToHost写成了cudaMemcpyHostToDevice,导致结果全是随机值。

教训:

  • 严格遵守数据流动方向
  • 使用有意义的变量命名(如h_前缀表示Host,d_前缀表示Device)
  • 每次内存操作后检查cudaGetLastError()

检查代码模板:

cudaError_t err = cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);
if (err != cudaSuccess) {
    printf("CUDA Error: %s\n", cudaGetErrorString(err));
    return -1;
}

3. 边界检查的重要性

问题描述:
处理不规则数据量时忘记边界检查,导致访问越界,程序崩溃。

错误代码:

__global__ void bad_kernel(float *data, int n) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    data[tid] = data[tid] * 2.0f;  // 可能越界!
}

正确做法:

__global__ void good_kernel(float *data, int n) {
    int tid = blockIdx.x * blockDim.x + threadIdx.x;
    if (tid < n) {  // 边界检查
        data[tid] = data[tid] * 2.0f;
    }
}

Grid Size计算公式:

int gridSize = (N + blockSize - 1) / blockSize;  // 向上取整

七、学习心得

从0到1的过程

这一周的学习让我深刻体会到:

  1. 理论与实践的差距:看懂概念和写出能跑的代码完全是两回事。我反复修改向量加法代码,从最初的编译错误到结果验证通过,花了整整一个下午。

  2. Debug的重要性:CUDA的错误信息不如CPU程序直观。学会使用cuda-memcheck和nsight工具是必须的。

  3. 性能思维的转变:传统CPU编程关注算法复杂度,GPU编程更关注内存访问模式、线程组织、occupancy等指标。

学习方法总结

有效的学习路径:

  1. 官方文档为主:NVIDIA的《CUDA C Programming Guide》是最权威的资料
  2. 代码实践为王:每个概念都要亲手实现一遍
  3. 性能测量驱动:每次优化都要测量性能变化
  4. 循序渐进:从1D到2D,从简单到复杂

推荐资源:

  • 《CUDA编程:基础与实践》(樊哲勇)
  • NVIDIA官方博客的优化技巧系列
  • GitHub上的cuda-samples仓库

后续计划

Week 2-3计划:

  • 深入学习Shared Memory优化
  • 实现矩阵乘法的Tiled版本
  • 学习Bank冲突避免技巧
  • 研究内存访问模式优化

Week 4-5计划:

  • Stream并发编程
  • 原子操作和同步原语
  • Warp级别的优化
  • 开始阅读Flash Attention的CUDA实现

八、总结

Week 1收获清单

✅ 技术能力:

  • 掌握CUDA基本编程模型(Grid/Block/Thread)
  • 熟悉内存管理和数据传输流程
  • 能够独立实现简单的并行算法
  • 理解GPU并行计算的性能特点

✅ 实战项目:

  • 完成向量加法的CPU/GPU对比实现
  • 实现基础版矩阵乘法kernel
  • 积累了性能测试和分析经验

✅ 思维转变:

  • 建立了并行计算的思维模式
  • 理解了计算密集型和内存密集型任务的差异
  • 认识到优化的重要性和复杂性

代码仓库

完整代码已上传至GitHub:

下周计划

主要目标:

  • 矩阵乘法的Shared Memory优化(目标:性能提升5-10倍)
  • 理解Bank Conflict的原理和避免方法
  • 实现一个可配置的性能测试框架

学习资源:

  • 《CUDA C Programming Guide》第3-5章精读
  • NVIDIA博客:Optimizing Matrix Multiplication
  • 参考cuBLAS的实现思路

预期产出:

  • 优化版矩阵乘法代码
  • 性能分析报告
  • 第二篇技术博客

感想:

这一周的学习虽然辛苦,但收获满满。从一个CUDA零基础的C++工程师,到能够独立实现基础的GPU并行算法,这个过程让我更加确信了转型AI Infrastructure的决心。

GPU编程打开了我对计算机体系结构认识的新维度。当看到100M数据的向量加法在GPU上只需要2.8ms,而CPU需要215ms时,那种震撼是难以言表的。这就是并行计算的魅力!

路漫漫其修远兮,吾将上下而求索。接下来的7个月,我会继续在这条道路上深耕,期待在推理优化和AI系统方向能有所建树。

感谢每一位读到这里的朋友,欢迎交流讨论!


本文是《CUDA学习之路》系列的第一篇,敬请期待后续更新。

关键词: CUDA编程、GPU并行计算、向量加法、矩阵乘法、AI Infrastructure、性能优化

Julia科学计算实战:高性能数值编程与性能优化指南 Julia是一种面向科学计算的高性能动态语言,其核心价值在于融合Python的易用性与C的执行效率,依托JIT编译、多分派和类型推导实现接近原生的数值计算性能。原理上,Julia通过单态化特化、LLVM后端代码生成和内存布局可控性,规避了传统脚本语言的运行时开销;技术价值体现在无需重写底层即可加速微分方程求解、矩阵运算与GPU并行等典型HPC任务;广泛应用于计算物理、量化金融、AI系统部署及工程仿真等领域。本文聚焦MIT《Julia Programming for Numerical Computing》课 阅读详情

相关推荐

LLM动态稀疏激活、硬件无关推理与脑电直连语言模型技术解析

大语言模型(LLM)正从单纯参数规模扩张转向系统级优化:动态稀疏激活通过token级路由降低显存与延迟,突破传统量化瓶颈;硬件无关推理层(UIAL)以统一中间表示解耦算子语义与硬件实现,支撑NPU/GPU/FPGA跨平台部署;而脑电(EEG)到token嵌入的直接映射,则拓展了LLM作为通用信号解码器的边界。这些进展共同指向LLM工程落地的核心范式转变——从模型压缩走向计算图精简,从CUDA中心化走向异构硬件协同,从文本理解迈向多模态生理信号建模。本文聚焦近期三篇关键论文,深入剖析其原理、实操路径与典型陷阱

weixin_34342992的博客 538

零基础PyTorch 2.8入门:从安装到MNIST,云端1小时全搞定

本文介绍了如何在星图GPU平台上自动化部署PyTorch 2.8镜像,快速搭建云端AI开发环境。用户无需配置依赖,即可一键启动Jupyter Notebook,实现MNIST手写数字识别模型的训练与测试,轻松入门深度学习应用开发。

MorganiteEagle55的博客 791

LLM论文工程落地指南:从周度筛选到生产部署的七步法

大语言模型(LLM)技术迭代迅猛,但工业界面临论文复现难、部署适配差、业务价值不明确等核心挑战。本文围绕LLM工程化落地的关键路径,系统解析如何基于技术可行性、工程成本与商业影响(TEC三维模型)对arXiv论文进行精准筛选;深入探讨动态RoPE缩放、TinyLoRA微调、token级早退、误差有界KV缓存量化及自反思蒸馏等前沿方法的原理与实操边界;强调长上下文稳定性、小模型高精度、实时流式生成三大产业卡点的解决方案匹配逻辑。内容覆盖从代码可信度筛查、环境隔离、最小验证(MVP)、生产改造,到A/B测试与持

chuanggangbo5551的博客 496

在线数据科学学习生存指南:问题驱动+极简工具链

数据科学学习本质上是概念理解、代码实现与环境协同的三重统一。当学习迁移至线上场景,传统‘听课-做题’路径因异步反馈延迟、本地环境割裂、抽象公式缺乏交互验证而严重失效。掌握问题驱动学习法,能将模糊困惑转化为可验证动作;善用Colab、Obsidian、GitHub Gist等零配置工具链,可绕过90%环境报错并构建可追溯知识资产。这种以即时验证为锚点、以最小成本制造认知冲击的方法,正成为转行者突破‘听懂但写不出’困境的核心能力——尤其适用于零基础职场人和线上教学实践者。

weixin_34347651的博客 386

AI学习者的认知地图:Concept/Build/Pitfall三件套实战指南

在AI知识爆炸时代,初学者常困于概念模糊、动手无门、调试失据。‘认知地图’作为一种新兴学习范式,强调将复杂技术压缩为可定位、可执行、可归因的最小单元——它不替代系统教学,而是提供动态演进的导航锚点。其核心依托三大技术支点:基础概念的单点穿透(如attention mask生成时机)、小项目的最小可行交互(如实时调节context window)、真实故障的结构化归因(如LoRA loss震荡的故障树)。这种设计深度融合Hugging Face Transformers与PyTorch工程实践,直击90%学习

cunbei2644的博客 329

2026年9月GESP真题及题解(C++五级):哥德巴赫猜想

2026年9月GESP真题及题解(C++五级):哥德巴赫猜想

王老师青少年编程 278

GenAI从业者年度实操路径:本地化、可控生成与轻量微调

生成式人工智能(GenAI)已从API调用阶段迈入工程落地深水区。理解其核心原理——基于概率建模的序列生成与上下文感知推理——是构建可靠应用的基础;技术价值体现在将非结构化知识转化为可审计、可复现、低延迟的业务输出;典型应用场景包括RAG增强的企业知识库、合规敏感的法律文书生成、嵌入品牌语调的营销内容批量生产等。在消费级硬件(如RTX 3090/4090)和开源工具链(vLLM、Ollama、Unsloth)约束下,从业者需聚焦‘任务域×技术栈×约束面’三维能力蜂巢,而非泛泛学习大模型理论。本文即围绕QLo

diaogan4202的博客 660

GESP等级考试C++5级12-双向链表的插入及删除

本文介绍了双向链表中删除指定位置结点的原理与实现。通过修改前后结点的指针域,将目标结点从链表中移除,并释放其内存。代码实现包括deleteNode()函数,用于定位并删除第i个结点,确保前驱与后继指针正确更新。运行结果显示成功删除第3个结点,正向遍历输出剩余元素。完整代码涵盖创建、删除与遍历操作,体现了双向链表的灵活管理能力。

hou09tian的博客 16

ARM学习笔记(四)——i.MX6ULL C语言点灯、蜂鸣器裸机驱动、BSP工程管理

C 语言可以通过指针直接访问指定的内存地址。0x020C4068↓转换成unsigned int指针↓访问这个地址中的32位数据↓通过CCM_CCGR0进行操作0x020C40680xFFFFFFFF。

weixin_56431759的博客 207

前端?C++ ?较大差异基础罗列

其他,注意在遍历的时候,如果想对原数值进行修改一定要遍历读取引用,否则是复制一份的数据修改不了原来数组。static声明的变量只初始化一次,并且在函数调用执行完成之后不会销毁。这样做,因为不同库可能有同名函数,会产生命名冲突。这真的,,,前端看到震惊写法(孤陋寡闻了,,,比 NULL 更安全、更清晰,避免类型混淆。const 对象只能调用 const 函数。的缩写,它是 C++ 标准库的。

沛沛呀、 194

Notepad++ 下载安装使用指南【一文通关,超详细~】

Notepad++ 文本编辑器的下载、安装及使用教程,附常用快捷键和相关实操案例

zwldn的博客 7万+

【C++三方组件】toml++:人性化的配置文件格式

配置文件写 JSON 没注释、写 YAML 踩缩进陷阱、写 INI 没类型——TOML 就是冲这些痛点设计的「给人写的配置格式」。toml++ 是它的单头 C++ 实现:解析、访问、序列化、报错一应俱全。How 实测完整回路:at_path 路径访问、数组表遍历、value_or 默认值、toml_formatter 写出、parse_error 报错。Why 拆三问:TOML 凭什么更适合人写、动态配置与 schema 序列化是两种什么哲学、单引号字符串背后的「写出来就能读回去」原则。

码工许师傅 405

海思Hi3516DV300实战:从yolov5模型训练到Sort跟踪算法的完整部署流程

本文详细介绍了在海思Hi3516DV300芯片上部署YOLOv5目标检测与Sort多目标跟踪算法的完整流程。内容涵盖从特定场景数据训练YOLOv5模型、模型格式转换(PyTorch到ONNX再到Caffe),到利用海思NNIE引擎生成WK模型并进行端侧集成与优化的全链路实战经验,旨在解决嵌入式设备上实时人流量统计等实际应用挑战。

weixin_29034963的博客 317

第 1 章 C++ 基础:从二进制到类的完整逻辑链

这一章把嵌入式 C++ 讲到底层:为什么用二进制、变量在内存里是什么、 溢出为什么发生、位运算在寄存器上怎么用、类和对象到底怎么工作。 学完本章,你不仅能看懂本书代码,还能说出"每个写法背后在硬件上发生什么"。

ai2work的博客 233

【C++三方组件】cxxopts:轻量首选的命令行解析

不是每个工具都需要子命令和校验器——更多时候要的只是「十行声明、一次解析、拿到值」。cxxopts 用 60KB 单头给出这一档:add_options() 一张表声明,parse 后按字符串 key 取值。How 实测六组:默认值与类型转换、隐式值布尔开关、逗号分拆、分组帮助文本(–help 不内建)、异常家族真实报文、allow_unrecognised 透传。Why 拆四问:砍掉子命令换来什么、字符串 key 代价怎么管、隐式值从哪来、help 为何不内建。与 CLI11 繁简分界一表看清。

码工许师傅 579

深入解析ffmpeg编码错误:avcodec_send_frame()返回-22的排查与修复

本文深入解析FFmpeg编码中avcodec_send_frame()返回-22(AVERROR(EINVAL))错误的根本原因与系统排查方法。核心问题通常在于编码器未正确打开或上下文传递错误。文章提供了从检查编码器查找、确认avcodec_open2()调用,到验证上下文传递的完整修复指南,并附有可运行的代码示例,帮助开发者快速定位并解决这一常见编码障碍。

weixin_28049429的博客 364

C++ 入门阶段完整总结(前端友好|零基础通关|含全部难点易错点)

本文为前端开发者零基础入门C++的第一阶段复盘,以通俗视角解析指针、引用、类、构造/析构、继承多态、虚函数与虚析构、RAII、智能指针、STL容器、Lambda等核心概念。重点对比C++与JS在内存管理、传递方式、生命周期上的本质差异,揭示“手动资源管理”是C++核心挑战。通过实战化讲解与高频踩坑点总结,帮助新手突破理解瓶颈,掌握现代C++安全编程范式,为后续进阶打下坚实基础。

X北辰北的博客 339

AI在保险产品定价优化中的创新应用

在当今保险市场中,准确合理的产品定价是保险公司实现可持续发展的关键因素之一。传统的保险产品定价主要基于经验数据和精算模型,但这种方式往往存在数据处理效率低、对复杂风险因素考虑不足等问题。本文的目的在于深入探讨AI技术如何在保险产品定价中进行创新应用,以提高定价的准确性、效率和灵活性,更好地满足市场需求和客户个性化需求。本文的范围涵盖了AI技术在保险产品定价各个环节的应用,包括风险评估、费率计算、产品设计等方面。同时,还会涉及到相关的核心算法、数学模型以及实际应用案例等内容。

欢迎来到我的CSDN空间!这里聚焦AI大模型应用实战,分享前沿技术、实战案例与开发经验。 966

关系操作符oj&&c语言大学第一节课笔记

因为这里是先用再加,所以先拿出来的是0,这是假,则到第二个++b,它是非0,为真,所以后面就短路。scanf 双引号里面除了占位符以外的普通字符,要求你键盘原样输入。#include<math.h>这个是开根号要用的。先用后加,直接把0取下来,后面直接为假,全部短路。但是如果把a改为1,后面就直接全对了,全部加1。scanf和printf的内容格式是一样的。如果a为6,1<a为真,这个就返回1了。1<5,反而成立,这就是问题所在。这里就反过来,全真即真,一假即假。void是虚空,实际上和不写一样。

Code_Solitude的博客 122

a---b 到底切成什么?C 编译器切符号的“贪心法“

C语言符号分为单字符与多字符两类,多字符符号因歧义需采用“贪心法”(大嘴法)处理:编译器尽可能多地读取字符,直至无法构成合法符号为止。例如a---b被切为(a--)-b,而x/*p则被误作注释开始,导致后续代码被忽略。符号间不可嵌入空白,否则破坏复合运算符如+=、>>=的完整性。历史版本编译器处理方式不同,如今严格遵循贪心规则,确保词法分析仅依赖字符序列,不考虑语义。

UIU114的博客 6283

无人机——电机篇(一)

1. 电机的定义 电机俗称“马达”,是无人机的动力来源,无人机通过改变电机的转速来改变无人机的飞行状态。即改变每个电机的速度,使得无人机能够盘旋空中,上升或下降,或向各个方向移动。2. 电机的分类 电机分为:“无刷电机”和“有刷电机” - 无刷电机:是采用半导体开关器件来实现电子换向,具有可靠性高、低干扰、低噪声低、寿命长等优点。一般应用在稍大型的飞行器,载重大,可以有更广泛的用途。 - 有刷电机:是内含电刷装置的将电能转换成机械能(电动机)或将机械能转换成电能(发电机)的旋转电机。有刷电机是所有电机的基

weixin_46066938的博客 1万+

C++之类和对象(上)02

本文介绍了C++中this指针的作用与使用限制,指出其由编译器隐式传递,用于访问当前对象的成员变量。同时讲解了类的默认成员函数,重点阐述构造函数的定义、作用及特点:函数名与类同名、无返回值、对象创建时自动调用、可重载,且用户未定义时编译器自动生成默认构造函数。默认构造函数包括无参、全缺省及编译器生成的三种形式,三者不可共存。对于自定义类型成员,会调用其默认构造函数初始化,内置类型则不保证初始化。

2401_88805889的博客 183

把外部系统接到 SAP Build Process Automation 的两条路:前台直连与后台编排的完整范式

想把外部系统接入到你的【销售订单管理】审批流程,最稳妥的路径是:在流程里开一个,在 BTP 配好以为核心的 Destination,让前端经由approuter代理、后台通过 Cloud SDK 直连,任务处理用My Inbox或Task APIs,异步对接用。这套组合拳,既满足安全边界与运维规范,又能让前台与后台两个世界各取所需、自然衔接。SAP。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1621

【C++三方组件】glog:Google 出品的 C++ 日志库

spdlog 之前十年的世界:LOG(INFO) << "msg";——流式语法、I/W/E 级别前缀、文件行号自动附体。glog 三件套是 LOG 分级、VLOG 按号码细控、CHECK 断言家族(实测失败即 F 级报文 Check failed: retries <= 10 (99 vs. 10) + 堆栈 + 非零退出)。Why 拆三问:流式语法为什么是那一代的选择、VLOG 的号码比级别好在哪、CHECK 为什么比 assert 更适合生产。与 gflags 的黄金搭档收尾。

码工许师傅 222
上一篇: Kolakoski序列的生成——搜狐2018研发类笔试编程题
下一篇: CUDA内存优化入门:从Naive到Tiled矩阵乘法—Week2学习总结
飞鹰51
博客等级 码龄15年 21粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值