user124784
user124784

Reputation: 956

Creating a struct with an array (Matrices)

I'm currently working on a simple project to get myself familiar with Rust. I don't have much systems programming experience but I'm hoping to learn!

I'm trying to create a Matrix struct but I'm finding it hard to figure out how I should store the data. I feel like I should be able to use an array. The size of the matrix must be defined on construction and so I would hope I can store the array on the stack.

Right now my code looks like this:

use std::ops::Mul;
use std::ops::Add;
use std::ops::Div;

struct Matrix {
    cols: i32,
    rows: i32,
    // Of course this doesn't work!
    data: [f32; ..cols*rows]
}

// Below here are a bunch of stub methods.
impl Mul<f32> for Matrix {
    type Output = Matrix;

    fn mul(self, m: f32) -> Matrix {
        return self;
    }
}

impl Mul<Matrix> for Matrix {
    type Output = Matrix;

    fn mul(self, m: Matrix) -> Matrix {
        // Will use Strassen algorithm if large, traditional otherwise
        return self;
    }
}

impl Add<Matrix> for Matrix {
    type Output = Matrix;

    fn add(self, m: Matrix) -> Matrix {
        return self;
    }
}

impl Div<f32> for Matrix {
    type Output = Matrix;

    fn div(self, f: f32) -> Matrix {
        return self;
    }
}

Upvotes: 5

Views: 6531

Answers (2)

Tianyi Shi
Tianyi Shi

Reputation: 969

You may also use a vector of vector (Vec<Vec<T>>), without implementing your own Matrix or using a library. But this is slighly slower than Adrian's approach in terms of indexing. nalgebra is good at vectorized computation, but it performs worse than simple matrices made of Vec<T> or Vec<Vec<T>> when it comes to elementwise operation. Here are some benchmarks:

test bench_vec               ... bench:  84,694,933 ns/iter (+/- 7,412,836)
test bench_vec_of_vec        ... bench:  87,083,636 ns/iter (+/- 1,171,842)
test bench_vec_unsafe        ... bench:  41,440,947 ns/iter (+/- 752,463)
test bench_vec_of_vec_unsafe ... bench:  44,532,595 ns/iter (+/- 629,209)
test bench_nalgebra          ... bench: 452,872,630 ns/iter (+/- 40,284,295)
#![feature(test)]

extern crate test;
use na::Matrix;
use na::{Dynamic, VecStorage};
use nalgebra as na;

type DMatrixi32 = Matrix<u8, Dynamic, Dynamic, VecStorage<u8, Dynamic, Dynamic>>;

use test::Bencher;

#[bench]
fn bench_vec_of_vec(b: &mut Bencher) {
    let (m, n) = (10000, 10000);
    let mut matrix = vec![vec![0u8; n]; m];
    b.iter(|| {
        for i in 0..m {
            for j in 0..n {
                matrix[i][j] = 1u8;
            }
        }
    });
}

#[bench]
fn bench_vec(b: &mut Bencher) {
    let (m, n) = (10000, 10000);
    let mut matrix = vec![0u8; n * m];
    b.iter(|| {
        for i in 0..m {
            for j in 0..n {
                matrix[i * n + j] = 1u8;
            }
        }
    });
}

#[bench]
fn bench_vec_of_vec_unsafe(b: &mut Bencher) {
    let (m, n) = (10000, 10000);
    let mut matrix = vec![vec![0u8; n]; m];
    b.iter(|| {
        for i in 0..m {
            for j in 0..n {
                unsafe {
                    *matrix.get_unchecked_mut(i).get_unchecked_mut(j) = 1u8;
                }
            }
        }
    });
}

#[bench]
fn bench_vec_unsafe(b: &mut Bencher) {
    let (m, n) = (10000, 10000);
    let mut matrix = vec![0u8; n * m];
    b.iter(|| {
        for i in 0..m {
            for j in 0..n {
                unsafe { *matrix.get_unchecked_mut(i * n + j) = 1u8 };
            }
        }
    });
}

#[bench]
fn bench_nalgebra(b: &mut Bencher) {
    let (m, n) = (10000, 10000);
    let mut matrix = DMatrixi32::from_vec(m, n, vec![0u8; n * m]);
    b.iter(|| {
        for i in 0..m {
            for j in 0..n {
                matrix[(i, j)] = 1u8;
            }
        }
    });
}

Upvotes: 0

Adrian
Adrian

Reputation: 15171

The easiest way to do this would be with a Vec.

struct Matrix {
    cols: i32,
    rows: i32,
    data: Vec<f32>
}

impl Matrix {
    fn new(cols: i32, rows: i32) -> Matrix {
        Matrix {
            cols: cols,
            rows: rows,
            data: vec![0.0; cols * rows]
        }
    }
}

If you don't want to store the data on the heap, you could make Matrix into a dynamically sized type, but this is hard to do and isn't really supported. See a Reddit thread on that topic.

If you don't want to use a Vec but are okay with storing the data on the heap, you could use a boxed slice instead (Box<[f32]>). See Vec::into_boxed_slice for one way to create one.

And of course, if you really don't want to use the heap, you could make different Matrix types for different sizes of matrices. This is what the crate nalgebra does.

Upvotes: 5

Related Questions