Replies: 5 comments 2 replies
-
What are the advantages of using custos' device
let device = CPU::new();
let a = Matrix::from((&device, 2, 3, [1., 2., 3., 4., 5., 6.]));
let b = Matrix::from((&device, 2, 3, [1., 2., 3., 4., 5., 6.]));
let c_cpu = a + b;
let device = CUDA::new(0)?;
let a = Matrix::from((&device, 2, 3, [1., 2., 3., 4., 5., 6.]));
let b = Matrix::from((&device, 2, 3, [1., 2., 3., 4., 5., 6.]));
let c_cuda = a + b;
assert_eq!(c_cpu.read(), c_cuda.read()); It should be possibly to create a struct that returns a device depending on the compilation target and activated feature-set.
let buf = buf![2, 3, 6].to_gpu(); How can we use
pub struct Buffer<'a, T = f32, D: Device = CPU, const N: usize = 0> {
pub ptr: D::Ptr<T, N>,
pub len: usize,
pub device: Option<&'a D>,
pub flag: BufFlag,
pub node: Node,
} The const generic optionally enables a |
Beta Was this translation helpful? Give feedback.
-
Thanks, this is really helpful. I see a lot of interesting principles, we currently have a way of providing bindings for external libraries. For example the trait let buf = buf![2, 3, 6];
let el = buf.get(0);
println!("{}", el);
>>> 2
let a = buf!([1, 2, 3]);
let v: Vec<i32> = a.iterator(0).map(|&v| v).collect();
assert_eq!(v, vec!(1, 2, 3));
let mut a = buf!([1, 2, 3]);
a.iterator_mut(0).for_each(|v| *v = 1);
assert_eq!(a, buf!([1, 1, 1])); This may be possible with something like: impl<T: Debug + Display + Copy + Sized> Array<T, (usize, usize)>
for Buffer<T, D>
where
D = Device
{
fn get(&self, pos: (usize, usize)) -> &T {
... return element at position ...
}
fn shape(&self) -> (usize, usize) {
... return rows, columns shape ...
}
fn is_empty(&self) -> bool {
...
}
fn iterator<'b>(&'b self, axis: u8) -> Box<dyn Iterator<Item = &'b T> + 'b> {
assert!(
axis == 1 || axis == 0,
"For two dimensional array `axis` should be either 0 or 1"
);
match axis {
0 => Box::new(self.iter()),
_ => Box::new(
(0..self.ncols()).flat_map(move |c| (0..self.nrows()).map(move |r| &self[[r, c]])),
),
}
}
} This is just a generic (probably not working) example, the same can be done for The part I cannot see at the moment is how to instantiate a
it would make sense or provide advantages to have a "scoped" device? Like: /// this is an high-level public API called by a client or end user
fn dot(&self, x: Buffer) { // assuming self is a Buffer
let device = CPU::new_with((self, x)); // "load" a device with existing buffers
device.dot().apply() // this returns the result of a dot product between the buffers in the device
// device is dropped
} This may look trivial with dot-product but maybe for operations on trees could do to load a tree in a device and use it only for that tree? We really want to develop things consistently from public interface to allocation level, sometimes there is tension between ergonomicity and "programmability" so better think things ahead. Please follow up with your ideas. |
Beta Was this translation helpful? Give feedback.
-
The part I cannot see at the moment is how to instantiate a Device "automatically" without the end user needing to instantiate explicitly This should work too, however, this probably isn't what you want: pub struct SomeMLAlgo<T, D: Device = custos::CPU> {
device: D,
a: Matrix<T, D>, // or whatever
...
}
impl<T, D: Device> SomeMLAlgo<T, D> {
pub fn fit(a: &[T], ...) -> Self {
let device = D::new();
let a = Matrix::from((&device, ..., a));
Self {
device,
a
}
}
} What is the cost of instantiating a device? /// this is an high-level public API called by a client or end user
fn dot(&self, x: Buffer) { // assuming self is a Buffer
let device = CPU::new_with((self, x)); // "load" a device with existing buffers
device.dot().apply() // this returns the result of a dot product between the buffers in the device
// device is dropped
} In this case I would probably do something like this: fn dot(&self, x: Buffer) {
self.device().dot(self, x)
}
`` |
Beta Was this translation helpful? Give feedback.
-
About GPUs: are you interested in doing something with triton? Do you see something like that to be a good fit for a Rust integration? If yes, how would you integrate kernels written in Triton into a Rust library? cc: @morenol @Steboss89 please contribute to the conversation if you have any idea |
Beta Was this translation helpful? Give feedback.
-
@Mec-iS |
Beta Was this translation helpful? Give feedback.
-
custos
andcustos-math
are two interesting device-centered libraries:custos
: "A minimal OpenCL, CUDA and host CPU array manipulation engine"custos-math
: "provides CUDA, OpenCL and CPU based matrix operations using custos"@elftausend is creator of
custos
It would be interesting to use custos functionalities, here some questions to start exploring this possibility:
let device = CPU::new();
in allocation compared to usual allocation? see example:custos
Buffer
for arrays? what would be the advantages?custos
as for the ones forndarrays
?linalg
usingcustos-math
? how can we provide these functionalities without impacting binary size (minimise dependencies)custos
codebase to replicate in smartcore?Beta Was this translation helpful? Give feedback.
All reactions