自由学习记录(108)

真正高效的 CUDA 程序,核心在于让数据尽可能留在 GPU 上计算完成再回传

PCIe 传输,过程慢且昂贵。

性能优化点

  1. 尽量减少 Host↔Device 传输次数。

  2. 使用 pinned memory(页锁定内存) 提高 PCIe 传输速度。

  3. 使用 Unified Memory(统一内存) 让CUDA自动管理数据迁移。

  4. 尽量在 GPU 内部复用数据,避免频繁回传到 CPU。

// 1. 在 CPU 上分配主机内存

float *h_data = (float*)malloc(size);

// 2. 在 GPU 上分配设备内存

float *d_data;

cudaMalloc(&d_data, size);

// 3. 从 CPU 内存传输到 GPU 内存

cudaMemcpy(d_data, h_data, size, cudaMemcpyHostToDevice);

// 4. CPU 调用 kernel(GPU 执行)

myKernel<<<grid, block>>>(d_data);

// 5. GPU 执行完后传回结果

cudaMemcpy(h_data, d_data, size, cudaMemcpyDeviceToHost);

// 6. 释放内存

cudaFree(d_data);

free(h_data);

数据在两者之间来回传输"的运行机制

https://www.youtube.com/watch?v=G-EimI4q-TQ

CPU 与 GPU 的内存体系是物理分离的

  1. CPU内存

    • 使用系统主内存(通常是DDR4/DDR5)。

    • 存放程序指令、控制逻辑、数据初始化结果。

    • 由操作系统与CPU调度器统一管理。

  2. GPU内存

    • 使用显存(GDDR6或HBM等高带宽内存)。

    • 专门为并行计算线程存放输入输出数据、纹理、矩阵、缓冲区等。

    • 不与CPU共享地址空间(除非使用统一内存机制)。

数据传输的关键路径

  1. 传输通道:PCI Express(PCIe)总线

    • 负责在CPU内存与GPU显存之间移动数据

    • 带宽远低于GPU显存内部带宽,例如:

      • GPU内部带宽:几百 GB/s~1 TB/s。

      • PCIe 4.0 ×16 带宽:约 32 GB/s。

    • 因此数据传输是整个异构计算流程的主要瓶颈。

  2. 传输方向与阶段

    • Host → Device :CPU把数据从主内存拷贝到GPU显存(例如训练前加载模型权重)。

    • Device → Device :GPU内部线程之间共享或复制数据(通过共享内存或全局内存)。

    • Device → Host :GPU计算结束后,把结果传回CPU(例如渲染输出帧或推理结果)。

1. 异构模型概念

  • CPU = Host(主机):运行普通程序,负责整体调度与数据管理。

  • GPU = Device(设备):运行并行计算部分,称为"kernel"。

  • 执行流程 :CPU启动(launch)GPU上的kernel,GPU执行完再返回结果。

    重点:CPU是指挥者,GPU是执行者。


2. CUDA编程框架

  • NVIDIA提供的CUDA允许一个程序包含CPU端与GPU端的部分。

  • 编译器会将同一个源文件分离成两套代码:

    • CPU部分(host code)

    • GPU部分(device code)
      重点:CUDA的核心是让开发者在同一代码中写出异构执行逻辑。


3. 内存结构与数据传输

  • CPU与GPU各自拥有独立的内存(DRAM)。

  • CPU控制整个过程,负责分配GPU内存与数据搬运。

  • 数据需要通过总线(如PCIe)在CPU与GPU间来回传递。
    重点:数据传输是性能瓶颈之一。

这页PPT的重点是讲解异构编程模型(Heterogeneous Programming Model),即CPU与GPU协同工作的方式。

老师会着重讲CPU 与 GPU 的设计目标差异:延迟(Latency) vs 吞吐量(Throughput)

重点内容如下:

  1. 延迟 (Latency)

    • 定义:完成单个任务所需的时间。
    • 指标:秒或毫秒。
    • CPU优化方向:减少单个任务完成时间。
    • 场景:系统响应、逻辑判断、顺序执行的代码。
  2. 吞吐量 (Throughput)

    • 定义:单位时间内完成的任务数量
    • 指标:jobs/hour、pixels/s、GFLOPS。
    • GPU优化方向:最大化同时处理的任务数。
    • 场景:图形渲染、神经网络推理、大规模矩阵运算。
  3. 核心对比逻辑

    • CPU追求快地完成一个任务
    • GPU追求同时完成更多任务
    • 所以CPU设计强调复杂控制逻辑、缓存层次;GPU强调并行流水线与计算密度。

总结一句话:这页PPT的重点是说明------

"CPU专注降低单任务延迟,GPU专注提升总体吞吐量。"

CPU和GPU都由晶体管构成,但晶体管的功能分配不同:

  1. 逻辑控制型晶体管(Control Transistor)

    • 负责判断、分支、调度。
    • 在CPU中比例高,用于执行复杂指令和控制流。
  2. 算术执行型晶体管(Compute Transistor)

    • 用于加法器、乘法器、ALU、FPU。
    • GPU中比例极高,因为主要执行并行算术操作。
  3. 存储缓存型晶体管(Memory/Cache Transistor)

    • 构成寄存器、L1/L2/L3缓存、显存控制器等。
    • CPU缓存层次多,因此在此部分耗费大量面积。
    • GPU更多依赖外部显存(VRAM),片上缓存较少。
  4. 互连与控制通路型晶体管(Interconnect / Bus Control)

    • 实现数据在各单元之间的高速传输。
    • CPU内部互连复杂,用于协调多级缓存和执行单元。
    • GPU为高带宽设计,连接大量计算核心(SM或CU)。

CPU的晶体管分配约有以下特点:

  1. 控制逻辑部分占比高

    • 包含分支预测器(Branch Predictor)、乱序执行单元(Out-of-Order Execution Unit)、指令调度器(Scheduler)、寄存器重命名表(Register Renaming Table)。

    • 这些模块让CPU可以处理分支频繁、指令依赖复杂的程序。

  2. 缓存层次结构占用大量面积

    • L1/L2/L3缓存层次多,容量大,保证低延迟数据访问。

    • 每个核心都带有独立或共享的Cache,用于减少主存访问瓶颈。

  3. 算术逻辑单元(ALU)数量相对少

    • 因为大量面积和功耗都给了控制电路和缓存,CPU每个核心的执行单元数有限。
  • CPU 把晶体管大量分配给控制和缓存,以支持灵活的分支和复杂任务。

  • GPU 减少控制电路,用更多晶体管堆计算核心来提升并行吞吐。

这会引出两个重点:

  1. 为什么CPU适合通用任务而GPU擅长并行计算。

  2. 这种架构差异导致的功耗与性能对比。

重点在CPU与GPU硬件架构的对比逻辑。老师大概率会讲以下几个核心点:

  1. 控制复杂度差异

    • CPU的控制逻辑复杂(Complex control HW),能灵活调度各种任务。

    • GPU的控制逻辑简单(Simpler control HW),控制单元占比低,更多晶体管用于计算。

  2. 优劣取舍

    • CPU:灵活、通用、适合少量复杂任务,但功耗高。

    • GPU:高并行度、功耗更低,但编程受限,适合大规模数据并行。

  3. 背后机制

    • CPU适合逻辑分支多、指令不规则的任务。

    • GPU适合SIMT模型(Single Instruction, Multiple Threads)的大规模数据运算。

内存带宽的利用,?意思是说原本的是每个线程独立的去取,然后就多次访问内存,而之后是直接把读取一大块比如四分之一块进显存,此时所有gpu同时去里面拿来算,减少了反复访问的时间浪费(但是增加了显存带宽的占用,所以选择的T如果太大,在实际使用中会极大占用其他的程序需要使用的显存,,所以T的选择需要一定的随机应变?)

ChatGPT said:

你理解方向完全正确,只需要我帮你精确化几个关键层次。

每个线程块把需要的一小块 A、B 从 global memory 一次性拷入 shared memory(片上高速 SRAM,延迟约是 global 的 1/100)。

cache tiling 的思路

把矩阵分成小块(tile 或 block),让每次运算只在缓存/共享内存中处理一块完整 tile

复制代码
for (ii = 0; ii < N; ii += T)
  for (jj = 0; jj < N; jj += T)
    for (kk = 0; kk < N; kk += T)
      // 在 T×T 子块上做完整乘法
      for (i = ii; i < ii+T; i++)
        for (j = jj; j < jj+T; j++)
          for (k = kk; k < kk+T; k++)
            C[i][j] += A[i][k] * B[k][j];

每个 tile 大小 T 选得刚好能装进缓存或共享内存。

优势:

  • A、B 的一部分被载入一次,多次复用;

  • 内存带宽负担减少;

  • 计算单元利用率提升。

cache tiling 是一种优化手段,不是新算法。

它保持结果不变,但极大改善了访问模式与性能。

在 GPU 上,"cache tiling" 演化为:

  • 每个线程块(block)处理一个 tile;

  • 每个线程负责 tile 内的一个或数个元素;

  • tile 数据放在 __shared__ 内存

  • 线程块间用同步 (__syncthreads()) 协调。

所以 CUDA 的"tiled matrix multiplication" 实际上是"cache tiling"思想在 GPU 上的并行实现。

  1. ls → 列出当前目录内容:

    cacheTiling LICENSE matrixMul README.md vectorAdd

    说明当前目录名是 from_scratch,里面有这些子目录。

  2. cd cacheTiling → 进入名为 cacheTiling 的子文件夹。

  3. vim matrix_mul.cu → 在该目录下用 Vim 打开(或新建)名为 matrix_mul.cu 的 CUDA 源文件。

所以你理解正确:

这几步是在项目 from_scratch 目录中进入 cacheTiling 子文件夹,并在里面新建/编辑 CUDA 程序文件 matrix_mul.cu

它确保 GPU 的并行算法在数学意义上等价于 CPU 串行算法。

main() 里:

  1. 调用 CUDA 核函数;

  2. cudaDeviceSynchronize() 等待 GPU 执行完;

  3. 调用 verify_result() 验证。

GPU 内核不会抛异常或中断逻辑错误,只会返回"某些值"。

验证函数是唯一能证明计算逻辑正确的手段,尤其在调试:

  • tile 大小、共享内存索引、边界条件 都可能出错;

  • GPU 的并行执行次序不确定,逻辑错误可能隐蔽;

  • CPU 结果可视为"真值表",用于对比验证。

条件 是否可运行 说明
N 是 tile 尺寸的倍数 ✅ 完全对齐
N 不是 tile 尺寸的倍数 ✅ 需边界判断
tile 尺寸(blockDim)固定 ✅ 任意 N 可运行,只影响效率

这段 CUDA tiled 矩阵乘法代码可以用于任意 N(不必是 4 或 16 的倍数),因为它在循环里专门写了:

复制代码

for (int i = 0; i < (N + dim - 1) / dim; i++)

这里 (N + dim - 1) / dim 是一种常见的向上取整技巧,用于应对矩阵尺寸不是 tile 大小整数倍的情况。

所以:

  • 如果 N=32、64、128 → 刚好整除,所有 tile 都满。

  • 如果 N=30、45、50 → 最后一个 tile 只覆盖部分区域,线程会读到边界之外的索引,这种情况要配合边界判断:

    复制代码

    if (row < N && col < N) c[row * N + col] = sum;

    否则会访问越界。

想象 A 和 B 是两本超大的表格。

  • 你(block)带着一个 16×16 的小"放大镜"去看一部分。

  • 每次你移动放大镜(tile),抄下对应部分做乘法。

  • 一直移动,直到把整行整列看完。

  • 最后每个位置得出 C 的一个元素。

每次循环,加载 A 和 B 的相邻 tile。

每个线程负责 tile 中一个像素的计算。

累积到最终结果 C。

矩阵太大,显存访问太慢,如果直接让每个线程从全局内存读整行整列,会反复访问相同数据 → 极低效率

术语 含义 作用
tile 矩阵的一小块(例如16×16) 优化内存访问与共享计算
block GPU 的线程块(执行单元) 通常负责一个 tile
flatten (压平) 把二维矩阵变成一维数组 仅是存储方式,与 tile 无关

for (int i = 0; i < (N + dim - 1) / dim; i++) {

A[ty * dim + tx] = a[row * N + i * dim + tx];

}

这一段是:

将大矩阵 a 的不同 tile 段拷贝进共享内存 A。

i 控制"tile 在矩阵中的水平移动次数"。

因此:

"across the length of the grid" 指沿着矩阵的宽度(列方向)不断移动 tile 进行分块加载计算,

而不是把矩阵压平。

也就是说:

  • tile ≈ "本线程块正在处理的矩阵局部块"

  • "move the tile across the length of the grid" 表示:

    在循环中,线程块不断加载矩阵的不同部分(每次移动一个 tile 的宽度),重复累积计算,直到整个矩阵的乘法完成。

  • Matrix tile:矩阵被划分成若干个固定大小的小方块(如 16×16)。

  • 每个线程块(block)负责计算一个 tile 的结果。

  • 共享内存(__shared__)中存放的是当前 block 对应的 tile 的局部数据。

典型的 CUDA 矩阵乘法 (tiled matrix multiplication)
tile 在这里不是把矩阵"压平成一维数组",而是指 矩阵被分块(tile/block)成小矩阵子块 的思想。

在 CUDA 里:

  • block(线程块) 是执行的基本调度单位,由多个线程组成。

  • grid(网格) 是由若干 block 构成的整体。

  • chunk 不是 CUDA 的正式术语,它通常出现在代码层或内核设计中,表示开发者人为划分的一段数据或任务片段

也就是说,chunk 通常指"要处理的数据的一部分",而不是 GPU 的硬件/调度单位。

int chunkSize = 256;

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

int start = idx * chunkSize;

这里每个线程负责处理一个 chunk 数据,但这只是开发者定义的概念,CUDA 自身并不知道什么是 chunk。

总结:

  • block:CUDA 官方定义的线程调度单元。

  • chunk:开发者自定义的数据切片,不是 CUDA 的执行单位。

矩阵乘法是二维数据的操作,但它的计算结构天然是三维的。张量化是把这个三维关系显式化,以便从代数层面优化乘法次数。

算法优化点:张量分解

  • Strassen 等算法的核心,就是寻找把这个三维张量 TTT 分解成若干个秩为1的张量之和(每个秩1张量对应一次乘法)。

  • 减少所需的秩(即最少乘法次数) = 优化矩阵乘法。

  • 所以后续算法研究方向就是"寻找矩阵乘法张量的最小秩分解"。

张量分解(Tensor Decomposition)

  • 矩阵乘法可以视为三维张量乘法。

  • 不同算法对应于对这个张量的不同分解方式。

  • 比如 Coppersmith-Winograd 用代数几何技巧在更高维度上找到稀疏分解,从而降低指数。

Strassen 是 2×2 分块。

  • 后续算法(如 Schönhage、Coppersmith-Winograd)把++更大矩阵看作高维张量,通过张量分解找到比"7乘法规则"更优的组合模式++。

  • 这些算法在理论上继续减少乘法数量,不改变 2×2 层内部公式,而是复用公式间的中间结果。

  • 普通 2×2 矩阵乘法需要 8 次乘法 + 4 次加法。

  • Strassen 算法把它优化为 7 次乘法 + 18 次加法。

  • 虽然加法数量多了,但乘法的代价远高于加法(在硬件上乘法≈数十倍慢)。

在递归层面:

当矩阵被不断分块时,节省的一次乘法会在每层递归放大。

例如 O(n3) 的算法经过这种优化,复杂度降为 O(n2.807)。

即使加法更多,整体运行时间随矩阵规模增大反而更快。

减少高代价的乘法、增加低代价的加法是典型的计算权衡。Strassen 算法在大规模矩阵中能显著提速。

https://www.youtube.com/watch?v=sZxjuT1kUd0

AI 的贡献

DeepMind 的 AlphaTensor 使用强化学习,在算子空间中自动搜索矩阵乘法的最优分解方式,相当于在"如何切片、怎样重组"上找到了比人类更高效的策略。

可视化类比

你的"切成多片的面包"类比接近于 张量分解(tensor decomposition):

  • 每层(切片)是矩阵。

  • 算法寻找在这些层之间最有效的组合方式,使得最终计算结果正确但乘法更少。

Tensor ≠ 简单的立方体矩阵

  • 矩阵是二维数组(两个索引:行、列)。

  • Tensor 是更一般的多维数组,可以是 3D(像立方体)、4D(视频帧序列)、甚至更高维度。

  • 每一维代表一种"轴",比如图像张量 (Height, Width, Channels)。

但是2022年的时候,有新的人提出新的理论,但是要求矩阵中的数字全是0和1,并且生效的范围是4x4的矩阵相乘

二x二阶的矩阵乘法,最多只能7,并且不可能6,后面被另一个人证明了

一个CPU核心可以至少 运行一个线程,但现代核心通常支持同时运行多个线程 ,这取决于是否启用了超线程(Hyper-Threading,SMT)

结构:

  • 无超线程核心:1核 = 1硬件线程。

  • 有超线程核心:1核 = 2硬件线程(每个线程独立调度指令,共享执行单元和缓存)。

例:

  • Intel Core i5-12400:6核12线程 → 每核2线程。

  • AMD Ryzen 5 7600:6核12线程 → 同样SMT。

总结:

一个物理核心可运行1~2个线程(取决于是否启用超线程)。

精度 名称 位数 常用场景 特点
FP64 双精度 64 bit 科学计算、数值仿真 精度最高,速度最慢
FP32 单精度 32 bit 游戏渲染、通用GPU计算 平衡精度与性能
FP16 半精度 16 bit 深度学习推理、AI训练 精度低但速度快、能耗低

还有这东西,之前会用的上,但现在没什么劲去用了

https://mermaid.js.org/#/

用途:衡量 GPU、CPU、TPU 等计算硬件的浮点计算吞吐能力。

  • 1 Tflop = 10¹² 次浮点运算/秒。

  • 通常分为FP32(单精度)、FP16(半精度)、FP64(双精度)等指标,分别表示在不同精度下的理论峰值性能。

示例:

  • NVIDIA GeForce RTX 4090 单精度 ≈ 82 Tflops。

  • A100 Tensor Core GPU 半精度 ≈ 312 Tflops。

区别:

  • 理论值 = 计算单元数 × 每周期指令数 × 频率。

  • 实际性能 依赖内存带宽、指令混合、瓶颈等,不会达到理论 Tflops。

"Teraflops" 是 trillion floating-point operations per second 的缩写,即 每秒一万亿次浮点运算

主要内容

  • 作者:Ian Buck、Tim Foley、Daniel Horn、Jeremy Sugerman、Kayvon Fatahalian、Mike Houston、Pat Hanrahan,均隶属 Stanford University 计算机科学系。 Stanford Graphics+2Stanford Graphics+2

  • 时间/出版:于 2004 年发表于 SIGGRAPH / ACM Transactions on Graphics。 ACM Digital Library+1

  • 核心目标:提供 Brook 这个编程环境(包括扩展 C 语言、编译器和运行时系统),将 GPU (图形处理单元)视作"流处理协处理器"用于通用计算。论文中分析了在何种算法和何种条件下 GPU 相比 CPU 更具优势。 Stanford Graphics+1

  • 方法/贡献包括:

    • 定义流(streams)、核(kernels)、归约(reductions)等抽象以适配 GPU 计算模型。 Stanford Graphics+1

    • 虚拟化 GPU 硬件限制(如输出数、纹理大小、用户数据结构)以提升可移植性。 Stanford Graphics

    • 提出简单成本模型(data-transfer + kernel cost)以判断 GPU 计算是否优于 CPU。 Stanford Graphics

    • 实验评估:包括 SAXPY、SGEMV、图像分割、FFT、光线追踪等代表性应用。结果显示:在某些情况下,使用 Brook 的 GPU 实现比 CPU 快至 7×。 Stanford Graphics

是否为 NVIDIA 员工撰写?

不。根据论文作者信息和其所属机构:作者来自 Stanford University。论文并未列出 NVIDIA 作为作者单位。故该论文并非由 NVIDIA 员工主导撰写

https://www.youtube.com/watch?v=pPStdjuYzSI

https://graphics.stanford.edu/papers/brookgpu/brookgpu.pdf

官方 PDF 《CUDA Programming Guide Version 1.0》标注版本 1.0 日期为 2007-06-23NVIDIA Developer Download+1

NVIDIA 成立早(1993 年)但 CUDA 平台在 2007 年才作为通用计算平台发布。

TPU 直接取消指令概念,只让数据按固定路径流过 → 零指令解码成本

  • 电路面积(控制单元约占核心面积 30%)

  • 时钟周期(控制器延迟不可省)

  • 功耗(控制信号切换频繁)

主要的优势比较参数,

GPU 每个周期都要做:取指令 → 解码 → 分派 → 执行 → 写回

即便只是做 A*B+C,也必须经过完整的指令路径。

这些「控制逻辑」虽然在架构上看是辅助模块,但在硬件层面占用大量晶体管、功耗与时钟周期。去掉它们,等于把电路从"智能机器"变成"纯算机器",差距是数量级的。

无需控制器、指令解码、缓存协调。

GPU 最初是为图形渲染而设计的,核心逻辑是 SIMT(Single Instruction, Multiple Thread)

也就是上万个小核心(CUDA Core)并行执行相同指令,适合像素、粒子、顶点这种可并行任务。

这种设计的优点:

  • 通用:几乎任何可并行的任务都能跑。

  • 可编程:程序员能写任意逻辑(条件分支、循环、访存控制)。

但也带来几个致命问题:

  • 调度复杂:需要线程分配、寄存器分配、Warp 同步。

  • 访存不规律:不同线程访问显存不连续时性能急降。

  • 能耗高:指令调度、寄存器重命名、缓存一致性都会耗电。

→ GPU 擅长"宽而灵活的并行",不是"死板而极致的矩阵流计算"。

差距确实大。出现 TPU 这种"重新设计"的硬件,是因为 GPU 的可编程通用性带来了结构性浪费,而在深度学习这种固定运算模式下,这种浪费变成瓶颈。

特性 GPU TPU
可编程性 高(通用 GPGPU) 低(固定硬件路径)
通用任务 游戏、科学计算、物理仿真 几乎无优势
深度学习矩阵计算 强,但需调度和显存管理 极强,直接硬件流水执行
延迟 稍高 极低
成本与生态 商业化成熟 仅云端可用(Google Cloud TPU)

TPU:

  • 主要由一个或多个 Systolic Array(典型如 128×128)组成。

  • 该阵列每个时钟周期完成固定矩阵乘加:

    C=A×B+C

  • 没有通用逻辑单元,不能自由编程。

  • 更像"专用计算电路"(ASIC),只为神经网络线性代数服务。

GPU:

  • 内部是数千个可编程「核心」(CUDA Core)。

  • 每个核心能执行各种浮点、逻辑、分支操作。

  • 张量计算只是 GPU 功能的一部分(NVIDIA 通过 Tensor Core 扩展支持)。

16-bit floating point data type

Bfloat16 ( bf16 ) is a 16-bit floating point data type based on the IEEE 32-bit single-precision floating point data type ( f32 ). Both bf16 and f32 have an 8-bit exponent. However, while f32 has a 23-bit mantissa, bf16 has only a 7-bit one, keeping only the most significant bits.

cl.exe

代表 C Language 编译器(C + L)。

Visual Studio C++ 工具链有两个目标平台:

架构 对应路径 主要用途
x86 Hostx86\x86\cl.exe 旧 32 位项目
x64 Hostx64\x64\cl.exe CUDA、现代桌面程序、游戏引擎等

你当前的 where cl 结果:

D:\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx86\x86\cl.exe

说明只有 32 位工具链,没有 64 位版本。

Unity 自己有 .unity 场景文件和 .meta 索引系统,所有资源与脚本都受 UnityEditor 管理。

但当 Unity 需要与 Visual Studio 协同时(例如脚本编辑、调试),它会自动生成 .sln.csproj 文件来告诉 VS:

"这里是一个 Unity C# 项目,脚本都在这些目录,编译参数如下。"

也就是说:

  • .sln 对 Unity 来说是 IDE 交互层

  • 对 Visual Studio 来说是 项目构建层

.sln 在 Visual Studio 的意义

  • 代表一个解决方案(Solution),相当于一个项目集合。

  • 内部列出若干 .vcxproj(C++ 工程)或 .csproj(C# 工程)。

  • 控制:

    • 源码文件位置

    • 构建目标(Debug/Release)

    • 依赖库路径

    • 使用的编译器与参数

Visual Studio 打开 .sln 就能重建整个工程环境。

为什么看起来像"直接执行"

当你在 VS 里点击"运行"(或按 F5):

  • VS 先解析 .sln

  • 调用 nvcc 编译 CUDA 源码;

  • 链接生成 .exe

  • 自动启动该 .exe

    因此看起来像是 ".sln 能直接执行"。

实际上 .sln 只是工程描述文件,执行的是编译后生成的二进制。

相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习