Giter Club home page Giter Club logo

block-grid's Introduction

block-grid

A quick, cache-conscious, blocked 2D array

Crate Docs License CI

block-grid gives you a fixed size, two-dimensional array, with a blocked memory representation. This has the sweet benefit of being much more cache-friendly if you're often accessing nearby coordinates.

Features

  • Can store any type
  • Generic compile-time block sizes
  • Indexing with (row, col): (usize, usize)
  • Block level access with Block and BlockMut
  • Constructors from row-major and column-major order arrays
  • Iterators for in-memory and row-major order, and by block
  • no_std and serde support
  • Also supports no blocks (i.e. classic row-major)

Example

use block_grid::{BlockGrid, CoordsIterator, U2};

fn main() {
    let data: Vec<_> = (0..(4 * 6)).collect();

    // Construct from row-major ordered data
    let grid = BlockGrid::<usize, U2>::from_row_major(4, 6, &data).unwrap();

    // The 2D grid looks like:
    // +-----------------------+
    // |  0  1 |  2  3 |  4  5 |
    // |  6  7 |  8  9 | 10 11 |
    // |-------+-------+-------|
    // | 12 13 | 14 15 | 16 17 |
    // | 18 19 | 20 21 | 22 23 |
    // +-----------------------+

    // Indexing
    assert_eq!(grid[(1, 3)], 9);

    // Access raw array
    let first_five = &grid.raw()[..5];
    assert_eq!(first_five, &[0, 1, 6, 7, 2]);

    // Iterate over blocks, and access the last
    let block = grid.block_iter().last().unwrap();
    assert_eq!(block[(0, 1)], 17);

    // Iterate in row-major order
    for (i, &x) in grid.row_major_iter().enumerate() {
        assert_eq!(x, i);
    }

    // Iterate in memory order, with coordinates
    for ((row, col), &x) in grid.each_iter().coords() {
        assert_eq!(row * 6 + col, x);
    }
}

Why

TODO: Stuff about caches

Trade-offs

  • Non-resizable, and grid dimensions have to be a multiple of the block size.
  • Currently, only square blocks, and power-of-two block sizes are supported.
  • Computing the modified index takes just a bit more time.
  • There are still cache misses when you cross tile boundaries.
  • No support for strides or general subsets.

Changelog

See CHANGELOG.md.

License

block-grid is licensed under the MIT license.

Alternatives

If your access patterns suit a typical row-major memory representation, you can still use block-grid! If you truly desire alternatives, however, check out array2d, imgref, grid, or toodee. The last two support dynamic resizing. For matrices and linear algebra, there's also nalgebra.

block-grid's People

Contributors

gunvirranu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

george-lewis

block-grid's Issues

Add blur benchmark

Write benchmark similar to tests/blur.rs. Should compare crates block-grid, toodee, and array2d. For each, test against a common naive indexed version (Index<Coords>) and an idiomatic version.

Change to integer const generics

Rust 1.51 will have min_const_generics which eliminates the need for BlockWidth generics. However, the current method will still work, so I'll probably delay it for a bit while people trickle over to the latest version. Obviously gonna be a breaking change.

Create Conway's game of life benchmark

Similar to #1, create a benchmark that runs Conway's game of life. Should benchmark the following versions.

  • Generic indexed approach (Index<Coords>)
  • Idiomatic BlockGrid
  • Idiomatic array2d
  • Idiomatic toodee (#19)

Add raw slice access to Block

Create methods raw() and raw_mut() on Block/BlockMut to get a reference to the internal slice &[T]. This might be needed for some block-level algorithms.

Make CoordsIterator a sealed private trait

There's no reason a user would need to implement CoordsIterator, and also current_coords() shouldn't need to be called by users, so see if it is possible to make the trait "pseudo-private", so they can access it, but can't implement it, and also coords() is the only public method available.

Add coordinates to `each_iter`

Change the iteration item for each_iter to (Coords, &T) (similarly with each_iter_mut). I wanna do this so that if you just wanna iterate over each element, but still need the indices for some reason. Using row_major_iter for that will be slower b/c of block changes every few elements. Basically, if you need to iterate over each element (with coordinates for whatever reason), it should be done in storage order. Also, as with row_major and block_iter, you can't just user .enumerate() to calc. 2D coordinates on the fly.

Just from thinking about it, this seems kinda annoying, b/c generating the coordinates from the in-memory index is weird. I think it would be useful though.

Support no block memory representation

Could be done by supporting a block width of U1. Just need to check if it all works out.

A few reasons why this might be useful:

  • Allow testing to see if blocks is useful at all (try U1 vs other block widths and see performance)
  • If both needed, don't need a whole other library
  • Even if only U1 needed, I think this lib has a decent interface and some useful helper funcs that still apply

Raw reference access to buffer

Create a BlockGrid::raw and raw_mut returning a references to the buf field. Also, you can't grow mutable array references right..? That wouldn't make much sense.

Add iterator on Block

Should create a in-memory/each/row-major (all the same) iterator for Block and BlockMut. I think this could be useful when using block_iter.

Consider non-square block sizes

Don't know if this was be particularly useful, but consider allowing non-square block sizes. This is two ways of implementing it.

Could by done by parameterizing as BlockGrid<W: BlockDim, H: BlockDim, T> where W and H are block width and height respectively. A SquareGrid type-alias could be for square blocks.

Alternatively, a BlockDims trait which itself holds the block and width. Would then have to create a buunch of types for each combination, such as U4ByU8 (or U4xU8), with type-aliases for the current square blocks.

Inline a bunch of small functions

There are a bunch of small methods which I can mark as #[inline] to further optimize across crate boundaries. Build times with LTO are annoying, so this should close the gap a little. Should also hopefully give better benchmarks results.

Investigate Serde support

Serialization support. Probably would be easy to add cause there's nothing fancy to store. Just gotta figure out the no_std and crate options/features stuff.

Change some operations to unchecked

Find all the places where I can "safely" use unchecked operations to increase performance. Investigate the performance gain (if any), and change if worthwhile. Remember to add debug assertions for a bit of safety.

Make crate no_std compatible

Make crate no_std compatible. Because Vec is used to store data, alloc is used for collections.

Consider making this crate no_std compatible. The data is stored in a Vec however, so alloc will be needed.

Try an each iter adapter struct

#24 introduced coordinates to each_iter, but the implementation is done via a on-the-fly calculation from an enumerated index. Should implement a EachIterCoords iterator with an inner iterator, that tracks the coordinates during iteration. This might improve performance instead of calculating from scratch every time.

Bug in WithCoordsIter nth impl

Bug in the impl of Iterator::nth for WithCoordsIter . When n = 1, the if isn't triggered, and the returned value is wrong. The condition needs to be if n >= 1.

block-grid/src/iters.rs

Lines 304 to 309 in 03a9a52

fn nth(&mut self, n: usize) -> Option<Self::Item> {
if n > 1 {
self.iter.nth(n - 1)?;
}
self.next()
}

Investigate grid dimensions not a multiple of block size

Look into having a BlockGrid with dimensions that are not a multiple of B::WIDTH. There would then be two pairs of dimensions, an actual and an internal. It don't think it affects indexing into a BlockGrid too much, cause you just replace the bounds check to use the actual dimensions. Adjusting EachIter might be annoying. Handling incomplete Blocks on the border is gonna be ugh ๐Ÿ˜ฟ.

Write basic documentation

Write some basic crate, type, and method documentation.

I'm not quite sure what needs to be done, but set up stuff for library and method documentation.

Compare to base branch in PR bench pipeline

Follow up of #9. Make the manually triggered pipeline checkout the base branch (probably just master), save a baseline, and then bench again with the actual branch. Criterion should then give you the perf delta.

Also look into using the critcmp tool to see if it has any nice features.

Set up CI

Consider Github Actions vs Travis.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.