bonsairobo / building-blocks

A voxel library for real-time applications.
MIT License
369 stars 30 forks source link

[Question] Is Array Indexing Unsafe in Release Mode? #32

Closed zicklag closed 3 years ago

zicklag commented 3 years ago

Hey there! I was looking into this library and I found this line in the docs:

Indexing assumes that the coordinates are in-bounds of the array, panicking otherwise. Bounds checking is only enabled in debug mode.

I was wondering what that meant would happen when indexing out-of-bounds on release builds. Would indexing out-of-bounds be unsafe in release builds?

bonsairobo commented 3 years ago

Here is the code in question.

In release builds indexing out of bounds is undefined behavior. This is here to avoid overhead of bounds checking that happens in slice::get.

zicklag commented 3 years ago

It seems like maybe that should be marked unsafe, then. I would be surprised to be able to cause undefined behavior with 100% safe rust, regardless of what optimization level I compile it at. ( Rust bugs excluded )

Maybe bounds checking could be enabled by default, but there could be an unsafe variant of the function, similar to the Rust slice API?

I personally would rather have it be safe and take the performance hit most of the time, unless I have a situaion where I really need it and benchmarks prove that it is indeed valuable. And at the very least I would want it marked as unsafe if it could cause unefined behavior.

bonsairobo commented 3 years ago

Fair points. I'll see if there is any appreciable regression in benchmarks from turning on bounds checking. If not, I'll just make it safe.

There actually used to be a unsafe versions of the Get traits, but it was cumbersome to maintain the extra GetUnchecked, GetRefUnchecked and GetMutUnchecked traits.

bonsairobo commented 3 years ago

Well unfortunately it seems that using "intrinsic slice indexing" instead of get_unchecked gives about a 50% regression in benchmarks that rely heavily on indexing.

zicklag commented 3 years ago

Oh, bummer. Well, at least there's nothing stopping us from using the get_unchecked for the algorithms implemented inside of building_blocks. That way users get the performance for those algorithms, and no unsafe for users, unless they need to implement thier own algorithms, at which point they can measure the cost for themselves.

On another note, I've seen instances on the Rust users forum where people have said that Rust can, in many circumstances, compile out the bounds checks completely, but sometimes it requires organizing the code differently so that rustc can make the determination that the index will never go out of bounds. I'm not sure if that helps us with the algorithms you are working with or not, but here's a relevant thread: https://users.rust-lang.org/t/performance-of-array-access-vs-c/43522/13?u=zicklag

bonsairobo commented 3 years ago

I'll try bringing back the unsafe traits, but I will only implement them where absolutely necessary, like for Channel and Array.

ChunkMap shouldn't need it because if you access out of bounds, you will always get an ambient value, so that's known to be safe to use get_unchecked internally without exposing an unsafe getter.

zicklag commented 3 years ago

Cool, sounds like a good idea. :+1:

bonsairobo commented 3 years ago

I think I've addressed this issue as much as I can in 6f7b86775.

Now there are both checked and unchecked variants of the Get* traits. Unchecked variants are marked unsafe. Most algorithms in this library are able to verify they're not accessing out of bounds with a single check outside of any tight loops.

zicklag commented 3 years ago

Yay! :tada: Thanks!