Logo

site iconJasonkayZK | 张小凯

本科就读于HPU电气工程,玩过ACM、嵌入式、投稿过B站鬼畜、参加过歌唱比赛、考过托福、自学日语、Java
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

JasonkayZK | 张小凯 RSS 预览

再见2025,你好2026!

2026-02-10 19:30:22

再有十天就要过年了,这两天刚刚放寒假。

继续抽出时间来总结一下过去的2025年、再展望一下2026年!

相关文章:


再见2025,你好2026!

一、过去的2025

刚刚过去的2025年可以说是比较忙碌的一年(虽然每年都这么说)。

回顾过去的一年,大概做了这么几个事情:

  • 带了新的25级学生;
  • 参加了教师技能大赛;
  • 带学生参加了学生技能大赛;
  • 上了几门新课。
  • ……

下面简单聊聊吧。


1)新学生

25年除了带 24级的学生之外,又新加入了 25级的学生。

因为不是第一个班,所以实际上有一种很奇怪的感觉。

虽然这个比喻不太恰当,但是:

这个感觉有点类似于,生了一个孩子之后,又有了二胎和三胎……

不过随着国内环境的越来越卷,招收的学生的质量也是在不断提高的;

有不少同学还是非常愿意学习的,这是一个好事!


2)教师技能大赛

除了日常的教学工作,辅导学生竞赛之外,在 2025 年同样也参加了教师技能大赛;

说实话,这个比赛有点类似于表演的性质,基本上都是提前准备好材料、稿子和PPT。

但是从比赛当中,确实从几位老教师身上学到了一些讲课的方式方法,包括:

  • 一个好的 PPT 如何制作、切换
  • 如何把握好上课的语速、节奏
  • 如何设计整个课程
  • 一些教学上的名词,比如:学情分析、教学设计、教学环节、教学过程、教学方法、教学手段、教学模式、教学评价等等
  • ……

总的来说,收获还是挺多的,尤其是在上课节奏、内容的把握上!


3)学生技能大赛

除了教师的技能大赛之外,每年必不可少的项目就是带学生比赛;

今年由于赛制改革,把云计算的比赛给砍掉了,所以比赛不是很多,这也给了我不少的时间去构思,如何才能做一个亮眼、有创意的项目。

当然,虽然说是“学生技能大赛”,但是实际上基本上所有的项目部分都是老师来完成的!

学生主要还是:熟悉项目功能、完成系统的部署和使用,同时能够做好汇报工作即可!

虽然今年的比赛拿到了省金奖,但是实际上整个项目还是有非常大的提升空间的!

目前新的思路有了,但是还不方便在这里透露,后续可以再额外开一个部分来分享!


4)几门新的课程

随着 AI 的发展,实际上高校的课程也是在不断的变化和改革的!

今年就接了一门新的课程:《人工智能导论》

由于之前做过一些比较有趣的项目,所以这个课程在设计的时候,就融入了一些比较有趣的内容;

整体的课程上下来,我个人觉得效果是比较好的!

这个课主要带着学生们做:

  • Python 基础;
  • 图像识别:标注、训练集制作、模型的训练、图像预测等等;
  • 自然语言:数据的爬取、数据的处理与分析、数据可视化、大模型基本原理、编写简单豆包;
  • 智能语音:目前这学期没有给大一的学生开,主要是给大二的学生开;

通过这门课程,实际上大部分学会的学生都能够:

  • 做自己想要识别的图像识别模型!
  • 做一个自己的豆包,同时编写简单的 function calling 工具!

我个人觉得这个课程还是比较有意思的!

同时为了让学生都能完成作业,我还专门录了B站的视频:


5)关于车

2025 年年底,终于买了人生的第一辆车!

有了车之后,可行动的范围也大大扩大,也跑了几次高速,感觉方便了很多!

目前还在熟练中,后续会做一些旅行、拍照吧!


6)关于AI

最后一个部分,想要聊一聊 AI;

大家也都能感受到,目前 AI 对各行各业都有着不小的冲击!

实际上教育行业也是一样的:

学校讲授的大量知识实际上 AI 已经完全可以做的很好了,甚至留的作业 AI 都能完美的完成。

这就导致,课程的内容在设计后:实际上即使学生不上这门课、甚至完全不懂这门课的内容,依然可以借助 AI 解决遇到的问题!

所以,是否还有必要像传统模式一样去授课目前是存在疑问的!

说实话,就我个人的学习和提升而言,有时候也会迷茫,目前学的东西还是否有用?

关于 AI 的讨论,可以看 yihong 大佬的这个讨论:

简而言之,我觉得未来在教育形式上,一定会有大的变革!

在期末考试的时候,我甚至设计的是开卷考试:

提供项目的需求书,让学生们借助大模型去完成项目需求说明中的功能,并测试!

我觉得这样的考核可能更符合当下企业的需求吧!

更多的改革,目前我也在思考和探索中!


二、你好2026

说完了过去的 2025,现在来展望一下 2026 吧……

虽然每年列出的计划,到最后大部分都没完成;

但是还是那句话:“梦想还是要有的,万一实现了呢?”

  • 证书:
    • 通过软考中级(软件设计师)
    • 日语N2证书
    • 雅思证书
  • 跑步:
    • 年跑量 1200km
    • 参加一次半马
  • 旅游:
    • 去一次日本旅游

如果后面有新加入的内容,也会再加进来!

最后,祝愿各位在新的一年都能顺顺利利,无限进步!

今年も、よろしくお願いします!


下一代提示词工程语言POML简明教程

2025-08-20 20:39:59

传统的提示词工程通常涉及编写自由文本,随着应用的发展,提示词文本会变得越来越复杂。

从而引出:提示词难以维护、难以进行版本控制、在不同场景下难以重用,几乎不可能进行系统化测试等一系列问题。

如何解决这些问题呢?

Microsoft给出了一个工程化的答案:POML!

文章和 Colab 配合,学习效果更佳:


下一代提示词工程语言POML简明教程

一、简介

POML通过引入一种结构化方法,使用类似于 HTML 的格式编写提示词内容;

用户无需编写纯文本提示词,而是可以使用 <role><task><example> 等语义组件来组织提示词意图,从而带来更好的 LLM 性能和更便捷的提示词维护。

同时,POML具有类似CSS的样式系统,将内容与定义表示分离;

(一)核心架构

POML采用三层架构运行,分离关注点并支持灵活的提示词开发:

poml-structure

该架构通过几个阶段处理POML文件:

  1. 解析:将类似XML的语法转换为结构化的中间表示;
  2. 处理:应用样式表、解析模板并集成外部数据;
  3. 生成:以各种格式生成最终的优化提示词;

这种分离使开发者能够在不改变核心逻辑的情况下修改表示样式,无缝集成外部数据源,并在项目中保持一致的提示词结构。


(二)主要特性

1、结构化标记系统

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>

2、外部数据集成

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>

3、解耦的表示样式

POML具有类似CSS的样式系统,将内容与表示分离。

例如:

<stylesheet>  role {    verbosity: concise;    format: markdown;  }  task {    emphasis: strong;  }</stylesheet>

这允许开发者修改详细程度、输出格式和强调等样式方面,而无需改变核心提示词逻辑,显著降低调整提示词时格式漂移的风险。


4、模板引擎

POML包含强大的模板引擎,用于动态提示词生成:

  • 变量:{ { variable_name } }
  • 循环:<for each="item in items">...</for>
  • 条件:<if condition="variable > 0">...</if>
  • 定义:<let name="variable" value="expression" />

这支持创建数据驱动的提示词,能够适应不同的上下文和输入。


(三)开发生态

POML提供全面的开发工具包,提高生产力:

1、VSCode扩展

Visual Studio Code扩展提供:

  • 语法高亮和语言支持
  • 上下文感知的自动补全
  • 实时预览功能
  • 与LLM提供商的集成测试
  • 错误诊断和验证
  • 可重用组件的提示词库

2、多语言SDK

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

(二)第一个案例

1、编写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>

示例定义了:

  • LLM 的角色和任务,包含一张图片作为上下文,并指定了所需的输出格式。
  • 同时,包含了一个变量 teacher_name

编写完成后,如果你安装了 Visual Studio Code poml 插件,则可以进行预览:


2、解析并渲染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!".']}]

可以看到,输出的内容将内容进行了渲染!


3、与LLM系统集成(Gemini)

最后,将我们的提示词和外部 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.js&Hexo

可以在官网下载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

参考:


二、初始化Hexo项目

直接通过命令行初始化项目 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 的格式去写就可以了!


六、发布到Github

本地调试无误后,修改根目录下的 _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 即可部署!

参考:

但是这种方式每次都要在本地生成静态资源,效率太低!


1、使用GitHub Actions自动部署

可以通过 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

  • key为:HEXO_DEPLOY_PRI
  • 值为:你连接 Github 的私钥!(注意是私钥!)

提交到 dev 分支后即可自动部署!


2、使用新分支保存笔记

同时,生成的内容可能会覆盖原本的笔记hexo d 会强行覆盖部署分支!)

  • 我们只需要 checkout 到另外的一个分支去编写博客,然后部署到 main 分支即可!

例如:

git checkout -b devgit push

附录

新博客地址:

源代码:


一、并行编程导论与CUDA入门

2025-07-29 17:41:50

随着人工智能的发展,科学计算(尤其是矩阵/张量计算)越来越重要;因此,基于CUDA的张量编程也越来越重要。

上一篇笔记中翻译了《An Even Easier Introduction to CUDA》,但是感觉作者写的不是很好;

这里重新写了一篇。同时,也作为CUDA和并行编程的开篇。

源代码:


一、并行编程导论与CUDA入门

温馨提示:本文章配合 Colab 一同执行学习效果更佳:

(一)、CUDA编程概述

1、什么是CUDA

CUDA 是 NVIDIA 开发的并行计算平台和编程模型;

具有以下特点:

  • C/C++ 语法;
  • SIMT(Single Instruction Multiple Threads)模式:一个指令会被多个线程同时执行!
  • 需要与CPU协作:CPU负责整理结果、处理逻辑;
  • 自动调度:根据设定的执行参数,自动调度;

2、CUDA 运算硬件单元

(1)SM 单元

下面是一个 GPU 硬件单元:

cuda-1.png

每个核心中包含了多个 SM(Stream Multi-processor),任务在 SM 中处理;

SM 中包含了:

  • CUDA Core/SP:进行并行的加减法等计算;
  • Tensor Core:张量计算
  • ……

(2)CPU与GPU协作

CPU 与 GPU 协同工作的流程如下:

cuda-2.png

首先,习惯上将:

  • CPU 所在端称为:Host 端,对应内存为 RAM;
  • GPU 所在称为:Device 端,对应内存为 Global Memory(通常对应 GPU RAM,显存);

通常,Global Memory 在其范围和生命周期中是全局的!

也就是说,每个在thread block grid 中的 thread 都可以访问Global Memory,并且生命周期与程序的执行时间一样长!

更多内容:

CUDA 程序执行时主要分为以下几个步骤:

  • CPU 准备(CPU Prepare):在主机端(Host ,包含 CPU 和 RAM 主存 ),CPU 负责初始化数据、设置计算参数等准备工作,为后续在 GPU 上的运算任务做铺垫,确定要处理的数据和运算逻辑;
  • CPU 传输数据至 GPU(CPU Transfers Data to GPU):通过总线(Bus),CPU 把主存(RAM)中准备好的数据传输到 GPU 端的全局内存(Global Memory ,GM),因为 GPU 运算需要的数据要先存放到其可访问的内存空间;
  • 从 GM 读数据(Read Data from GM):GPU(如 NVIDIA A100 )从自身的全局内存中读取需要参与运算的数据,将数据加载到运算单元可处理的位置;
  • 运算(Compute):NVIDIA A100 等 GPU 设备利用自身的并行运算核心,对读取的数据执行 CUDA 核函数定义的运算操作,发挥 GPU 并行计算优势,高效处理大规模数据计算任务;
  • 写回 GM(Write Back to GM):运算完成后,GPU 将运算结果写回到全局内存中,暂存运算产出的数据;
  • GPU 传输数据至 CPU(GPU Transfers Data to CPU):再次通过总线,GPU 把全局内存中存储的运算结果传输回主机端的主存(RAM),供 CPU 进一步处理(如数据展示、后续其他主机端逻辑运算等 ),完成一次 CUDA 编程的计算流程;

CUDA 这种流程实现了 CPU 与 GPU 协同,让 GPU 承担并行计算 heavy - lifting ,提升计算密集型任务效率,广泛用于深度学习训练推理、科学计算等场景!


(二)、CUDA运算示例:加法

1、CPU加法

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;}

主要分为以下几个步骤:

  • 准备和初始化数据;
  • 定义加法函数
    • 循环来进行所有的元素加法
  • 调用函数
  • 验证结果

2、修改为GPU加法(重点!)

分为以下几个步骤:

  • 修改文件名为 *.cu:例如add_cuda.cu(表示 CUDA 程序)
  • 准备和初始化数据(CPU):使用 vector 等进行 Host 端的 RAM 分配;
  • 数据传输到 GPU:使用 cudaMalloc 分配显存、使用 cudaMemcpy 复制数据等;
  • GPU 从 GM 中读取并计算后写回(调用核(kernel)函数计算)
    • 修改核函数声明:
    • 修改调用方式:
  • 将 GPU 数据传输回 CPU
  • 验证结果

下面分别来看;


(1)修改文件名为 *.cu

CUDA 规定其文件扩展名为 *.cu,语法和 C++ 类似!


(2)准备和初始化数据(CPU)

这步比较简单:

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 分配内存;


(3)数据传输到 GPU

数据传输到 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; \    } \}

(4:补)CUDA层级结构

i.线程层级结构

在 CPU 中,使用循环进行执行;

而在 GPU 中,使用的是 SIMT,即:一条命令会同时被多个线程执行!

此时需要指挥每个线程:组织结构和编号!

在 CUDA 中,包含:

  • Grid:
  • Block:
  • Thread:

如下图:

cuda-3.png

其中:每一个 Grid 中包含多个已编号的 Block,而每一个 Block 中包含多个已编号的 Thread!

同时,每个 Block 中包含的线程数是一样的!

一共有:0~N-1个Thread(假设每个 Block 包含 N 个 Thread);

cuda-4.png


ii.线程索引计算方法

在 CUDA 中:

  • 每个线程都有 独一无二 的编号索引(idx);
  • idx = BlockID * BlockSize + ThreadID

如下图:

cuda-5.png


(4)编写和调用核函数

相对于 CPU 中使用循环的方式执行,在 GPU 中主要使用的是:多线程并行

步骤如下:

  • 定义 block 的数量和大小来指挥线程、进行/并行计算;
  • 定义 GPU 上的加法函数(核函数);
  • 结合定义的信息调用 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;

(5)将 GPU 数据传输回 CPU

同样,使用 cudaMemcpy

CUDA_CHECK(cudaMemcpy(h_c.data(), d_c, size_bytes, cudaMemcpyDeviceToHost));

(6)验证结果,释放内存

验证结果使用已经复制到 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));}

3、编译&运行CUDA程序

需要使用 NVCC(NIVIDEA CUDA Compiler) 的编译器来编译程序;

NVCC 是 CUDA Toolkit 的一部分:


(1)编译流程

如下图所示:

cuda-6.png

流程如下:

  1. 每个 cu:Host 代码与 Device 代码分离(部分在CPU执行、部分在GPU执行)
  2. 每个虚拟架构:Device 代码编译出 fatbin
  3. Host 端使用系统的 C++ 编译器(如 g++)
  4. 链接(device,host)
  5. 最终获得可使用 GPU 的可执行二进制文件

补:GPU虚拟架构

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

cuda-7.png

以 A100 为例,A100 为 Ampere 架构;同时,各个架构间有区别;

因此提出:Compute Capability (CC)

  • 类似版本,表示能支持的功能和指令集合
  • A100 (Ampere 架构)是:cc8.0

虽然 A100 举例,但从 CUDA 编程的角度目前各种架构没有本质区别!

正因为如此,所以说CUDA是一个编程平台

同时,在编译时也可以指定架构编译选项:

  • -arch:指定虚拟架构,PTX生成目标。决定代码中可使用的CUDA 功能;
  • -code:指定实际架构,生成针对特定 GPU 硬件的二进制机器码(cubin);

(2)编译命令

通过:

nvcc add_cuda.cu -o add_cuda

即可编译!

运行:

./add_cuda

(三)、GPU性能测试

可以通过:

nvidia-smi

观察 GPU 利用率!

1、并行加法性能对比

分别对比:

  • CPU;
  • <<<1,1>>>
  • <<<256,256>>>
  • GPU 满载;

代码如下:

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);

不同情况下的性能如下:

cuda-8.png

可以使用 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 分析结果如下:

cuda-9.png

可以看到:<<<1,1>>> cudaLaunchKernel 占比非常高这是由于:

核函数调用有开销,在外面多次循环调用开销巨大!

因此,需要进行优化!


2、将循环放入核函数(Grid-strided loop)

由于在循环中频繁的调用核函数具有巨大的性能开销,因此可以将循环放入核函数中:

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);}

分析后结果如下图:

cuda-10.png

同时使用 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 的确少了很多!

这说明:

核函数的发射数量减少,因此总体执行时间降低!


3、CUDA并行加法性能评估(加速比)

指标:

加速比 = Tcpu / Tgpu

其中:

  • T_cpu 是任务在 CPU 上的执行时间;
  • T_gpu 是任务在 GPU 上的执行时间;

理想加速比与实际加速比

  1. 理想加速比:当任务完全并行化且没有任何开销时,加速比等于处理器核心数之比。例如,一个具有 1000 个 CUDA 核心的 GPU 理论上可以实现 1000 倍的加速(相对于单核 CPU)。
  2. 实际加速比:由于以下因素,实际加速比通常远低于理想值:
    • 任务中存在无法并行化的串行部分
    • 数据在 CPU 和 GPU 之间的传输开销
    • 线程同步和内存访问延迟
    • 算法在 GPU 架构上的效率低下

为什么<<<1,1>>> 比 CPU慢?

这是由于,单个 GPU 的核心实际上要比 CPU 的能力要弱!

实际上,GPU 是由于干活的人多,所以快!


4、CUDA并行加法性能评估(总耗时)

实际上观察 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]

总体的耗时应当是三个部分:

  • 总耗时 = TH2D + Tkernel + TD2H

并且,对于 <<<256,256>>> 来说:HtoD 和 DtoH 的耗时会远大于 kernel 的运行时间!

这就说明,来回的移动和复制数据比计算更消耗时间!

能否对这一部分进行优化呢?

后面的文章中会讲解!


(四)、设备信息

对于 GPU 而言:越多的线程 => 越大的并行度 => 越好的性能

GPU 最大可以启动的线程数可以参考:

也可以参考:


1、cudaDeivceProp

重点的几个参数:

  • maxGridSize(int[3]):x、y、z三个方向分别最多可支持的 block 数;
  • maxBlockSize(int[3]):每个 Block中x、y、z三个方向分别最多可支持的线程数;
  • maxThreadsPerBlock(int):每个 block 中最多可有的线程数

其中:Blocksize 需同时满足这两组条件:maxBlockSize、maxThreadsPerBlock:

  • x、y、z加起来不超过:maxThreadsPerBlock;
  • x、y、z各个方向不超过:maxBlockSize;

2、CUDA版本

查看 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!


后记

更加详细的内容,可以看:


附录

源代码:

参考文章:


一些免费的GPU资源

2025-07-24 15:37:04

在学习AI时,经常需要用到GPU资源;而有些时候我们手头并没有老黄的显卡,或者显卡已经不支持进行人工智能的学习了;

本文总结了一些常用的GPU资源;

同时,后续也会在博客更新一些和并行计算、人工智能相关的内容,敬请期待!


一些免费的GPU资源

一、Google Colab(推荐)

网址:

特点:

  • 提供 NVIDIA T4/P100/V100/A100(具体型号随机分配);
  • 免费用户每天最多可使用 12 小时(可能因资源调度而中断);
  • 可升级至 Colab Pro(9.99 美元/月)Colab Pro+(49.99 美元/月),享受更长运行时间和优惠优先级;
  • 集成 Jupyter Notebook 环境,适合深度学习和机器学习任务

二、Kaggle

网址:

特点:

  • GPU小时数:每周30小时。
  • GPU:提供Tesla P100,与Google Colab的T4相当。
  • 使用质量:非常好,很少断连。
  • CPU和内存:提供四个CPU和29GB RAM。
  • 易用性:易用,有类似笔记本的界面。
  • 存储:无持久性存储。

三、Paperspace Gradient

网址:

特点:

  • GPU小时数:无具体限制,质量不佳。
  • 存储:有持久性存储,数据不会丢失。
  • GPU:提供M4000 GPU,质量低于Google Colab的T4。

四、其他

1、AWS Sagemaker Studio Lab

特点:

  • GPU小时数:每天4小时,CPU小时数12小时。
  • GPU:提供T4 GPU,与Google Colab相同。
  • 使用质量:非常好,很少断连。
  • 易用性:需要在网站上注册。
  • 存储:有持久性存储。

2、Lightning AI

网址:

特点:

  • GPU小时数:每月22小时。
  • CPU:提供一个Studio,4个CPU完全免费。
  • 使用质量:非常好,连接稳定。
  • 易用性:非常好,提供VS Code界面。

3、百度 AI Studio

网址:

特点:

  • 免费提供 GPU 运行环境,支持常见型号(如 T4、P40 等,具体配置可能随时调整);
  • 集成 PaddlePaddle 以及 TensorFlow、PyTorch 等主流深度学习框架;
  • 类似 Jupyter Notebook 的在线编程环境,适合快速上手、学习和实验;

4、云平台注册赠费

  • Google Cloud Free Tier
    • 新用户可获得 300 美元免费试用额度(90 天内有效),体验包括 GPU 实例在内的众多云服务
    • 用户可自行创建 NVIDIA T4/V100/A100 GPU 实例(需手动配置)
    • 适用于大规模机器学习和 AI 训练
    • 注册时需绑定信用卡,但试用期间不会产生扣费
  • Microsoft Azure Free Tier
    • 新用户可获得 200 美元免费试用额度(30 天内有效),支持 GPU 虚拟机(如 NC/ND 系列)
    • 适用于 AI 深度学习训练和企业级应用开发
    • 需要信用卡验证,但试用期间不收费
  • AWS Free Tier(Amazon Web Services)
    • 免费层主要提供 750 小时 t2.micro 实例(不含 GPU),部分新用户可额外申请 GPU 实例(如 p3/g4 系列)
    • 提供 Amazon SageMaker 平台,支持机器学习项目的快速部署
    • 注册需绑定信用卡,确保试用过程不产生费用

其他资源:


附录

参考文章: