Repository navigation
Sparse Halide #7683
soufianekhiat
started this conversation in
Ideas
Sparse Halide
#7683
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I would to open a discussion about feasability and a possible new feature to change index for being able to remap sparse data to dense memory layout.
Here few of my prototype:
Version 0 (working but limited)
I'm exploring the feasability to extend the usage of Halide for Sparse data.
Based on:
"Halide is a programming language designed to make it easier to write high-performance image and array processing code on modern machines."
That can fit on this definition.
A simple way to tackle the problem is to keep everything in dense memory, and considering sparse as a simple reindexing to a dense memory layout.
Here my first prototype:
The reindexing job is done with:
Produces:

This approch would work for single indirection data. But limit us for richer indirection from "local sparse data". For instance I would like to have NanoVDB version based on Halide.
Version 1 (WIP proposition, open to discussion).
Inspired by Taichi (the C++ front-end is barely usable). I have a new front-end:
Here few usecase:
The Read function should produce the offset for the read or write.
RegularGrid AoS
SparseHalide::Block GridAoS; GridAoS.Dense({ x, y, z }, { 128, 128, 128 }) .Place(0, "p", SparseHalide::MemberType::f32, SparseHalide::MemberSemantic::Vec3); ////////////////////////////////////////////////////////////////////////// // High Level equivalent: struct Root { Vec3 p[128][128][128]; }; // p Reader i = {0, 1, 2}: f32 value_i = memory(3*(128*(128*x + y) + z) + i); // p Writer i = {0, 1, 2}: memory(3*(128*(128*x + y) + z) + i) = value_i;RegularGrid SoA
SparseHalide::Block GridSoA; GridSoA.Dense({ x, y, z }, { 128, 128, 128 }) .Place(0, "x", SparseHalide::MemberType::f32) .Place(1, "y", SparseHalide::MemberType::f32) .Place(2, "z", SparseHalide::MemberType::f32); ////////////////////////////////////////////////////////////////////////// // High Level equivalent: struct Root { Vec3 p[128][128][128]; };Here the main idea is the operator () or the function Read will produce another function for reading,
Exactly what BoundaryCondition do:
Quadtree MaxDepth 3
SparseHalide::Block QuadTree; QuadTree .Dense({ x, y }, { 4, 4 }).Bitmasked().Pointer() .Dense({ x, y }, { 4, 4 }).Bitmasked().Pointer() .Dense({ x, y }, { 4, 4 }) .Place(0, "Vel", SparseHalide::MemberType::f32, SparseHalide::MemberSemantic::Vec3) .Place(1, "Density", SparseHalide::MemberType::f32); ////////////////////////////////////////////////////////////////////////// // High Level equivalent struct Root { std::bitfield<4*4> childsMasks; Child1* children1[4][4]; }; struct Child1 { std::bitfield<4*4> childsMasks; Child2* children1[4][4]; }; struct Child2 { Vec3 Vel[4][4]; f32 Density[4][4]; }; // Dense Memory Layout: // ... memoryBitRoot ... // --- padding --- // ... memoryBitChild1... // --- padding --- // ... memoryBitChild2... // --- padding --- // velocities // --- padding --- // densities // --- padding --- // Pseudo code, Reader Vec2i ChildRoot; ChildRoot.x = RoundToInt(Bound(v.x, minX, maxX)/4.0f/4.0f/4.0f); ChildRoot.y = RoundToInt(Bound(v.y, minY, maxY)/4.0f/4.0f/4.0f); Vec2i Child1; Child1.x = RoundToInt(Bound(v.x, minX, maxX)/4.0f/4.0f); Child1.y = RoundToInt(Bound(v.y, minY, maxY)/4.0f/4.0f); Vec2i Child2; Child2.x = RoundToInt(Bound(v.x, minX, maxX)/4.0f); Child2.y = RoundToInt(Bound(v.y, minY, maxY)/4.0f); bool bitRoot = ReadBit(memoryBitRoot, ChildRoot); bool bitChild1 = ReadBit(memoryBitChild1, Child1); bool bitChild2 = ReadBit(memoryBitChild2, Child2); // 64 == 4*4*4 f32 vel_i = select(bitRoot, select(bitChild1, select(bitChild2, ReadFloat(memoryVel, 64, 64, 64, 3, x, y, z, i) , 0.0f) , 0.0f) , 0.0f); // Pseudo code Writer: Vec2i ChildRoot; ChildRoot.x = RoundToInt(Bound(v.x, minX, maxX)/4.0f/4.0f/4.0f); ChildRoot.y = RoundToInt(Bound(v.y, minY, maxY)/4.0f/4.0f/4.0f); Vec2i Child1; Child1.x = RoundToInt(Bound(v.x, minX, maxX)/4.0f/4.0f); Child1.y = RoundToInt(Bound(v.y, minY, maxY)/4.0f/4.0f); Vec2i Child2; Child2.x = RoundToInt(Bound(v.x, minX, maxX)/4.0f); Child2.y = RoundToInt(Bound(v.y, minY, maxY)/4.0f); bool bitRoot = SetBit(memoryBitRoot, ChildRoot, true); bool bitChild1 = SetBit(memoryBitChild1, Child1, true); bool bitChild2 = SetBit(memoryBitChild2, Child2, true); // 64 == 4*4*4 SetFloat(memoryVel, 64, 64, 64, 3, x, y, z, i, value_i); //////////////////////////////////////////////////////////////////////////Some extra sparse layout:
I'm open to any comment.
All reactions