Taichi 类型系统完全指南:静态类型、原始类型、复合类型与类型转换实战
2026/9/11 3:33:52 网站建设 项目流程

Taichi 类型系统完全指南:静态类型、原始类型、复合类型与类型转换实战

【免费下载链接】taichiProductive, portable, and performant GPU programming in Python.项目地址: https://gitcode.com/GitHub_Trending/ta/taichi

Taichi 是一门静态类型的嵌入式语言:变量在 Taichi 作用域(@ti.kernel/@ti.func内)中的类型在编译期即被确定,一经声明便不可再被赋予不同类型的数据。本文以 docs/lang/articles/type_system/type.md 为骨架,结合仓库源码与测试,系统讲解 Taichi 的原始类型(primitive types)、复合类型(compound types)、默认类型定制、类型别名、显式/隐式类型转换规则,以及参数包(argpack)类型。读完本文,你将能正确地为 kernel、field、函数参数与结构体成员选择类型,避免因隐式转换与精度丢失引入的隐蔽 Bug。

静态类型:编译期定型的核心语义

Taichi 中变量的类型在首次赋值时确定,之后无法改变。以下示例(摘自原文档)直观展示了这一行为:

@ti.kernel def test(): x = 1 # x 是整数 1 x = 3.14 # x 是整数,3.14 被自动转型为 3,x 取值为 3 x = ti.Vector([1, 1]) # 报错!ti.Vector 无法转换为整数
  • 第 1 行:x首次被赋值为整数,故x的类型为整型;
  • 第 2 行:x被重新赋值为浮点数 3.14,但x的类型不会改变——3.14 被自动(隐式)转换为整数 3 以匹配x的类型;
  • 第 3 行:ti.Vector([1, 1])无法被转换为整数,编译器直接报错。

这一"首次赋值定类型"的规则意味着,在 Taichi 作用域内编写代码时,类型设计必须前置:先想清楚每个变量应当是什么类型,再写赋值语句。

Taichi 的全部数据类型由ti.types模块定义,分为两大类:

  • 原始类型(Primitive types):常用的数值标量类型,如ti.i32(32 位有符号整数)、ti.u8(8 位无符号整数)、ti.f64(64 位浮点数)。
  • 复合类型(Compound types):类数组或类结构体的类型,如ti.types.matrixti.types.ndarrayti.types.struct,由多个成员组成,成员本身可以是原始类型或其他复合类型。

原始类型:命名规则与后端支持矩阵

Taichi 的原始类型是标量,也是构成复合类型的最小单元。其命名规则为:类别字母 + 精度位数(bit)。类别字母含义如下:

  • i:有符号整数(signed integer);
  • u:无符号整数(unsigned integer);
  • f:浮点数(floating-point number)。

精度位数支持 8、16、32、64 位;特殊地,无符号数u1(1 位)用于表示布尔值。最常用的三种原始类型为:

  • i32:32 位有符号整数;
  • f32:32 位浮点数;
  • u1:1 位无符号整数,即布尔值。

在 Python 前端 python/taichi/types/primitive_types.py 中,这些类型被逐一导出为可直接使用的ti.i8ti.i16ti.i32ti.i64ti.u1ti.u8ti.u16ti.u32ti.u64ti.f16ti.f32ti.f64;C++ 侧则通过宏展开在 taichi/inc/data_type.inc.h 中注册每种原始类型的内建信息。

各后端对原始类型的支持情况

不同后端对原始类型的支持程度不同,部分后端需要扩展(extension)才能完整支持特定类型。支持矩阵如下(:o:表示需要后端扩展):

后端i8i16i32i64u1u8u16u32u64f16f32f64
CPU
CUDA
OpenGL🟠
Metal
Vulkan🟠🟠🟠🟠🟠🟠🟠

🟠:该后端需要启用扩展才能支持此类型。

实际选型时需注意:OpenGL 后端在 64 位整数、8/16 位整数等类型上支持缺失,Metal 不支持i64/u64f16/f64,Vulkan 的多种类型依赖扩展。因此在编写跨后端(cross-backend)代码时,建议优先使用i32f32u1这类全后端支持的类型,或通过ti.init(arch=...)指定目标后端后查阅上述矩阵。

定制默认原始类型

Taichi 初始化时自动采用以下默认类型:

  • ti.i32:默认整数类型;
  • ti.f32:默认浮点类型。

这一默认值在 C++ 侧CompileConfig构造函数中显式给出,见 taichi/program/compile_config.cpp:

default_fp = PrimitiveType::f32; default_ip = PrimitiveType::i32; default_up = PrimitiveType::u32;

其中default_up(默认无符号类型)总是与default_ip保持一致的位宽——在 python/taichi/lang/misc.py 中,如果用户显式传入default_up,会直接抛出KeyError提示应通过default_ip设置。

用户可以在调用ti.init()时指定默认原始类型:

ti.init(default_ip=ti.i64) # 将默认整数类型设为 ti.i64 ti.init(default_fp=ti.f64) # 将默认浮点类型设为 ti.f64

ti.init的函数签名(见 python/taichi/lang/misc.py)接受default_fp(默认浮点类型)与default_ip(默认整型)两个关键字参数。此外,Taichi 还支持通过环境变量TI_DEFAULT_FP/TI_DEFAULT_IP设置默认类型,取值为"32""64";当环境变量与ti.init参数同时存在时,显式参数优先生效(详见 python/taichi/lang/misc.py):

TI_DEFAULT_FP=64 python my_kernel.py # 等价于 ti.init(default_fp=ti.f64)

精度注意事项:Taichi 作用域内的数字字面量具有默认的整数或浮点类型。例如默认浮点类型为ti.f32时,字面量 3.14159265358979 会被截断为 32 位浮点数,精度约为 7 位有效十进制数字。在工程仿真等对精度敏感的场合,建议将default_fp设为ti.f64

数据类型别名:int 与 float

在 Taichi 作用域内,intfloat分别是默认整数类型与默认浮点类型的别名,其含义随default_ip/default_fp变化。例如设置default_ip=ti.i64default_fp=ti.f64后:

ti.init(default_ip=ti.i64, default_fp=ti.f64) @ti.kernel def example_cast() -> int: # 返回值类型为 ti.i64 x = 3.14 # x 的类型是 ti.f64 y = int(x) # 等价于 ti.i64(x) return y

在 Python 作用域中使用ti.fieldti.Vectorti.Matrixti.ndarray等 Taichi 数据容器声明类型时,intfloat同样作为默认整数/浮点类型的别名:

x = ti.field(float, 5) # 等价于: x = ti.field(ti.f64, 5)

但需要注意作用域区分:在普通 Python 代码(Taichi 数据容器之外)中使用int/float,它们仍是 Python 内建函数,而非 Taichi 默认类型别名:

x = np.array([1, 2, 3, 4], dtype=int) # NumPy 的 int64 类型 y = int(3.14) # Python 内建 int 类型

显式类型转换

由于 Taichi 是静态类型语言,编译器在编译期执行类型检查,一旦变量声明后就不能赋给它不同类型的值。当确实需要切换数据类型时(例如原始类型无法满足某次赋值或计算),必须进行显式类型转换

使用 ti.cast()

ti.cast()可将给定值转换为指定目标类型,例如用ti.cast(x, float)将变量x转换为浮点类型:

@ti.kernel def foo(): a = 3.14 b = ti.cast(a, ti.i32) # 3 c = ti.cast(b, ti.f32) # 3.0

从源码看(python/taichi/lang/ops.py),ti.cast(obj, dtype)接受标量或矩阵作为输入:当对象是 Taichi 类(如矩阵)时调用其cast方法,否则生成底层value_castIR 表达式。这印证了文档中"向量/矩阵的转型按元素逐位进行"的说明。

使用类型名直接转换(Taichi v1.1.0+)

自 Taichi v1.1.0 起,可以直接使用原始类型名(如ti.f32ti.i64)对标量变量进行类型转换:

@ti.kernel def foo(): a = 3.14 x = int(a) # 3 y = float(a) # 3.14 z = ti.i32(a) # 3 w = ti.f64(a) # 3.14

注意int(a)/float(a)在此处是默认类型别名的转换(受default_ip/default_fp影响),而ti.i32(a)/ti.f64(a)是显式指定目标类型的转换,两者语义不同。

隐式类型转换:规则与风险

隐式类型转换发生在"某个值被放置或赋值到期望不同数据类型的场景"中。

警告:隐式类型转换通常是 Bug 的重要来源。Taichi 强烈不建议依赖该机制,推荐对所有变量与操作显式指定目标类型。

二元运算中的隐式转换

Taichi 实现了专属的隐式转换规则,与 C 语言(C 语言转换规则)略有差异,按优先级排序如下:

  1. 整数 + 浮点数 -> 浮点数
    • i32 + f32 -> f32
    • i16 + f16 -> f16
  2. 低精度位 + 高精度位 -> 高精度位
    • i16 + i32 -> i32
    • f16 + f32 -> f32
    • u8 + u16 -> u16
  3. 有符号整数 + 无符号整数 -> 无符号整数
    • u32 + i32 -> u32
    • u8 + i8 -> u8

规则冲突时,高优先级规则生效

  • u8 + i16 -> i16(规则 2 与规则 3 冲突时,规则 2 生效,因为有符号 16 位精度更高);
  • f16 + i32 -> f16(规则 1 与规则 2 冲突时,规则 1 生效,因为整数+浮点数优先)。

例外情况:

  • 位移运算返回左操作数的类型:u8 << i32 -> u8i16 << i8 -> i16
  • 逻辑运算返回i32
  • 比较运算返回i32

赋值中的隐式转换

当给不同数据类型的变量赋值时,Taichi 会执行隐式转换;若值比目标变量精度更高(存在精度损失风险),会显示警告。

  • 示例 1:变量a初始类型为float,随后被赋值为 1,1 由int隐式转换为float无警告

    @ti.kernel def foo(): a = 3.14 a = 1 print(a) # 1.0
  • 示例 2:变量a初始类型为int,随后被赋值为 3.14,3.14 由float隐式转换为int,产生精度损失,触发警告

    @ti.kernel def foo(): a = 1 a = 3.14 print(a) # 3

这正与本文开头的静态类型示例一脉相承:隐式转换在"能转换"时进行,但精度损失会通过编译警告暴露,提醒开发者及时改用显式ti.cast

复合类型

复合类型是用户自定义的、由多个元素构成的数据类型,包括向量(vector)、矩阵(matrix)、ndarray 与结构体(struct)。Taichi 允许以ti.types模块提供的全部类型为骨架,自定义更高层的复合类型。

ndarray类型详见另一篇文档 与外部数组交互。

矩阵与向量:ti.types.matrix() / ti.types.vector()

使用ti.types.matrix()ti.types.vector()创建自定义矩阵/向量类型:

vec4d = ti.types.vector(4, ti.f64) # 64 位浮点 4 维向量类型 mat4x3i = ti.types.matrix(4, 3, int) # 4x3 整数矩阵类型

自定义复合类型既可以用于实例化向量/矩阵,也可以作为函数参数与结构体成员的类型注解:

v = vec4d(1, 2, 3, 4) # 创建向量实例,v = [1.0 2.0 3.0 4.0] @ti.func def length(w: vec4d): # 以 vec4d 作为类型注解 return w.norm() @ti.kernel def test(): print(length(v))

行列向量语义:Taichi 中不区分行向量与列向量。矩阵乘向量时,向量被视为列向量;向量乘矩阵时,向量被视为行向量:

mat = ti.types.matrix(n=3, m=3, dtype=ti.i32)([[1, 1, 1], [0, 0, 0], [0, 0, 0]]) vec = ti.types.vector(n=3, dtype=ti.i32)([1, 1, 1]) print(mat @ vec) # [3 0 0] print(vec @ mat) # [1 1 1]

结构体类型与 @ti.dataclass

使用ti.types.struct()创建结构体类型。以下示例用结构体表示 3D 空间中的一个球体(中心 + 半径):

# 定义复合类型 vec3,表示球心 vec3 = ti.types.vector(3, float) # 定义复合类型 sphere_type,表示球体 sphere_type = ti.types.struct(center=vec3, radius=float) # 初始化 sphere1:球心 [0,0,0],半径 1.0 sphere1 = sphere_type(center=vec3([0, 0, 0]), radius=1.0) # 初始化 sphere2:球心 [1,1,1],半径 1.0 sphere2 = sphere_type(center=vec3([1, 1, 1]), radius=1.0)

当结构体成员很多时,ti.types.struct写法会显得杂乱。Taichi 提供了@ti.dataclass装饰器作为结构体类型的轻量封装:

@ti.dataclass class Sphere: center: vec3 radius: float

上面的写法与下面一行完全等价,但可读性更好:

Sphere = ti.types.struct(center=vec3, radius=float)

@ti.dataclass相比ti.types.struct的另一个优势是:可以在 dataclass 内定义成员函数,支持面向对象编程(OOP)。从实现看(python/taichi/lang/struct.py),dataclass装饰器会检查类的注解与方法:注解被设为结构体字段,方法被挂载到结构体类型上,从而可在结构体实例上直接调用。关于面向数据编程(ODOP)的更多内容,可参考 objective>vec3 = ti.types.vector(3, float) @ti.dataclass class Ray: ro: vec3 rd: vec3 t: float # 上面的定义等价于: # Ray = ti.types.struct(ro=vec3, rd=vec3, t=float) # 使用位置参数按序设置成员 ray = Ray(vec3(0), vec3(1, 0, 0), 1.0) # ro 设为 vec3(0),t 自动置 0 ray = Ray(vec3(0), rd=vec3(1, 0, 0)) # ro 与 rd 均设为 vec3(0) ray = Ray(t=1.0) # ro 设为 vec3(1),rd=vec3(0),t=0.0 ray = Ray(1) # 所有成员均为 0 ray = Ray()

提示:由于向量、矩阵、结构体的形状已知,可以使用 GLSL 风格的广播(broadcast)语法创建实例,例如vec3(0)会生成[0, 0, 0]

复合类型的类型转换

目前仅向量与矩阵支持类型转换,且转换是逐元素进行的,生成新的向量/矩阵:

@ti.kernel def foo(): u = ti.Vector([2.3, 4.7]) v = int(u) # ti.Vector([2, 4]) # 若 default_ip 为 ti.i32,则等价于: v = ti.cast(u, ti.i32) # ti.Vector([2, 4])

ti.cast对矩阵/向量类对象的处理在 python/taichi/lang/matrix.py 与 python/taichi/lang/matrix_ops.py 中实现为元素级cast

参数包类型(Argument Pack)

参数包(argpack)是用户自定义的、作为多个参数包装器的数据类型。argpack的详细讨论见 Argument Pack 文档;其 Python 前端实现位于 python/taichi/lang/argpack.py,支持cast等操作(见该文件cast_funccast方法)。

小结:类型系统实践要点

  1. 静态类型是前提:Taichi 作用域内变量首次赋值即定型,后续赋值只能发生隐式转换或报错,写 kernel 前先规划好每个变量的类型。
  2. 按后端选类型:跨后端代码优先使用i32/f32/u1;使用i64f64f16等类型前,对照本文后端支持矩阵确认目标后端(含扩展需求)。
  3. 善用默认类型定制:仿真等精度敏感场景通过ti.init(default_fp=ti.f64)提升精度;可用TI_DEFAULT_FP/TI_DEFAULT_IP环境变量统一配置(参数优先级高于环境变量)。
  4. 显式优于隐式:隐式转换规则优先级为"整数+浮点 > 低精度+高精度 > 有符号+无符号",且位移、逻辑、比较运算有特例;为避免精度损失与隐蔽 Bug,推荐显式使用ti.cast()或类型名转换。
  5. 复合类型分层定制:以ti.types.vector/ti.types.matrix/ti.types.struct为骨架组合出领域类型,用@ti.dataclass提升可读性与支持成员方法;初始化时支持位置参数、关键字参数与自动置零,向量/矩阵支持逐元素类型转换。

【免费下载链接】taichiProductive, portable, and performant GPU programming in Python.项目地址: https://gitcode.com/GitHub_Trending/ta/taichi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询