你这个状态太正常了——Python 和 C++ 的“运行”是两个完全不同的世界。我刚从 Python 切过来时,也被 build system 绕晕过。现在你不需要深入懂 Makefile 和 CMake 的全部原理,但必须掌握最基本的命令行编译,而不是依赖 IDE 的“播放按钮”。

这对你后续编译 NCNN、ONNX Runtime 源码,做性能测试、写 benchmark 极其关键。我做了一个对比,帮你快速转换思维方式:

  1. 一个文件时的等价操作

Python 的心智模型:写完了,直接跑。

python my_script.py

C++ 的心智模型:编译(把代码变成机器能执行的二进制文件)→ 执行。

比如你有 main.cpp:

# 1. 编译
g++ main.cpp -o my_program -std=c++17
# 这个命令的意思是:用g++编译器,把main.cpp编译(-c是只编译不链接,这里是编译+链接)成可执行文件my_program,使用c++17标准

# 2. 运行
./my_program
  1. 多个文件时的等价操作

Python:拆成 utils.py 和 main.py,import 就能用。

C++:比这复杂,但因为编译和链接是两个独立步骤。

# 你的项目结构
my_project/
├── main.cpp        # 有main函数
├── tensor.cpp      # 实现了Tensor类
└── tensor.h        # 声明了Tensor类

编译过程是这样的:

# 步骤1:分别编译每个 .cpp 文件,生成 .o 文件(目标文件)
g++ -c main.cpp -o main.o -std=c++17      # -c 是“只编译,不链接”
g++ -c tensor.cpp -o tensor.o -std=c++17  # 每个.o文件包含本文件的机器码,但“悬空”的函数调用还没填上地址

# 步骤2:把所有 .o 文件“链接”成一个可执行文件,把函数调用地址都填好
g++ main.o tensor.o -o my_program

在你之前看到的 make 背后,Makefile 文件里就是把这些编译链接步骤自动化了(哪个文件改了才重编译,哪个先编译,哪个后编译)。这一步是先有个感性认识,后面用到自然就懂了。

  1. 现代C++的Python化体验:CMake

手动 g++ main.o tensor.o … 一旦文件多了就疯掉。CMake 就是 C++ 世界的构建“管理器”,帮你生成 Makefile(或其他构建脚本)。 你甚至可以把它理解为 C++ 版的虚拟环境 + 依赖管理 + 打包工具。

一个最简单的 CMakeLists.txt 长这样:

cmake_minimum_required(VERSION 3.10)  # 声明CMake最低版本
project(MyTensorDemo)                 # 项目名

# 设置C++标准为17,并且要求编译器必须支持
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 这就是你的“源代码文件”,添新文件就往里加
add_executable(my_demo main.cpp tensor.cpp)

使用方法是:

# 你的习惯:python xxx.py
# 现在:在项目根目录下执行
mkdir build && cd build  # 标准做法,把所有编译产物隔离在build目录
cmake ..                 # 让CMake读取上级目录的CMakeLists.txt,生成Makefile
make                     # 根据生成的Makefile实际编译
./my_demo                # 运行你的程序

这里有一个关键的心智转变:

· Python 是解释型,你运行的是 .py 文件,改完代码直接运行就生效。
· C++ 是编译型,你运行的是 build/ 目录里生成的二进制文件。改代码后,必须重新 make 才能生效。

  1. 调试习惯的降维打击

习惯了 Python 的 print() 和 pdb 交互式调试,到 C++ 这里最难受的就是不能运行到一半停下来随便打印对象。

但 C++ 有更好用的东西:真正的、能看内存、看汇编的调试器。

在 macOS/Linux 下用 lldb,Windows 下用 Visual Studio 内置的调试器。推荐你先在 VS Code 里配好调试环境,这样:

  1. 在 main.cpp 某行左侧点一下,设置断点(小红点)
  2. 按 F5 启动调试
  3. 程序运行到断点自动停下,你可以:
    · 看变量当前值(甚至二维数组、vector 的内容)
    · 查看调用栈(谁调用了当前函数)
    · 单步执行(F10 逐行,F11 进入函数内部)

这对你后续阅读 NCNN 源码至关重要——你可以亲手运行一个卷积,然后单步进入 convolution_arm.cpp,亲眼看见数据在寄存器里怎么流、怎么被 SIMD 指令处理。

本周行动建议

花一小时,脱离 IDE 的播放按钮,用下面这个流程跑起来:

  1. 打开 VS Code,新建一个文件夹
  2. 写一个 main.cpp,里面就实现一个二维数组的矩阵乘法(60行代码量级)
  3. 使用 CMake + 命令行编译运行,在终端看到结果
  4. 配置 F5 调试,在关键循环里设断点,查看变量值

做完这一步,你就拥有了编译优化工程师最基础的肌肉记忆:改代码 → 编译 → 运行 → 设断点观察 → 改代码。 这个循环是后面所有 SIMD 优化、内存布局调整的核心工作流。

环境确认一下:你目前在 Windows 上用 Visual Studio,还是在 macOS/Linux 上用其他工具?我可以给你一个针对你环境的、能复制粘贴就能跑通的 CMake 示例。