2026-02-10 19:30:22
再有十天就要过年了,这两天刚刚放寒假。
继续抽出时间来总结一下过去的2025年、再展望一下2026年!
相关文章:
刚刚过去的2025年可以说是比较忙碌的一年(虽然每年都这么说)。
回顾过去的一年,大概做了这么几个事情:
下面简单聊聊吧。
25年除了带 24级的学生之外,又新加入了 25级的学生。
因为不是第一个班,所以实际上有一种很奇怪的感觉。
虽然这个比喻不太恰当,但是:
这个感觉有点类似于,生了一个孩子之后,又有了二胎和三胎……
不过随着国内环境的越来越卷,招收的学生的质量也是在不断提高的;
有不少同学还是非常愿意学习的,这是一个好事!
除了日常的教学工作,辅导学生竞赛之外,在 2025 年同样也参加了教师技能大赛;
说实话,这个比赛有点类似于表演的性质,基本上都是提前准备好材料、稿子和PPT。
但是从比赛当中,确实从几位老教师身上学到了一些讲课的方式方法,包括:
总的来说,收获还是挺多的,尤其是在上课节奏、内容的把握上!
除了教师的技能大赛之外,每年必不可少的项目就是带学生比赛;
今年由于赛制改革,把云计算的比赛给砍掉了,所以比赛不是很多,这也给了我不少的时间去构思,如何才能做一个亮眼、有创意的项目。
当然,虽然说是“学生技能大赛”,但是实际上基本上所有的项目部分都是老师来完成的!
学生主要还是:熟悉项目功能、完成系统的部署和使用,同时能够做好汇报工作即可!
虽然今年的比赛拿到了省金奖,但是实际上整个项目还是有非常大的提升空间的!
目前新的思路有了,但是还不方便在这里透露,后续可以再额外开一个部分来分享!
随着 AI 的发展,实际上高校的课程也是在不断的变化和改革的!
今年就接了一门新的课程:《人工智能导论》
由于之前做过一些比较有趣的项目,所以这个课程在设计的时候,就融入了一些比较有趣的内容;
整体的课程上下来,我个人觉得效果是比较好的!
这个课主要带着学生们做:
通过这门课程,实际上大部分学会的学生都能够:
我个人觉得这个课程还是比较有意思的!
同时为了让学生都能完成作业,我还专门录了B站的视频:
2025 年年底,终于买了人生的第一辆车!
有了车之后,可行动的范围也大大扩大,也跑了几次高速,感觉方便了很多!
目前还在熟练中,后续会做一些旅行、拍照吧!
最后一个部分,想要聊一聊 AI;
大家也都能感受到,目前 AI 对各行各业都有着不小的冲击!
实际上教育行业也是一样的:
学校讲授的大量知识实际上 AI 已经完全可以做的很好了,甚至留的作业 AI 都能完美的完成。
这就导致,课程的内容在设计后:实际上即使学生不上这门课、甚至完全不懂这门课的内容,依然可以借助 AI 解决遇到的问题!
所以,是否还有必要像传统模式一样去授课目前是存在疑问的!
说实话,就我个人的学习和提升而言,有时候也会迷茫,目前学的东西还是否有用?
关于 AI 的讨论,可以看 yihong 大佬的这个讨论:
简而言之,我觉得未来在教育形式上,一定会有大的变革!
在期末考试的时候,我甚至设计的是开卷考试:
提供项目的需求书,让学生们借助大模型去完成项目需求说明中的功能,并测试!
我觉得这样的考核可能更符合当下企业的需求吧!
更多的改革,目前我也在思考和探索中!
说完了过去的 2025,现在来展望一下 2026 吧……
虽然每年列出的计划,到最后大部分都没完成;
但是还是那句话:“梦想还是要有的,万一实现了呢?”
如果后面有新加入的内容,也会再加进来!
最后,祝愿各位在新的一年都能顺顺利利,无限进步!
今年も、よろしくお願いします!
2025-08-20 20:39:59
传统的提示词工程通常涉及编写自由文本,随着应用的发展,提示词文本会变得越来越复杂。
从而引出:提示词难以维护、难以进行版本控制、在不同场景下难以重用,几乎不可能进行系统化测试等一系列问题。
如何解决这些问题呢?
Microsoft给出了一个工程化的答案:POML!
文章和 Colab 配合,学习效果更佳:
POML通过引入一种结构化方法,使用类似于 HTML 的格式编写提示词内容;
用户无需编写纯文本提示词,而是可以使用 <role>、<task> 和 <example> 等语义组件来组织提示词意图,从而带来更好的 LLM 性能和更便捷的提示词维护。
同时,POML具有类似CSS的样式系统,将内容与定义表示分离;
POML采用三层架构运行,分离关注点并支持灵活的提示词开发:

该架构通过几个阶段处理POML文件:
这种分离使开发者能够在不改变核心逻辑的情况下修改表示样式,无缝集成外部数据源,并在项目中保持一致的提示词结构。
POML使用类似HTML的语法和语义组件,使提示词更具可读性和可维护性。
主要组件包括:
<role>:定义LLM应采用的角色或身份;<task>:指定LLM需要完成的任务;<example>:提供少样本学习示例;<output-format>:控制预期的响应格式;<hint>:提供额外的上下文或约束;例如:
<poml> <role>You are a patient teacher explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <img src="photosynthesis.png" alt="Diagram of photosynthesis" /> <output-format> Keep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!". </output-format></poml>
POML通过专用组件来集成外部数据:
<document>:嵌入文本文件、PDF或Word文档;<table>:集成电子表格或CSV文件中的结构化数据<img>:包含带有替代文本的图像,用于支持视觉的模型<audio>:处理多模态应用的音频文件;例如:
<hint captionStyle="header" caption="Background Knowledge"> <Document src="assets/tom_and_jerry.docx"/></hint><example> <input> <img src="assets/tom_cat.jpg" alt="The image contains the Tom cat character." syntax="multimedia" /> </input> <output> <Document src="assets/tom_introduction.txt"/> </output></example>
POML具有类似CSS的样式系统,将内容与表示分离。
例如:
<stylesheet> role { verbosity: concise; format: markdown; } task { emphasis: strong; }</stylesheet>
这允许开发者修改详细程度、输出格式和强调等样式方面,而无需改变核心提示词逻辑,显著降低调整提示词时格式漂移的风险。
POML包含强大的模板引擎,用于动态提示词生成:
{ { variable_name } }
<for each="item in items">...</for>
<if condition="variable > 0">...</if>
<let name="variable" value="expression" />
这支持创建数据驱动的提示词,能够适应不同的上下文和输入。
POML提供全面的开发工具包,提高生产力:
Visual Studio Code扩展提供:
POML为Python和TypeScript/JavaScript提供SDK:
Python SDK:
from poml import load, render# Load and render a POML fileprompt = load("example.poml")result = render(prompt, variables={"topic": "photosynthesis"})
TypeScript SDK:
import { loadPoml, renderPoml } from 'pomljs';// Load and render a POML fileconst prompt = await loadPoml('example.poml');const result = await renderPoml(prompt, { topic: 'photosynthesis' });
Node.js (via npm):
npm install pomljs
Python (via pip):
pip install poml
编写一个名为 example.poml 的文件,内容如下:
example.poml
<poml> <role>You are a patient teacher(named {{teacher_name}}) explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <input> <img src="photosynthesis.jpg" alt="Diagram of photosynthesis" syntax="multimedia"/> </input> <output-format> Keep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!". </output-format></poml>
示例定义了:
teacher_name;编写完成后,如果你安装了 Visual Studio Code poml 插件,则可以进行预览:

借助 POML 工具包,此提示词可以轻松渲染为灵活的格式,并可通过 LLM 进行测试。
例如在 Python 中:
from poml import poml# Process a POML file# result = poml("example.poml")# Process with context variablesresult = poml("example.poml", context={"teacher_name": "Jasonkay"})print(f"Process with context variables: {result}")# Get OpenAI chat format(Within the higher version)# messages = poml("example.poml", format="openai_chat")# print(f"Get OpenAI chat format: {messages}")
poml 函数接受以下参数:
markup:POML 内容(字符串或文件路径)context:可选的模板注入数据stylesheet:可选的样式自定义format:输出格式(”dict”、”openai_chat”、”langchain”、”pydantic” 或 “raw”)执行代码后,输出结果为:
Process with context variables: [{'speaker': 'system', 'content': '# Role\n\nYou are a patient teacher(named Jasonkay) explaining concepts to a 10-year-old.\n\n# Task\n\nExplain the concept of photosynthesis using the provided image as a reference.'}, {'speaker': 'human', 'content': [{'type': 'image/webp', 'base64': 'UklGRg......', 'alt': 'Diagram of photosynthesis'}, '# Output Format\n\nKeep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!".']}]
可以看到,输出的内容将内容进行了渲染!
最后,将我们的提示词和外部 LLM 系统相结合!
由于目前最新的 POML SDK 还不支持使用
format参数来渲染openai_chat类型的 Prompt;因此,这里使用 Gemini API 来发送图片!
使用下面的 poml 文件来渲染:
example.poml
<poml> <role>You are a patient teacher(named {{teacher_name}}) explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <output-format> Keep the explanation simple, engaging, and under 100 words. Start with "Hey there, future scientist!". </output-format></poml>
首先安装 Gemini SDK:
pip install -U google-genai
要运行下面的代码,你需要创建一个 Gemini 的 API Key:
随后,将下面的
YOUR_API_KEY替换为你生成的 Key!
from google import genaifrom poml import pomlfrom google.genai import typesGEMINI_API_KEY="YOUR_API_KEY"client = genai.Client(api_key=GEMINI_API_KEY)# Read the picturewith open('photosynthesis.jpg', 'rb') as f: image_bytes = f.read()# Render the POML fileresult = poml("example.poml", context={"teacher_name": "Jasonkay"}, chat=False)# print(f"Process with context variables: {result}")response = client.models.generate_content( model="gemini-2.5-flash", contents=[ result, types.Part.from_bytes( data=image_bytes, mime_type='image/jpeg', ), ])print(response.text)
最后,执行即可输出内容:
Look at our amazing plant friend! Just like you need food, plants need to eat too! This image shows how they do it, a process called **photosynthesis**. Plants use sunlight from the sun, and "drink" water through their roots. They also breathe in a gas called CO2 (carbon dioxide) from the air, shown by the blue arrow going in. Using these, they make their own sugary food to grow! As a super cool bonus, they release O2 (oxygen) for us to breathe, shown by the blue arrow going out. Amazing, right?
现在,让我们为上面的例子增加相关的样式,来优化的 Prompt 配置!
example-2.poml
<poml> <role>You are a patient teacher(named {{teacher_name}}) explaining concepts to a 10-year-old.</role> <task>Explain the concept of photosynthesis using the provided image as a reference.</task> <output-format> <list listStyle="dash"> <item className="explanation">Keep the explanation simple, engaging, and under 100 words.</item> <item className="greeting"> Start with "Hey there, future scientist!". </item> </list> </output-format></poml><stylesheet> { ".explanation": { "syntax": "json" }, "list" : { "whiteSpace": "trim" } }</stylesheet>
渲染结果如下:
# RoleYou are a patient teacher(named ) explaining concepts to a 10-year-old.# TaskExplain the concept of photosynthesis using the provided image as a reference.# Output Format```json"Keep the explanation simple, engaging, and under 100 words."```- Start with "Hey there, future scientist!".
更多内容可以参考官方文档:
在完成了基础学习之后,可以继续阅读下面的内容:
进行更加深度的学习!
文章和 Colab 配合,学习效果更佳:
参考文章:
2025-07-30 13:22:44
之前一直都在这里发一些学习日语相关的内容。但是感觉这些可以单独开一个新的网站来总结;
最近比较有时间,就开了一个新的坑;
博客主题用的是:hexo-theme-anzhiyu,真的很酷!
也是花了半个小时,使用hexo,从零搭建了一个博客;
之前没有好好总结,这里就又简单记录了一下搭建的过程,如果你还没有博客,可以跟着我一步一步来搭建一个GithubPages博客!
新博客地址:
源代码:
可以在官网下载Node:
我这里使用的是 fnm 作为版本管理工具;
配置国内源:
# 国内 淘宝 镜像源 npm config set registry https://registry.npmmirror.com/参考:
也可以把 yarn 安装了:
npm install -g yarn# 国内 淘宝 镜像源yarn config set registry https://registry.npmmirror.com/
随后下载 Hexo-cli 命令行工具:
npm install hexo-cli -g
参考:
直接通过命令行初始化项目 jp:
# hexo init jpINFO Cloning hexo-starter https://github.com/hexojs/hexo-starter.gitINFO Install dependencies......INFO Start blogging with Hexo!
安装依赖并测试:
cd jpnpm ihexo s # 本地调试
此时访问:
你就能看到你的博客了!
参考文档:
默认的主题不太好看,可以在 github 上搜索 hexo 的相关主题(一般为 hexo- 开头);
以 hexo-theme-anzhiyu 为例,文档:
首先在你项目的根目录将主题 clone 下来:
git clone -b main https://github.com/anzhiyu-c/hexo-theme-anzhiyu.git themes/anzhiyu
随后打开 Hexo 根目录下的 config.yml, 找到以下配置项,把主题改为anzhiyu
# Extensions## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: anzhiyu
然后安装 pug 和 stylus 渲染插件:
npm install hexo-renderer-pug hexo-renderer-stylus --save
再次执行:
hexo s
此时再次访问:
你就能看到你新的主题的博客了!
拉下来的新的主题,很多内容大概率都不符合你的预期(比如:title、分类等等);
此时你可以参考文档进行个性化的配置:
需要注意的是:
在根目录下存在 _config.yml、各自主题下也有这个文件,优先级为:根目录 > 各自主题配置!
通过:
hexo new xxx
即可创建一篇新的博文!
实际上就是在
source/_posts/目录下创建了一个新的*.md文件而已!
可以增加 Post Front-matter 来对文章进行配置!
文章内容就按照 markdown 的格式去写就可以了!
本地调试无误后,修改根目录下的 _config.yml:
# Deployment## Docs: https://hexo.io/docs/one-command-deploymentdeploy: type: 'git' repo: [email protected]:jasonkayzk/jp.git # 替换为你的仓库地址 branch: main # 部署分支
安装插件:
npm install hexo-deployer-git --save
随后,在发布时,通常情况下:
hexo g 生成静态资源;hexo d 即可部署!参考:
但是这种方式每次都要在本地生成静态资源,效率太低!
可以通过 Github Actions,每次 push 代码之后自动提交!
在 .github/workflows/ 目录下创建:
deploy.yml
name: Build & Deploy Blogon: workflow_dispatch: push: branches: - devjobs: build: runs-on: ubuntu-latest strategy: matrix: node_version: [18] steps: - name: Checkout source uses: actions/checkout@v3 with: ref: dev - name: Cache dependencies uses: actions/cache@v3 with: path: | node_modules public key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - name: Use Node.js ${{ matrix.node_version }} uses: actions/setup-node@v3 with: version: ${{ matrix.node_version }} - name: Add SSH key uses: webfactory/[email protected] with: ssh-private-key: ${{ secrets.hexo_deploy_pri }} - name: Setup hexo run: | git config --global user.email "[email protected]" git config --global user.name "jasonkayzk" npm install hexo-cli -g npm install - name: Hexo deploy env: GIT_SSH_COMMAND: ssh -o StrictHostKeyChecking=no run: | hexo clean hexo g hexo d
随后在仓库的 Secrets and variables 设置中,添加 Repository secrets:
HEXO_DEPLOY_PRI;提交到 dev 分支后即可自动部署!
同时,生成的内容可能会覆盖原本的笔记(hexo d 会强行覆盖部署分支!):
例如:
git checkout -b devgit push
新博客地址:
源代码:
2025-07-29 17:41:50
随着人工智能的发展,科学计算(尤其是矩阵/张量计算)越来越重要;因此,基于CUDA的张量编程也越来越重要。
在上一篇笔记中翻译了《An Even Easier Introduction to CUDA》,但是感觉作者写的不是很好;
这里重新写了一篇。同时,也作为CUDA和并行编程的开篇。
源代码:
温馨提示:本文章配合 Colab 一同执行学习效果更佳:
CUDA 是 NVIDIA 开发的并行计算平台和编程模型;
具有以下特点:
下面是一个 GPU 硬件单元:

每个核心中包含了多个 SM(Stream Multi-processor),任务在 SM 中处理;
SM 中包含了:
CPU 与 GPU 协同工作的流程如下:

首先,习惯上将:
通常,Global Memory 在其范围和生命周期中是全局的!
也就是说,每个在thread block grid 中的 thread 都可以访问Global Memory,并且生命周期与程序的执行时间一样长!
更多内容:
CUDA 程序执行时主要分为以下几个步骤:
CUDA 这种流程实现了 CPU 与 GPU 协同,让 GPU 承担并行计算 heavy - lifting ,提升计算密集型任务效率,广泛用于深度学习训练推理、科学计算等场景!
add_cpu.cpp
#include <cmath>#include <iostream>#include <vector>// Step 2: Define add functionvoid add_cpu(std::vector<float> &c, const std::vector<float> &a, const std::vector<float> &b) { // CPU use loop to calculate for (size_t i = 0; i < a.size(); i++) { c[i] = a[i] + b[i]; }}int main() { // Step 1: Prepare & initialize data constexpr size_t N = 1 << 20; // ~1M elements // Initialize data const std::vector<float> a(N, 1); const std::vector<float> b(N, 2); std::vector<float> c(N, 0); // Step 3: Call the cpu addition function add_cpu(c, a, b); // Step 4: Check for errors (all values should be 3.0f) float maxError = 0.0f; for (int i = 0; i < N; i++) { maxError = fmax(maxError, fabs(c[i] - 3.0f)); } std::cout << "Max error: " << maxError << std::endl;}
主要分为以下几个步骤:
分为以下几个步骤:
*.cu:例如add_cuda.cu(表示 CUDA 程序)cudaMalloc 分配显存、使用 cudaMemcpy 复制数据等;下面分别来看;
*.cu
CUDA 规定其文件扩展名为 *.cu,语法和 C++ 类似!
这步比较简单:
add_cuda.cu
// Step 1: Prepare & initialize dataconstexpr size_t N = 1 << 20; // ~1M elementsconstexpr size_t size_bytes = sizeof(float) * N;// Initialize dataconst std::vector<float> h_a(N, 1);const std::vector<float> h_b(N, 2);std::vector<float> h_c(N, 0);
此时在 Host 端的 RAM 分配内存;
数据传输到 GPU 使用 CUDA 提供的函数:
cudaMalloc 分配显存;cudaMemcpy 复制数据;add_cuda.cu
float *d_a, *d_b, *d_c;CUDA_CHECK(cudaMalloc(&d_a, size_bytes));CUDA_CHECK(cudaMalloc(&d_b, size_bytes));CUDA_CHECK(cudaMalloc(&d_c, size_bytes));CUDA_CHECK(cudaMemcpy(d_a, h_a.data(), size_bytes, cudaMemcpyHostToDevice));CUDA_CHECK(cudaMemcpy(d_b, h_b.data(), size_bytes, cudaMemcpyHostToDevice));CUDA_CHECK(cudaMemcpy(d_c, h_c.data(), size_bytes, cudaMemcpyHostToDevice));
这里使用了:
CUDA_CHECK 宏进行校验;cudaMemcpyHostToDevice 指定数据流方向;CUDA_CHECK 宏定义如下:
#define CUDA_CHECK(call) \{ \ cudaError_t err = call; \ if (err != cudaSuccess) { \ std::cerr << "CUDA Error at " << __FILE__ << ":" << __LINE__ \ << " - " << cudaGetErrorString(err) << std::endl; \ } \}
在 CPU 中,使用循环进行执行;
而在 GPU 中,使用的是 SIMT,即:一条命令会同时被多个线程执行!
此时需要指挥每个线程:组织结构和编号!
在 CUDA 中,包含:
如下图:

其中:每一个 Grid 中包含多个已编号的 Block,而每一个 Block 中包含多个已编号的 Thread!
同时,每个 Block 中包含的线程数是一样的!
一共有:0~N-1个Thread(假设每个 Block 包含 N 个 Thread);

在 CUDA 中:
idx = BlockID * BlockSize + ThreadID;如下图:

相对于 CPU 中使用循环的方式执行,在 GPU 中主要使用的是:多线程并行;
步骤如下:
层级结构定义:
// Set up kernel configurationdim3 block_dim(256);dim3 grid_dim((N + block_dim.x - 1) / block_dim.x);
核函数定义:
template<typename T>__global__ void add_kernel(T *c, const T *a, const T *b, int n) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx < n) { c[idx] = a[idx] + b[idx]; }}
只能通过指针的方式传递!
因为像是 vector 等数据结构,都是在 Host 端定义的,并不能在 Global Memory 中分配!
核函数调用:
// Call cuda add kerneladd_kernel<<<grid_dim, block_dim>>>(d_c, d_a, d_b, N);
其中:
dim3:CUDA 表示线程层级结构的类型(包括:x、y、z 三个维度);<<<>>>:传递线程层级信息给核函数;核函数(kernel):设备侧的入口函数;__global__:表示这是个核函数;blockldx:表示 block 的编号,第几个 block;blockDim:表示 block 的大小,一个 block 多少个线程;threadldx:表示 thread 的编号,表示第几个 thread;同样,使用 cudaMemcpy:
CUDA_CHECK(cudaMemcpy(h_c.data(), d_c, size_bytes, cudaMemcpyDeviceToHost));
验证结果使用已经复制到 h_c 中的数据;
释放内存使用 cudaFree:
add_cuda.cu
float maxError = 0.0f;for (int i = 0; i < N; i++) { maxError = fmax(maxError, fabs(h_c[i] - 3.0f));}std::cout << "Max error: " << maxError << std::endl;if (d_a) { CUDA_CHECK(cudaFree(d_a));}if (d_b) { CUDA_CHECK(cudaFree(d_b));}if (d_c) { CUDA_CHECK(cudaFree(d_c));}
需要使用 NVCC(NIVIDEA CUDA Compiler) 的编译器来编译程序;
NVCC 是 CUDA Toolkit 的一部分:
如下图所示:

流程如下:
NVIDIA 不同年代生产的GPU可能有不同的架构,如下图所示:

以 A100 为例,A100 为 Ampere 架构;同时,各个架构间有区别;
因此提出:Compute Capability (CC)
虽然 A100 举例,但从 CUDA 编程的角度目前各种架构没有本质区别!
正因为如此,所以说CUDA是一个编程平台
同时,在编译时也可以指定架构编译选项:
-arch:指定虚拟架构,PTX生成目标。决定代码中可使用的CUDA 功能;-code:指定实际架构,生成针对特定 GPU 硬件的二进制机器码(cubin);通过:
nvcc add_cuda.cu -o add_cuda
即可编译!
运行:
./add_cuda
可以通过:
nvidia-smi
观察 GPU 利用率!
分别对比:
<<<1,1>>>;<<<256,256>>>;代码如下:
add_cuda_profiling.cu
#include <cmath>#include <iostream>#include <vector>#define CUDA_CHECK(call) \{ \ cudaError_t err = call; \ if (err != cudaSuccess) { \ std::cerr << "CUDA Error at " << __FILE__ << ":" << __LINE__ \ << " - " << cudaGetErrorString(err) << std::endl; \ } \}// Step 3: Define add kerneltemplate<typename T>__global__ void add_kernel(T *c, const T *a, const T *b, const size_t n, const size_t step) { int idx = blockIdx.x * blockDim.x + threadIdx.x + step; if (idx < n) { c[idx] = a[idx] + b[idx]; }}template<typename T>void vector_add(T *c, const T *a, const T *b, size_t n, const dim3& grid_dim, const dim3& block_dim) { size_t step = grid_dim.x * block_dim.x; for (size_t i = 0; i < n; i += step) { add_kernel<<<grid_dim, block_dim>>>(c, a, b, n, i); }}int main() { // Step 1: Prepare & initialize data constexpr size_t N = 1 << 20; // ~1M elements constexpr size_t size_bytes = sizeof(float) * N; // Initialize data const std::vector<float> h_a(N, 1); const std::vector<float> h_b(N, 2); std::vector<float> h_c(N, 0); // Step 2: Allocate device memory & transfer to global memory float *d_a, *d_b, *d_c; CUDA_CHECK(cudaMalloc(&d_a, size_bytes)); CUDA_CHECK(cudaMalloc(&d_b, size_bytes)); CUDA_CHECK(cudaMalloc(&d_c, size_bytes)); CUDA_CHECK(cudaMemcpy(d_a, h_a.data(), size_bytes, cudaMemcpyHostToDevice)); CUDA_CHECK(cudaMemcpy(d_b, h_b.data(), size_bytes, cudaMemcpyHostToDevice)); CUDA_CHECK(cudaMemcpy(d_c, h_c.data(), size_bytes, cudaMemcpyHostToDevice)); // Step 4: Call the cpu addition function // Set up kernel configuration dim3 block_dim(1); dim3 grid_dim(1); // Call cuda add kernel vector_add(d_c, d_a, d_b, N, block_dim, grid_dim); // Step 5: Transfer data from global mem to host mem CUDA_CHECK(cudaMemcpy(h_c.data(), d_c, size_bytes, cudaMemcpyDeviceToHost)); // Step 6: Check for errors (all values should be 3.0f) float sumError = 0.0f; for (int i = 0; i < N; i++) { sumError += fabs(h_c[i] - 3.0f); } std::cout << "Sum error: " << sumError << std::endl; if (d_a) { CUDA_CHECK(cudaFree(d_a)); } if (d_b) { CUDA_CHECK(cudaFree(d_b)); } if (d_c) { CUDA_CHECK(cudaFree(d_c)); }}
可以修改其中的:
dim3 block_dim(1); dim3 grid_dim(1);
不同情况下的性能如下:

可以使用 Nsight Systems(nsys,NVIDIA系统级性能分析工具)来分析;
启动 profiling:
nsys profile -t cuda,nvtx,osrt -o add_cuda_profiling -f true ./add_cuda_profiling
解析并统计性能信息:
nsys stats add_cuda_profiling.nsys-rep ** OS Runtime Summary (osrt_sum): Time (%) Total Time (ns) Num Calls Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------- ------------- ---------- ----------- ------------- ---------------------- 56.2 7,592,724,284 84 90,389,574.8 100,130,776.0 2,330 370,626,986 45,049,255.4 poll 42.4 5,736,493,727 26 220,634,374.1 189,702,756.5 41,077,614 752,975,386 124,762,585.8 sem_wait 1.2 164,252,099 543 302,490.1 13,509.0 529 111,402,991 4,818,716.4 ioctl 0.1 14,968,499 38 393,907.9 131,267.0 135 5,539,804 890,642.6 pthread_rwlock_wrlock ...... ** CUDA API Summary (cuda_api_sum): Time (%) Total Time (ns) Num Calls Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------ ----------- -------- ----------- ------------- ---------------------- 96.9 6,504,565,162 1,048,576 6,203.2 5,159.0 2,928 37,814,020 99,097.6 cudaLaunchKernel 3.0 203,141,797 3 67,713,932.3 103,908.0 73,162 202,964,727 117,130,625.1 cudaMalloc 0.1 4,017,591 4 1,004,397.8 1,012,632.0 941,545 1,050,782 45,652.8 cudaMemcpy 0.0 524,788 3 174,929.3 136,182.0 122,785 265,821 78,999.0 cudaFree 0.0 2,584 1 2,584.0 2,584.0 2,584 2,584 0.0 cuModuleGetLoadingMode......
各个类型 API Summary 分析结果如下:

可以看到:<<<1,1>>> cudaLaunchKernel 占比非常高这是由于:
核函数调用有开销,在外面多次循环调用开销巨大!
因此,需要进行优化!
由于在循环中频繁的调用核函数具有巨大的性能开销,因此可以将循环放入核函数中:
template<typename T>__global__ void add_kernel_inner_loop(T *c, const T *a, const T *b, const size_t n, const size_t step) { int idx = blockIdx.x * blockDim.x + threadIdx.x; for (size_t i = idx; i < n; i += step) { if (i < n) { c[i] = a[i] + b[i]; } }}template<typename T>void vector_add(T *c, const T *a, const T *b, size_t n, const dim3& grid_dim, const dim3& block_dim) { size_t step = grid_dim.x * block_dim.x; add_kernel_inner_loop<<<grid_dim, block_dim>>>(c, a, b, n, step);}
分析后结果如下图:

同时使用 nsys 分析:
** CUDA API Summary (cuda_api_sum): Time (%) Total Time (ns) Num Calls Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------ ----------- -------- ----------- ------------- ---------------------- 55.4 204,935,456 3 68,311,818.7 104,741.0 79,097 204,751,618 118,160,333.0 cudaMalloc 44.4 164,057,041 4 41,014,260.3 1,000,521.5 926,775 161,129,223 80,076,651.2 cudaMemcpy 0.2 653,441 3 217,813.7 204,732.0 194,409 254,300 32,016.9 cudaFree 0.1 264,055 1 264,055.0 264,055.0 264,055 264,055 0.0 cudaLaunchKernel 0.0 2,429 1 2,429.0 2,429.0 2,429 2,429 0.0 cuModuleGetLoadingMode
可以看到 cudaLaunchKernel 的确少了很多!
这说明:
核函数的发射数量减少,因此总体执行时间降低!
指标:
加速比 = Tcpu / Tgpu
其中:
理想加速比与实际加速比
- 理想加速比:当任务完全并行化且没有任何开销时,加速比等于处理器核心数之比。例如,一个具有 1000 个 CUDA 核心的 GPU 理论上可以实现 1000 倍的加速(相对于单核 CPU)。
- 实际加速比:由于以下因素,实际加速比通常远低于理想值:
- 任务中存在无法并行化的串行部分
- 数据在 CPU 和 GPU 之间的传输开销
- 线程同步和内存访问延迟
- 算法在 GPU 架构上的效率低下
<<<1,1>>> 比 CPU慢?
这是由于,单个 GPU 的核心实际上要比 CPU 的能力要弱!
实际上,GPU 是由于干活的人多,所以快!
实际上观察 nsys 的输出结果:
** CUDA GPU Kernel Summary (cuda_gpu_kern_sum): Time (%) Total Time (ns) Instances Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Name -------- --------------- --------- ------------- ------------- ----------- ----------- ----------- --------------------------------------------------------------------------------------------- 100.0 160,054,287 1 160,054,287.0 160,054,287.0 160,054,287 160,054,287 0.0 void add_kernel_inner_loop<float>(T1 *, const T1 *, const T1 *, unsigned long, unsigned long)Processing [add_cuda_profiling2.sqlite] with [/usr/local/cuda-12.1/nsight-systems-2023.1.2/host-linux-x64/reports/cuda_gpu_mem_time_sum.py]... ** CUDA GPU MemOps Summary (by Time) (cuda_gpu_mem_time_sum): Time (%) Total Time (ns) Count Avg (ns) Med (ns) Min (ns) Max (ns) StdDev (ns) Operation -------- --------------- ----- --------- --------- -------- -------- ----------- ------------------ 78.4 2,318,310 3 772,770.0 763,159.0 761,400 793,751 18,191.4 [CUDA memcpy HtoD] 21.6 640,473 1 640,473.0 640,473.0 640,473 640,473 0.0 [CUDA memcpy DtoH]
总体的耗时应当是三个部分:
并且,对于 <<<256,256>>> 来说:HtoD 和 DtoH 的耗时会远大于 kernel 的运行时间!
这就说明,来回的移动和复制数据比计算更消耗时间!
能否对这一部分进行优化呢?
后面的文章中会讲解!
对于 GPU 而言:越多的线程 => 越大的并行度 => 越好的性能
GPU 最大可以启动的线程数可以参考:
也可以参考:
重点的几个参数:
其中:Blocksize 需同时满足这两组条件:maxBlockSize、maxThreadsPerBlock:
- x、y、z加起来不超过:maxThreadsPerBlock;
- x、y、z各个方向不超过:maxBlockSize;
查看 CUDA 版本使用:
# CUDA版本nvcc --versionnvcc: NVIDIA (R) Cuda compiler driverCopyright (c) 2005-2023 NVIDIA CorporationBuilt on Tue_Feb__7_19:32:13_PST_2023Cuda compilation tools, release 12.1, V12.1.66Build cuda_12.1.r12.1/compiler.32415258_0
可以看到 CUDA 为 12.1!
而 nvidia-smi 命令输出的是:驱动支持的的最高版本,而非实际正在使用的版本!
Tue Jul 29 09:30:09 2025 +-----------------------------------------------------------------------------------------+| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 ||-----------------------------------------+------------------------+----------------------+| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC || Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. || | | MIG M. ||=========================================+========================+======================|| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 || N/A 38C P8 10W / 70W | 0MiB / 15360MiB | 0% Default || | | N/A |+-----------------------------------------+------------------------+----------------------++-----------------------------------------------------------------------------------------+| Processes: || GPU GI CI PID Type Process name GPU Memory || ID ID Usage ||=========================================================================================|| No running processes found |+-----------------------------------------------------------------------------------------+
可以看到,最高支持 12.4!
更加详细的内容,可以看:
源代码:
参考文章:
2025-07-24 15:37:04
在学习AI时,经常需要用到GPU资源;而有些时候我们手头并没有老黄的显卡,或者显卡已经不支持进行人工智能的学习了;
本文总结了一些常用的GPU资源;
同时,后续也会在博客更新一些和并行计算、人工智能相关的内容,敬请期待!
网址:
特点:
网址:
特点:
网址:
特点:
特点:
网址:
特点:
网址:
特点:
其他资源:
参考文章: