view is not slice-contiguous
主线位置:直接计算主线的最后一步,结果的存储和读回。视图只换布局、不复制,见 列主序与视图;为什么默认张量没有
Clone,见 数据模型 的「大小与 Clone」;读回的几种方式见 存储与所有权。
flowchart TB
a["TypedTensor 2x3"] --> v["transpose_view"]
v --> c{"紧凑列主序"}
c -->|否| s["as_slice duplicate"]
s --> err["not slice-contiguous"]
c -->|否| be["engine to_contiguous"]
be --> own["新的紧凑张量"]
c -->|否| host["to_col_major"]
host --> own
实验 crate exp-tenferro8/pit2-exp,bin p31_view;编译错误在 exp-tenferro8/pit2-cfail(bin no_clone、dyn_index、fixed)。CPU(CpuEngine::serial()),debug,M2 Max、macOS 27、rustc 1.99.0。
现象
A = [[1, 2, 3], [4, 5, 6]],取它的转置视图和第 1 行,想拿到数据:
let a = TypedTensor::<f64>::from_vec_col_major(vec![2, 3], vec![1.0, 4.0, 2.0, 5.0, 3.0, 6.0])?;
let t = a.as_view().transpose_view([1, 0])?;
t.as_slice(); t.duplicate();
let row = a.as_view().slice_axis_view(0, StridedSliceSpec::new(1, Some(2), 1))?;
row.as_slice(); row.reshape_view(&[3]);
t: shape [3, 2] strides [2, 1] compact false
t.as_slice() -> Err: TypedTensorView::as_slice: view is not slice-contiguous; materialize with to_contiguous before requesting a borrowed slice
t.duplicate() -> Err: TypedTensorView::as_slice: view is not slice-contiguous; materialize with to_contiguous before requesting a borrowed slice
row: shape [1, 3] strides [1, 2] compact false
row.as_slice() -> Err: TypedTensorView::as_slice: view is not slice-contiguous; materialize with to_contiguous before requesting a borrowed slice
row.reshape_view([3]) -> Err: TypedTensorView::reshape_view: view is not slice-contiguous; materialize with to_contiguous before requesting a borrowed slice
columns 1..: compact true as_slice [2.0, 5.0, 3.0, 6.0]
duplicate()的报错说的是as_slice,因为它就是先借切片再复制。- 一行数据在列主序里是隔一个取一个(步长 2),同样不紧凑。
reshape_view也拒绝它,消息还是「before requesting a borrowed slice」,虽然这里并没有要切片。 - 连续的几列是紧凑的,能借出切片(最后一行)。
想直接复制默认的张量,或者按下标取值,编译就不过:
error[E0599]: no method named `clone` found for struct `tenferro_runtime::TypedTensor<T, R, D>` in the current scope
--> src/bin/no_clone.rs:5:13
|
5 | let b = a.clone();
| ^^^^^ method not found in `tenferro_runtime::TypedTensor<f64>`
error[E0599]: no method named `clone` found for struct `tenferro_runtime::Tensor` in the current scope
--> src/bin/no_clone.rs:7:13
|
7 | let u = t.clone();
| ^^^^^ method not found in `tenferro_runtime::Tensor`
error[E0608]: cannot index into a value of type `tenferro_runtime::TypedTensor<f64>`
--> src/bin/dyn_index.rs:5:19
|
5 | println!("{}", a[&[1][..]]);
| ^^^^^^^^^^
出处
错误文字来自 tenferro-tensor-core 的 NonContiguousViewAsSlice,as_slice 和 reshape_view 共用这一个变体,所以措辞都是「borrowed slice」。主机侧借切片只认紧凑列主序的布局:借出去的是一段连续内存,步长不对就没法用一个 &[T] 表示。reshape_view 只改元数据、不搬数据,非紧凑的视图在一般情况下改不了形状,于是一并拒绝。这都是有意的:每一次物理复制都要写出名字(上游文档的说法是「Copies are named」,见 存储与所有权),视图上的读回不会替你偷偷重排。
Clone 同理。impl Clone for TypedTensor<T, R, Host> 只给 Host 表示;TypedTensor<f64> 的默认表示是 Dynamic,内容可能在设备上,clone 一旦成立就得偷偷下载,所以没有。动态秩张量也没有 Index,Rank<N> 的张量才能用 [usize; N] 下标(数据模型)。
修法
视图交给 engine 或主机侧的显式复制变成紧凑张量;拥有型张量用 duplicate():
let tc = engine.to_contiguous(&t)?; // TensorViewCanonicalization
let rc = engine.to_contiguous(&row)?;
let th = t.to_col_major()?; // 不要 engine,逐个元素按列主序复制
let ts = a.transpose(&[1, 0], &mut engine)?;
let a2 = a.duplicate()?;
engine.to_contiguous(t): shape [3, 2] data [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
engine.to_contiguous(row): shape [1, 3] data [4.0, 5.0, 6.0]
t.to_col_major(): shape [3, 2] data [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
a.transpose(engine): data [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
a.duplicate(): [1.0, 4.0, 2.0, 5.0, 3.0, 6.0]
Aᵀ 是 [[1, 4], [2, 5], [3, 6]],列主序正好是 1 到 6。to_col_major 是单线程逐元素复制,大矩阵时比 engine 的 to_contiguous 慢(列主序与视图 的计时)。
编译错误的那两处:
let b = a.duplicate()?; // 代替 clone
println!("get(&[1]): {:?}", a.get(&[1]));
let h = a.into_host()?; // 确定在主机上:换成 Host 表示
let h2 = h.clone();
println!("into_host().clone()[&[1]] = {}", h2[&[1][..]]);
let r = TypedTensor::<f64, Rank<1>>::from_vec_col_major([2], vec![1.0, 2.0])?;
println!("Rank<1> r[[1]] = {}", r[[1]]);
duplicate: [1.0, 2.0]
get(&[1]): Ok(2.0)
into_host().clone()[&[1]] = 2
Rank<1> r[[1]] = 2
怎么确认没中招
- 读数据之前先看
is_col_major_contiguous();为false就别调as_slice/duplicate/reshape_view,改走to_contiguous或to_col_major。 - 转置和取行得到的视图不紧凑;取连续的几列是紧凑的。
- 需要「再要一份」的地方写
duplicate()?;要下标访问,用get(返回Result)或先into_host()。
小结
- 转置、取行等视图不是紧凑列主序,
as_slice、duplicate、reshape_view都报view is not slice-contiguous; materialize with to_contiguous ...。 - 用 engine 的
to_contiguous(或会话里的transpose、主机侧的to_col_major)得到新的紧凑张量;拥有型张量用duplicate()。 - 默认的
Dynamic张量和Tensor没有Clone(E0599),动态秩张量不能下标(E0608);into_host()之后才有clone和下标。