Rust in Everything
基于 tenferro-runtime 0.8.0

EagerExtensionBackendKind::Cuda not covered

主线位置:eager 主线开始之前,Cargo.toml 这一步。eager 运行时按 engine 的种类给扩展族(einsum、linalg、fft)装模块,种类是 tenferro-ad 里的一个枚举。CUDA engine 本身见 CUDA。

flowchart TB
  fft["fft 开 cuda"] --> adc["tenferro-ad 开 cuda"]
  adc --> var["枚举多出 Cuda"]
  ein["einsum 只开 autodiff"] --> m1["match 无 Cuda 分支"]
  lin["linalg 只开 autodiff"] --> m2["match 无 Cuda 分支"]
  var --> m1
  var --> m2
  m1 --> e["E0004"]
  m2 --> e
  all["每个都开 cuda"] --> ok["cargo check 通过"]

实验 crate exp-tenferro8/pit1-cuda、pit1-cudafix、pit1-webgpu,cargo check --offline(debug);M2 Max、macOS 27、rustc 1.99.0。本机没有 CUDA,这一页只编译、不运行。报错里 cargo 缓存的路径前缀写成 ...。

现象

只想让 FFT 上 GPU,于是只在 tenferro-fft 上开 cuda;einsum 和 linalg 照常开 autodiff:

[dependencies]
tenferro-runtime = "=0.8.0"
tenferro-einsum = { version = "=0.8.0", features = ["autodiff"] }
tenferro-linalg = { version = "=0.8.0", features = ["autodiff"] }
tenferro-fft = { version = "=0.8.0", features = ["autodiff", "cuda"] }

main.rs 只有 fn main() {},cargo check 以 101 退出:

error[E0004]: non-exhaustive patterns: `tenferro_ad::extension::EagerExtensionBackendKind::Cuda` not covered
   --> .../tenferro-einsum-0.8.0/src/eager_ad.rs:214:11
    |
214 |     match backend_kind {
    |           ^^^^^^^^^^^^ pattern `tenferro_ad::extension::EagerExtensionBackendKind::Cuda` not covered
    |
note: `tenferro_ad::extension::EagerExtensionBackendKind` defined here
   --> .../tenferro-ad-0.8.0/src/extension.rs:45:1
    |
 45 | pub enum EagerExtensionBackendKind {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
 50 |     Cuda,
    |     ---- not covered
...
error: could not compile `tenferro-einsum` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
error[E0004]: non-exhaustive patterns: `tenferro_ad::extension::EagerExtensionBackendKind::Cuda` not covered
   --> .../tenferro-linalg-0.8.0/src/eager_ext.rs:982:11
...
error: could not compile `tenferro-linalg` (lib) due to 1 previous error

(pit1-cuda。)出错的是没开 cuda 的两个 crate,rustc 的 help 还建议在上游代码里加一个 => todo!() 分支,这改不了。

用户指南 devices-and-gpu.md 在 v0.8.0 还留着「WebGPU Quickstart」一节,教人写 tenferro-gpu = { version = "...", default-features = false, features = ["webgpu"] }。照抄(pit1-webgpu),cargo 在解析依赖时就停下:

package `pit1-webgpu` depends on `tenferro-gpu` with feature `webgpu` but `tenferro-gpu` does not have that feature.
help: available features: blas, blas-accelerate, blas-mkl, blas-openblas, cuda, default, native, rocm

出处

枚举在 tenferro-ad 的 extension.rs,Cuda 变体跟着 tenferro-ad 自己的 cuda feature:

pub enum EagerExtensionBackendKind {
    /// The eager runtime owns a CPU backend.
    Cpu,
    /// The eager runtime owns a CUDA backend.
    #[cfg(feature = "cuda")]
    Cuda,
}

tenferro-einsum 的 eager_extension_module 对它 match,Cuda 分支挂在 einsum 自己的 cuda 上:

    match backend_kind {
        EagerExtensionBackendKind::Cpu => {
            crate::extension::extension_module::<CpuEngine>(engine_id)
                .map_err(eager_runtime_config_error)
        }
        #[cfg(feature = "cuda")]
        EagerExtensionBackendKind::Cuda => {
            // ...

tenferro-linalg 的 eager_ext.rs 第 982 行是同样的写法。tenferro-fft 的 cuda 转发 "tenferro-ad?/cuda",开了 autodiff 时就打开了 tenferro-ad/cuda。依赖图里只有一份 tenferro-ad,cargo 把各处请求的 feature 取并集,于是枚举对所有人都多了 Cuda;einsum 和 linalg 没开 cuda,match 少一个分支,编译失败。没开 autodiff 的运算族 crate 不编译 eager_ad.rs 这类文件,不受影响。

这是上游的 feature 设计问题,0.8.0 上复现:一个 crate 的可选 feature 改变了另一个 crate 看到的枚举,而 #[doc(hidden)] 的这个枚举没有 #[non_exhaustive] 也没有通配分支。0.7.1 的同一个枚举有 #[cfg(feature = "webgpu")] WebGpu,坑的形状一样,换的只是变体名;WebGPU 后端和 webgpu feature 在 0.8.0 删掉了,来历见 GPU 与 XLA。

修法

依赖图里每个开了 autodiff 的运算族 crate 都一起开 cuda:

[dependencies]
tenferro-runtime = "=0.8.0"
tenferro-einsum = { version = "=0.8.0", features = ["autodiff", "cuda"] }
tenferro-linalg = { version = "=0.8.0", features = ["autodiff", "cuda"] }
tenferro-fft = { version = "=0.8.0", features = ["autodiff", "cuda"] }
    Checking pit1-cudafix v0.1.0 (...)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.63s

(pit1-cudafix,cargo check。)这只说明能编译。运行期要有 CUDA 驱动,没有驱动时 cuda_devices() 和 CudaEngine::open 会 panic,见 CUDA 的「没有驱动时」。WebGPU 没有替代,0.8.0 的 GPU 后端只有 cuda。

怎么确认没中招

  • E0004 的模式是 EagerExtensionBackendKind::Cuda、出错位置在 tenferro-einsum / tenferro-linalg 的源码里:找依赖图里谁开了 cuda。cargo tree -e features -i tenferro-ad 里出现 tenferro-ad feature "cuda" 时,它下面列出的就是来源。
  • 加 GPU feature 时把所有 tenferro 运算族 crate 的 feature 列表放在一起改,不要只改一行。
  • 上游指南、README 里的 webgpu 是 0.7.1 留下的文字,tenferro-gpu 0.8.0 的 feature 只有 cuda、rocm(空占位)和 CPU provider 的那几个。

小结

  • tenferro-ad 的 EagerExtensionBackendKind::Cuda 跟着 tenferro-ad/cuda 出现,任何一个运算族 crate 开 cuda 都会打开它。
  • 只开 autodiff 的 einsum、linalg 对它 match 时没有 Cuda 分支,报 E0004;上游设计问题,0.8.0 复现。
  • 每个开 autodiff 的运算族 crate 都一起开 cuda;指南里的 features = ["webgpu"] 在 0.8.0 解析不了。

💬 关联讨论

发起讨论
加载中...