CHAPTER 46 / Cerebras · 晶圆级计算
CSL 与 host:装载、启动、回读是一份共同合同
为什么一个局部函数还不构成可运行的设备程序?
这一章要弄清楚
- 区分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 的类型是 i32,void 表示没有返回值 |
{ ... } |
函数体的起止;其中的分号结束一条语句 |
total += value; |
用原来的 total 加 value,把和写回 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 可以继续。走完 load、run 后仍不能读出这个 9;阻塞 launch 返回后,本次操作已满足完成条件,但 host 还须按导出符号、PE 区域、元素数和 dtype 安排回读,才能比较结果。若该程序要求显式解除 command stream 阻塞,只算出 9 却漏掉结束协议,不能据此预测阻塞调用已经返回;下方代码没有展示这部分 PE 实现。
本节没有运行 SDK。依据 SdkRuntime API 的 load、run、launch、stop 合同,核对日期 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生命周期模型
未装载时不允许run。
装载并启动后才能计算。
结果可供回读。
阅读完整推演文字
- Created
state:Created;run:拒绝
未装载时不允许run。
- Running
state:Running;input:4 5
装载并启动后才能计算。
- Finished
state:Finished;result:9
结果可供回读。
跟着例子,走完一遍
拒绝错误生命周期顺序
Created状态不能run;load后run,再计算得到9。
- 检查未装载状态
- 依次load/run/finish
- 完成后读取9
// 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';
}
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
导出名称合同
符号表只有input=3、result=7;查result与missing。
- 成功查找result
- 检查missing不存在
- 不以默认插入制造假符号
// 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核对清单。
给我一点提示
- 名称只是开始
- 把传输两端对起来
查看答案与推理
核对编译产物、导出名、dtype、每PE元素数、区域坐标/宽高、排列与host容量;再确认调用完成和回读顺序。先用单PE、两项已知值验证,不直接扩大到多PE。
练习 2
把符号表查找改成map[name]为什么可能误导?
给我一点提示
- operator[]可能插入
- 零值不代表合法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.继续查证
- Cerebras complete program tutorial ↗
Layout, PE, host and command completion
- Cerebras public host memcpy example ↗
SdkRuntime lifecycle and transfer arguments
- Cerebras basic syntax ↗
Types, functions and builtins
公开资料用于查证;本章图解和例题是独立教学内容。CPU 逻辑模型不能证明设备性能。