6个版本
0.3.1 | 2022年5月2日 |
---|---|
0.3.0 | 2022年4月12日 |
0.2.1 | 2022年1月25日 |
0.1.1 | 2021年12月31日 |
#1112 in 数据结构
每月24次下载
220KB
1.5K SLoC
一个专注于性能、静态分配、静态形状数据和写时复制(又称cow)行为的线性代数系统。同时也提供对blas/blis的安全和快速绑定。
使命
slas的目标是在编译时知道尽可能多的信息的情况下,提供最佳的性能。这主要包含代数对象的形状和大小、目标架构和可用硬件特性/设备。
请注意,slas专门针对在相同系统上编译和执行二进制文件的情况,因此主要针对本地编译。
注意:如果不使用本地编译,slas可能仍然非常不完善。
通过模块化后端系统尝试在硬件和使用案例上进行专业化,该系统将支持未来的自定义分配器。
示例
可以使用StaticCowVec
和moo
宏以及cow_vec
宏来创建。它们具有相同的语法,但cow_vec宏对于认真的程序员来说是一个很好的选择。有关moo宏的更好文档可以在此处找到。
use slas::prelude::*;
use slas::cow_vec;
let a = moo![f32: 1, 2, 3.2];
let b = cow_vec![f32: 3, 0.4, 5];
println!("Dot product of {a:?} and {b:?} is {:?}", a.dot(&b));
println!("{a:?} + {b:?} = {:?}", a.add(&b));
默认情况下,slas会尝试为您选择后端,您也可以自己选择静态后端。(有关后端究竟是什么以及如何配置它的更多信息,请参阅此处。)
use slas::prelude::*;
let a = moo![on slas_backend::Rust:f32: 1, 2, 3.2];
// This will only use rust code for all operations on a
use slas::prelude::*;
let a = moo![on slas_backend::Blas:f32: 1, 2, 3.2];
// This will always use blas for all operations on a
默认情况下,slas将选择在构建过程中设置环境选项时假定最快的后端。(有关更多信息的说明,请参阅此处。)
StaticCowVec
解引用到StaticVecUnion
,它反过来又解引用到[T; LEN]
,因此可以对[T;LEN]
实现的任何方法也应用于StaticCowVec
和StaticVecUnion
。
什么是cow,何时有用?
写时复制(Copy-on-Write,简称COW)功能灵感来源于 std::borrow::cow。其基本思路是通过在运行时而非编译时确定何时复制,从而节省分配(和时间)。在某些情况下,这可能会导致内存效率低下(因为枚举的大小是其最大字段大小加标签大小),这就是为什么你可以选择使用 StaticVecUnion
和 StaticVec
的原因。你可以在实现 StaticVec
的任何类型上调用 moo
、moo_ref
和 mut_moo_ref
来将其转换为适用于其用例的适当类型,且无任何开销。
moo_ref 返回一个 StaticVecRef
,这是一个指向 StaticVecUnion
的引用的类型别名。当你知道不需要对向量进行可变访问或所有权时,这最有效。
mut_moo_ref 返回一个 MutStaticVecRef
。这和 moo_ref
很相似,但当你想在原地修改数据时(例如规范化一个向量),它非常有用。你应该只在需要具有向量可变访问并且有副作用的情况下使用它。
moo 返回一个引用 self
的 StaticCowVec
。如果你不知道是否需要向量的可变访问并且不希望有副作用,这很有用。如果你想将数据复制到 StaticCowVec
,则需要使用 StaticCowVec::from
。
moo_owned 只会返回一个 StaticVecUnion
。当你真正只需要一个 [T; LEN]
,但你需要只有 StaticVecUnion
实现的方法时,这很有用。
写时复制行为示例
use slas::prelude::*;
let source: Vec<f32> = vec![1., 2., 3.];
let mut v = source.moo();
// Here we mutate v,
// so the content of source will be copied into v before the mutation occours.
v[0] = 0.;
assert_eq!(**v, [0., 2., 3.]);
assert_eq!(source, vec![1., 2., 3.]);
在创建 v
后,借用检查器不会允许修改 source
,因为不允许对借用值进行赋值。在某些情况下,这可能会成为一个问题。
use slas::prelude::*;
let mut source: Vec<f32> = vec![1., 2., 3.];
let mut v = unsafe { StaticCowVec::<f32, 3>::from_ptr(source.as_ptr()) };
// Here we can mutate source, because v was created from a raw pointer.
source[1] = 3.;
v[0] = 0.;
source[2] = 4.;
assert_eq!(**v, [0., 3., 3.]);
assert_eq!(source, vec![1., 3., 4.]);
在上面的示例中,你可以看到 v
在第一次修改 source
时改变了值,但在第二次没有改变。这是因为当它被修改时,v
被复制了。
矩阵示例
use slas::prelude::*;
use slas_backend::*;
let a = moo![f32: 1..=6].matrix::<Blas, 2, 3>();
let b = moo![f32: 1..=6].matrix::<Blas, 3, 2>();
let c = a.matrix_mul(&b);
let d = b.vector_mul(&[1., 2.]);
assert_eq!(c, [22., 28., 49., 64.]);
assert_eq!(d, [5., 11., 17.]);
println!("{a:.0?} * {b:.0?} = {:.0?}", c.matrix::<Blas, 2, 2>());
在 slas 中有一个 Matrix
类型和一个 Tensor
类型。对于大多数操作,可以使用二维张量代替矩阵。矩阵可以解引用为一个二维张量,二维张量实现了 Into<Matrix>
,这两个操作都没有开销,因为它们只更改类型信息。矩阵类型有一些额外的可选泛型参数,包括 IS_TRANS,如果在编译时已经懒惰转置,则该参数为真。如果不需要这些信息进行矩阵操作,则应将其实现为二维张量。当索引二维张量时,将使用 [usize; 2]
,它首先选择列,而使用 (usize, usize)
对矩阵进行索引时,首先选择行。
use slas::prelude::*;
use slas_backend::*;
let a = moo![f32: 1..=6].matrix::<Blas, 2, 3>();
assert_eq!((*a)[[0, 1]], a[(1, 0)]);
张量示例
目前,张量(因此也是矩阵)能做的事情不多。
注意:张量(以及因此矩阵)始终需要关联的后端。
use slas::prelude::*;
let t = moo![f32: 0..27].reshape([3, 3, 3], slas_backend::Rust);
assert_eq!(t[[0, 0, 1]], 9.);
let mut s = t.index_slice(1).matrix();
assert_eq!(s[(0, 0)], 9.);
assert_eq!(s.transpose()[(1, 0)], 10.);
到目前为止,就这些了...
为什么不直接使用 ndarray(或类似工具)呢?
在某些特定用例中,例如需要进行大量分配或在使用向量化操作中的引用数据时,Slas 可能比 ndarray 更快。此外,Slas 应始终至少与 ndarray 一样快,所以这不会造成伤害。
Ndarray 将始终使用你在 Cargo.toml
中选择的后端。使用 Slas,你可以选择代码中的后端,甚至可以创建符合你需求的自定义后端。
静态分配以及 Slas 与借用检查器协同工作的方式,还意味着你可能会在编译时捕获许多错误,而 ndarray 大多数时候会让你轻易地绕过去。例如,对大小不同的两个向量求点积,将在 ndarray 中引发恐慌,在 Slas 中引发编译时错误。
安装
默认情况下,Slas 会假设你的系统已安装了 blis。你可以通过禁用默认功能并启用 blis-static
功能,轻松地静态链接和编译 blis。如果你想选择自己的 blas 提供商,请将 dependencies.slas.default-features = false
设置在你的 Cargo.toml
中,并参考 blas-src 获取进一步说明。记住,如果你使用 blas-src 作为 blas 提供商,请添加 extern crate blas_src;
。
在 slas 的 crates.io 版本(v0.1.0 和 0.1.1)中,blis 将自动编译。
目前,如果你想使用 Slas 的最新版本,你需要在你的系统上安装 blis/blas。
- 在 Arch Linux 上,来自 AUR 的 blis-cblas v0.7.0 已经过测试并且工作正常。
- 在 Debian 上,你可以简单地运行
apt install libblis-dev
。 - 在 Windows 上,已测试 openblas-src。这意味着你需要禁用 Slas 的默认功能,遵循 openblas 中的安装说明,并将
extern crate openblas_src;
添加到你的主文件中。
环境变量
当未指定后端时,所选择的后端取决于环境变量。
例如,SLAS_BLAS_IN_DOT_IF_LEN_GE=50
将在默认情况下使用 blas,对于在大于或等于 50 个元素的向量上执行的任何点积操作。可以在 slas::config::BLAS_IN_DOT_IF_LEN_GE
中找到 SLAS_BLAS_IN_DOT_IF_LEN_GE
常量。
同样,这仅适用于未为向量指定后端的情况(例如 moo![f32: 1, 2].dot(moo![2, 1])
)。
变量和默认值
SLAS_BLAS_IN_DOT_IF_LEN_GE = 750
其他
- Slas 仍然处于非常早期阶段,并且可能会经历许多破坏性变化。
- 基准测试、测试及相关
待办事项
许可证:Apache-2.0
依赖项
~0–4.5MB
~85K SLoC