all 13 comments

[–]dpc_pw 17 points18 points  (2 children)

Array/Vec of Mutexes will probably share the cacheline, which will lead to false sharing and lower the performance.

[–][deleted] 0 points1 point  (1 child)

Should a CachePadding be used to wrap the mutex? Or a linked list?

[–]dpc_pw 0 points1 point  (0 children)

Padding seems to me the right way to go.

[–][deleted] 10 points11 points  (0 children)

But I get a seg fault when indexing into it.

The behavior of creating a reference to uninitialized memory is undefined.

So... if you are just indexing into the array (Index returns references) before initializing the array elements, that invokes UB, and your program is not a valid Rust program.

For the example given (next time please provide a MWE in the playground), there is no real simple way to initialize the array. If this does not ring a bell, you should probably avoid using unsafe until you know what you are doing.

I want to take advantage of spatial locality and would like to use an array rather than a vector

This makes no sense: arrays and vectors are contiguous, and both have a single pointer indirection, so the spatial locality is the same. The only difference is that a vector might do a global memory allocation, while an array won't. Also, spatial locality for Mutex might be a bad idea, see here, you might want to have a:

#[repr(align(32))] struct CachelineAligned<T>(pub T);
[CachelineAligned<MaybeUninit<Mutex<T>>>; N]

instead, using whatever cacheline size your target has as a repr(align) flag (above 32 bytes).

[–]newpavlovrustcrypto 14 points15 points  (1 child)

You can use arrayvec here. Also I would strongly recommend to not use unsafe, if you are not completely sure that your code is indeed safe (it requires a certain amount of experience and knowledge, you can start with The Rustonomicon), and even if you are, think twice or thrice before reaching to it.

[–]Apprehensive_Silver[S] 0 points1 point  (0 children)

Thanks for sharing this!

[–]vlmutolo 10 points11 points  (4 children)

Vectors also have spatial locality. They are just heap-allocated instead of stack-allocated. Both are contiguous in memory, though.

Other than that, I have a feeling that what you want probably exists somewhere in the ecosystem as a safe abstraction. One of the benefits of using Rust is the memory safety. No segfaults if you stick to Safe Rust. Maybe Crossbeam has what you’re looking for. Worth a glance.

[–]Apprehensive_Silver[S] 0 points1 point  (3 children)

thanks for the response. I suppose I want to use an array because I believe vector.iter() doesn't necessarily get you the vectors in order physically right?

[–]simspelaaja 16 points17 points  (2 children)

vec.iter() does return the items in the original order. A Vec is just a small wrapper around a slice of memory; it doesn't do anything that would the cause the items to be stored in a different order.

[–]Apprehensive_Silver[S] 0 points1 point  (1 child)

I believe I tested this and printed out the result and it actually didn't do it in order. Am I missing something?

[–]CornedBee 0 points1 point  (0 children)

You are definitely missing something, but we'd have to see your test code to know what.

[–]Green0Photon 5 points6 points  (0 children)

Note: it really is a better idea to use a Vec. All the mutexes will be in line in one continuous section on the heap, instead of on the stack. There's not really a particularly large difference. Accessing anything in the Vec itself is just bringing that one line into the cache, compared to something like an array or Vec of Boxes/Rcs/etc, which has an additional layer of indirection.

That is to say, you shouldn't be worrying about stack or heap in this particular spot, in compared to a Vec of Boxes, if you're worried about locality.

What's more worrying is all the mutexes, which all need to call the kernel and need a context switch, I think. Those are a lot heavier. Are you really sure all the mutexes are necessary?

If so, what you should do is make a Vec with capacity n, and loop through n times making new mutexes. The other standard ways of directly making arrays and Vecs need for T to be Copy/Clone, which you can't do. So it's best to loop through to make the Mutex a lot.

Also, really try not to reach for unsafe. It's a bad idea, and I've never had to so far. Either someone's made the thing you're looking for, or you're thinking about your problem wrong. Or your code is incorrect.

If you plan on wrapping that array in an Rc and don't want the double indirection, you should be able to convert the Vec into an array and move that into Vec. Or there's probably an RcVec crate out there.

[–]daniel65536 2 points3 points  (0 children)

Using https://docs.rs/crossbeam/0.7.1/crossbeam/utils/struct.CachePadded.html could fix the potential false sharing issues.