CHAPTER 46 / Cerebras · 晶圆级计算

CSL 与 host:装载、启动、回读是一份共同合同

为什么一个局部函数还不构成可运行的设备程序?

阅读与推演约 55 分钟练习时间另计

这一章要弄清楚

  • 区分layout、PE程序和host职责
  • 识别编译参数与运行参数
  • 解释导出符号和完成协议

先备知识:错误处理与输入解析 / WSE 与 PE:把数据放在拥有它的计算节点旁边

C++20 本机逻辑模型;不是设备仿真或性能测量。厂商语法片段未在设备或 SDK 执行,实际 API 以匹配版本的公开文档为准。

三份代码合作完成一次运行

一个完整CSL工作流通常不只有一个计算函数。公开教程区分描述程序空间布局的代码、在PE上执行的代码,以及负责装载、数据传输和调用的host程序。你可以先把它们看成座位表、每个座位的工作说明和外部协调者。缺少任一合同,局部算式正确也可能无法运行或回读。

Layout描述使用的PE区域以及代码如何放置;PE程序声明本地数据并实现计算;host根据编译产物与导出符号准备输入、启动工作、取得结果。不同SDK版本还可能提供不同布局接口,不能把某个版本的文件形状当永远固定的语言要求。这里依据公开传统layout/PE/host教程解释职责。

CSL像Zig,但不是Zig

公开语言入门展示了带类型的变量、函数、循环和以@开头的builtins。语法相似不代表可以直接用Zig编译器构建设备程序,也不代表全部Zig库与语言特性都可用。先学少量足以表达局部数据与操作的语法,再进入路由、任务和数据描述符,能减少初学负担。

只读这一份累积函数需要的 CSL 语法(补充,另估20分钟)

写法 在下面片段中的含义
var total: i32 = 0; 声明可修改的 total,冒号后给类型,等号后给初值;这里位于函数外,是顶层变量
i32 32 位有符号整数,范围为 −2147483648 至 2147483647;本题不让加法超出范围
fn add_value(value: i32) void 定义函数,形参 value 的类型是 i32void 表示没有返回值
{ ... } 函数体的起止;其中的分号结束一条语句
total += value; 用原来的 totalvalue,把和写回 total

CSL 的函数形参是只读的;此函数修改的是 total,不是 value。不要把 C++ 可重新赋值的普通形参规则直接搬过来,也不要从这份签名推断底层一定按值复制还是按引用传递。

**纸上走一次:**初始 total=0。设调用 add_value(3),函数体读到 0 和 3,写回 3;接着调用 add_value(-1),读到 3 和 −1,写回 2。每次都没有返回数值,观察的是同一份 total 的变化。这里是假定调用发生后的语言推演;这份定义没有自动建立 host 导出、PE 布局或启动协议。

读法来自 CSL Syntax 的变量、函数与运算符,核对日期 2026-09-08。下方片段保持未编译状态,不把 C++ 累积模型当作 CSL 验证。

// CSL局部函数语法示意;不是完整可装载程序,未编译。
var total: i32 = 0;
fn add_value(value: i32) void {
    total += value;
}

这段代码只展示局部累积。要从host调用或回读,还需按匹配SDK规则建立导出、布局和运行时协议。缺少这些内容不是加一个main函数就能自动解决。

Host应沿明确状态前进

例一给原创CPU模型定义Created、Loaded、Running、Finished四个状态。未load就run被拒绝;进入Running后才接受计算;结束后才能取结果。真实SdkRuntime的完整状态与API语义以文档为准,本例不是其实现或替身。模型只帮助检查“先后条件”这个普遍问题。

公开host示例通过SdkRuntime取得符号标识,load、run,复制输入,launch,复制结果,最后stop。部分示例的设备函数需要解除command stream阻塞,使后续host命令能继续。计算得到数值与向host声明可继续,是两个必须一起完成的责任。

从启动到“host 可以继续”(补充,另估25分钟)

先备是一个已经构造的 runner、与它匹配的编译产物,以及已导出的、无实参的 host-callable 函数 compute。沿用前面 TTNN 小节的 Python 调用读法runner.load() 是调用对象上的方法,括号为空表示这里没有传实参;"compute" 是符号名称字符串;nonblock=False 是名为 nonblock 的参数取布尔值假。

顺序 当前公开 API 合同 不能由此推出什么
load() 将编译二进制装载到 simfabric 或 WSE 尚未证明用户计算已完成
run() 启动运行,使程序等待 host runtime 命令 不是自动执行任意名为 compute 的函数
launch("compute", nonblock=False) 启动该导出的 host-callable 函数,检查实参类型;本次调用阻塞到该操作完成 不会替代省略的输出回读
stop() 等待已有传输和函数调用命令完成,再停止运行;之后此 runner 不再接新命令 不会凭空生成一份此前没有请求回读的 host 结果

**纸上走一次:**假定 compute 的任务是把某个已约定设备结果写成 9,并按对应程序的协议通知 host 可以继续。走完 loadrun 后仍不能读出这个 9;阻塞 launch 返回后,本次操作已满足完成条件,但 host 还须按导出符号、PE 区域、元素数和 dtype 安排回读,才能比较结果。若该程序要求显式解除 command stream 阻塞,只算出 9 却漏掉结束协议,不能据此预测阻塞调用已经返回;下方代码没有展示这部分 PE 实现。

本节没有运行 SDK。依据 SdkRuntime APIloadrunlaunchstop 合同,核对日期 2026-09-08;当时公开发布说明最高版本为 SDK 2.10.0(2026-03-15,接续 1.4.0)。真实环境仍须记录安装版本,前述 CPU 四状态模型只检查自己定义的顺序。

# 公开SdkRuntime API顺序示意;未在SDK/设备执行。
# runner已按匹配版本构造,编译产物有对应导出。
runner.load()
runner.run()
runner.launch("compute", nonblock=False)
# 根据导出符号、PE区域、元素数与dtype执行回读。
runner.stop()

名称一致不代表数据合同一致

Host查找result符号成功,只证明找到约定名称;还要核对元素类型、每PE元素数、区域宽高、排列方式及目标数组大小。例二用一个符号表拒绝未导出的名字。实际错误也可能来自同名符号对应了旧编译产物,因此必须记录当前build与运行参数。

编译期参数改变布局或局部静态资源时,通常需要匹配的重新编译产物;运行期参数不能随意突破已编译的容量。把四个PE的host传输参数交给只编译两个PE的程序,不是普通数组长度变化,需要同时审查布局、容量与传输合同。

从最小例子开始排查

先确认单PE、一个导出数组、一种dtype和一个易手算操作,再增加元素数与PE数量。若host卡在回读,检查计算是否到达结束协议、数据流是否仍在等待、传输参数是否一致。不要直接增加超时时间掩盖永不满足的条件。

本章不使用私有SDK源码或项目结果。公开示例仓库提供按release匹配的例子,并明确不同release不保证完全兼容。实际动手时把SDK、例子tag与命令作为一组保存;本章C++状态和符号模型通过,不意味着CSL编译、simulator或硬件运行已完成。

观察 · 推演

原创host生命周期模型

stateCreatedrun拒绝01 / 03 · PIPELINEstateCreatedrun拒绝01 / 03 · PIPELINE
Created

未装载时不允许run。

1 / 3
阅读完整推演文字
  1. Created

    state:Created;run:拒绝

    未装载时不允许run。

  2. Running

    state:Running;input:4 5

    装载并启动后才能计算。

  3. Finished

    state:Finished;result:9

    结果可供回读。

跟着例子,走完一遍

例题 01C++20 · 本机可运行

拒绝错误生命周期顺序

Created状态不能run;load后run,再计算得到9。

  1. 检查未装载状态
  2. 依次load/run/finish
  3. 完成后读取9
42-a.cpp
下载
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <stdexcept>

void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }

int main() {
 enum class State {created,loaded,running,finished};
 State state=State::created;
 require(state!=State::loaded);
 const bool early_run=state==State::loaded;
 state=State::loaded; require(state==State::loaded);state=State::running;
 int result=4+5; require(state==State::running);state=State::finished;
 require(!early_run && state==State::finished && result==9);
 std::cout<<"early_run=blocked result="<<result<<'\n';
}

如何编译和运行下载的 .cpp 文件 →

结果与解释

early_run=blocked result=9。

原创状态合同,不是SdkRuntime内部实现。

在本机运行这个例子

下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 42-a.cpp -o example && ./example

预期标准输出:

early_run=blocked result=9
例题 02C++20 · 本机可运行

导出名称合同

符号表只有input=3、result=7;查result与missing。

  1. 成功查找result
  2. 检查missing不存在
  3. 不以默认插入制造假符号
42-b.cpp
下载
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <map>
#include <string>
#include <stdexcept>

void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }

int main() {
 const std::map<std::string,int> symbols{{"input",3},{"result",7}};
 const auto found=symbols.find("result"),missing=symbols.find("missing");
 require(found!=symbols.end() && missing==symbols.end());
 std::cout<<"result_id="<<found->second<<" missing=rejected\n";
}
结果与解释

result_id=7 missing=rejected。

find不会把不存在名称插入表。

在本机运行这个例子

下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 42-b.cpp -o example && ./example

预期标准输出:

result_id=7 missing=rejected
常见错误

计算结束却没满足完成协议

局部数值已算出,host仍在等待后续命令。

修正思路:检查匹配SDK要求的结束/解阻塞及传输合同,不以延长超时替代。

轮到你动手

先写预测或代码,再按需打开提示。完整答案用于对照自己的推理。

练习 1

写出只有一个输出数组的最小host核对清单。

给我一点提示
  1. 名称只是开始
  2. 把传输两端对起来
查看答案与推理

核对编译产物、导出名、dtype、每PE元素数、区域坐标/宽高、排列与host容量;再确认调用完成和回读顺序。先用单PE、两项已知值验证,不直接扩大到多PE。

练习 2

把符号表查找改成map[name]为什么可能误导?

给我一点提示
  1. operator[]可能插入
  2. 零值不代表合法ID
查看答案与推理

不存在名称会被默认插入一个值,可能让代码看似成功。使用find/at并显式处理缺失,确保不把查找失败变成虚构的符号标识。

把理解说出来

先用中文讲清因果,再用英文回答。问题依据技能主题编写,并非公司内部题库。

解释

layout、PE程序、host分别负责什么?

参考回答 / English answer

分别描述空间部署、本地执行、外部装载调用传输;三者需共享shape等合同。

Layout describes placement, while PE code performs local work. The host loads, transfers, and invokes the program.
解释

CSL可以直接当普通Zig程序编译吗?

参考回答 / English answer

不可以据语法相似推断兼容;CSL有自己的编译器、builtins和设备合同。

Similar syntax does not make CSL interchangeable with Zig. It has its own compiler and device-specific contracts.
追问

导出符号找到后,回读还需检查什么?

参考回答 / English answer

类型、元素数、PE区域、排列、目标容量及编译产物版本。

Symbol lookup is only one step. I also verify dtype, counts, placement, ordering, and the matching build.
调试

host在回读等待,增加超时能修复吗?

参考回答 / English answer

若设备未满足完成或通信协议,条件永远不成立;先查等待图与参数。

A longer timeout cannot fix an impossible completion condition. I inspect device completion and transfer contracts.
预测

编译两PE,运行时把宽度改四能直接扩展吗?

参考回答 / English answer

不能假定,布局与资源可能编译期固定,需匹配产物。

Runtime arguments cannot override an incompatible compiled layout. I rebuild and validate the full placement contract.
解释

CPU状态机通过说明SdkRuntime可用了么?

参考回答 / English answer

不说明,只验证原创时序模型;实际SDK装载执行另需验证。

The state machine checks our teaching contract. It does not validate an installed SDK runtime.

继续查证

公开资料用于查证;本章图解和例题是独立教学内容。CPU 逻辑模型不能证明设备性能。