十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

Taichi 类型系统完全指南:静态类型、原始类型、复合类型与类型转换实战 Taichi 类型系统完全指南静态类型、原始类型、复合类型与类型转换实战【免费下载链接】taichiProductive, portable, and performant GPU programming in Python.项目地址: https://gitcode.com/GitHub_Trending/ta/taichiTaichi 是一门静态类型的嵌入式语言变量在 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 被自动转型为 3x 取值为 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.i3232 位有符号整数、ti.u88 位无符号整数、ti.f6464 位浮点数。复合类型Compound types类数组或类结构体的类型如ti.types.matrix、ti.types.ndarray、ti.types.struct由多个成员组成成员本身可以是原始类型或其他复合类型。原始类型命名规则与后端支持矩阵Taichi 的原始类型是标量也是构成复合类型的最小单元。其命名规则为类别字母 精度位数bit。类别字母含义如下i有符号整数signed integeru无符号整数unsigned integerf浮点数floating-point number。精度位数支持 8、16、32、64 位特殊地无符号数u11 位用于表示布尔值。最常用的三种原始类型为i3232 位有符号整数f3232 位浮点数u11 位无符号整数即布尔值。在 Python 前端 python/taichi/types/primitive_types.py 中这些类型被逐一导出为可直接使用的ti.i8、ti.i16、ti.i32、ti.i64、ti.u1、ti.u8、ti.u16、ti.u32、ti.u64、ti.f16、ti.f32、ti.f64C 侧则通过宏展开在 taichi/inc/data_type.inc.h 中注册每种原始类型的内建信息。各后端对原始类型的支持情况不同后端对原始类型的支持程度不同部分后端需要扩展extension才能完整支持特定类型。支持矩阵如下:o:表示需要后端扩展后端i8i16i32i64u1u8u16u32u64f16f32f64CPU✅✅✅✅✅✅✅✅✅✅✅✅CUDA✅✅✅✅✅✅✅✅✅✅✅✅OpenGL❌❌✅✅❌❌❌❌❌✅✅Metal✅✅✅❌✅✅✅✅❌❌✅❌Vulkan✅✅✅✅✅该后端需要启用扩展才能支持此类型。实际选型时需注意OpenGL 后端在 64 位整数、8/16 位整数等类型上支持缺失Metal 不支持i64/u64与f16/f64Vulkan 的多种类型依赖扩展。因此在编写跨后端cross-backend代码时建议优先使用i32、f32、u1这类全后端支持的类型或通过ti.init(arch...)指定目标后端后查阅上述矩阵。定制默认原始类型Taichi 初始化时自动采用以下默认类型ti.i32默认整数类型ti.f32默认浮点类型。这一默认值在 C 侧CompileConfig构造函数中显式给出见 taichi/program/compile_config.cppdefault_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_ipti.i64) # 将默认整数类型设为 ti.i64 ti.init(default_fpti.f64) # 将默认浮点类型设为 ti.f64ti.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.pyTI_DEFAULT_FP64 python my_kernel.py # 等价于 ti.init(default_fpti.f64)精度注意事项Taichi 作用域内的数字字面量具有默认的整数或浮点类型。例如默认浮点类型为ti.f32时字面量 3.14159265358979 会被截断为 32 位浮点数精度约为 7 位有效十进制数字。在工程仿真等对精度敏感的场合建议将default_fp设为ti.f64。数据类型别名int 与 float在 Taichi 作用域内int和float分别是默认整数类型与默认浮点类型的别名其含义随default_ip/default_fp变化。例如设置default_ipti.i64、default_fpti.f64后ti.init(default_ipti.i64, default_fpti.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.field、ti.Vector、ti.Matrix、ti.ndarray等 Taichi 数据容器声明类型时int与float同样作为默认整数/浮点类型的别名x ti.field(float, 5) # 等价于 x ti.field(ti.f64, 5)但需要注意作用域区分在普通 Python 代码Taichi 数据容器之外中使用int/float它们仍是 Python 内建函数而非 Taichi 默认类型别名x np.array([1, 2, 3, 4], dtypeint) # 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.pyti.cast(obj, dtype)接受标量或矩阵作为输入当对象是 Taichi 类如矩阵时调用其cast方法否则生成底层value_castIR 表达式。这印证了文档中向量/矩阵的转型按元素逐位进行的说明。使用类型名直接转换Taichi v1.1.0自 Taichi v1.1.0 起可以直接使用原始类型名如ti.f32、ti.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 语言转换规则略有差异按优先级排序如下整数 浮点数 - 浮点数i32 f32 - f32i16 f16 - f16低精度位 高精度位 - 高精度位i16 i32 - i32f16 f32 - f32u8 u16 - u16有符号整数 无符号整数 - 无符号整数u32 i32 - u32u8 i8 - u8规则冲突时高优先级规则生效u8 i16 - i16规则 2 与规则 3 冲突时规则 2 生效因为有符号 16 位精度更高f16 i32 - f16规则 1 与规则 2 冲突时规则 1 生效因为整数浮点数优先。例外情况位移运算返回左操作数的类型u8 i32 - u8、i16 i8 - i16逻辑运算返回i32比较运算返回i32。赋值中的隐式转换当给不同数据类型的变量赋值时Taichi 会执行隐式转换若值比目标变量精度更高存在精度损失风险会显示警告。示例 1变量a初始类型为float随后被赋值为 11 由int隐式转换为float无警告ti.kernel def foo(): a 3.14 a 1 print(a) # 1.0示例 2变量a初始类型为int随后被赋值为 3.143.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(n3, m3, dtypeti.i32)([[1, 1, 1], [0, 0, 0], [0, 0, 0]]) vec ti.types.vector(n3, dtypeti.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(centervec3, radiusfloat) # 初始化 sphere1球心 [0,0,0]半径 1.0 sphere1 sphere_type(centervec3([0, 0, 0]), radius1.0) # 初始化 sphere2球心 [1,1,1]半径 1.0 sphere2 sphere_type(centervec3([1, 1, 1]), radius1.0)当结构体成员很多时ti.types.struct写法会显得杂乱。Taichi 提供了ti.dataclass装饰器作为结构体类型的轻量封装ti.dataclass class Sphere: center: vec3 radius: float上面的写法与下面一行完全等价但可读性更好Sphere ti.types.struct(centervec3, radiusfloat)ti.dataclass相比ti.types.struct的另一个优势是可以在 dataclass 内定义成员函数支持面向对象编程OOP。从实现看python/taichi/lang/struct.pydataclass装饰器会检查类的注解与方法注解被设为结构体字段方法被挂载到结构体类型上从而可在结构体实例上直接调用。关于面向数据编程ODOP的更多内容可参考 objective>vec3 ti.types.vector(3, float) ti.dataclass class Ray: ro: vec3 rd: vec3 t: float # 上面的定义等价于 # Ray ti.types.struct(rovec3, rdvec3, tfloat) # 使用位置参数按序设置成员 ray Ray(vec3(0), vec3(1, 0, 0), 1.0) # ro 设为 vec3(0)t 自动置 0 ray Ray(vec3(0), rdvec3(1, 0, 0)) # ro 与 rd 均设为 vec3(0) ray Ray(t1.0) # ro 设为 vec3(1)rdvec3(0)t0.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_func与cast方法。小结类型系统实践要点静态类型是前提Taichi 作用域内变量首次赋值即定型后续赋值只能发生隐式转换或报错写 kernel 前先规划好每个变量的类型。按后端选类型跨后端代码优先使用i32/f32/u1使用i64、f64、f16等类型前对照本文后端支持矩阵确认目标后端含扩展需求。善用默认类型定制仿真等精度敏感场景通过ti.init(default_fpti.f64)提升精度可用TI_DEFAULT_FP/TI_DEFAULT_IP环境变量统一配置参数优先级高于环境变量。显式优于隐式隐式转换规则优先级为整数浮点 低精度高精度 有符号无符号且位移、逻辑、比较运算有特例为避免精度损失与隐蔽 Bug推荐显式使用ti.cast()或类型名转换。复合类型分层定制以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),仅供参考
返回列表