5

Consider:

class Vector
{
  double x, y, z;
  // …
};

class Object
{
  Vector Vec1, Vec2;
  std::mutex Mtx1, Mtx2;

  void ModifyVec1() { std::lock_guard Lock(Mtx1); /* … */ }
  void ModifyVec2() { std::lock_guard Lock(Mtx2); /* … */ }
};

If either the mutexes or the guarded variables are stored contiguously and they share a cache line when cached, can this cause a sort of “cross-locking”?

If so, is it a good practice to declare the mutexes right after (or before) the variable they guard?

Aligning the class to std::hardware_destructive_interference_size (P0154) might avoid this effect. Are the potential benefits worth the overalignment of the object?

metalfox
  • 6,301
  • 1
  • 21
  • 43
  • The C++ standard does not require class members to be laid out in any particular order (with some caveats that are not applicable here). You can determine from your compiler's documentation whether it uses a deterministic order for class members (likely). – Sam Varshavchik Sep 16 '16 at 11:09
  • 3
    @SamVarshavchik For a standard-layout class they must be in the declaration order, and based on what's posted so far, this could be a standard layout class. Also that doesn't really matter as OP is asking about details of particular implementations – M.M Sep 16 '16 at 11:14
  • @Rakete1111 Please feel free to edit – metalfox Sep 16 '16 at 11:17
  • @metalfox Wait, what? I was just "answering" your first question :) Rather to put them in a specific order above variables, I would give them names that make it clear that the mutex guards that specific variable. – Rakete1111 Sep 16 '16 at 11:18
  • @Rakete1111 The question is about how member order might affect caching, not about making the relationship between the members clear to a human reader. – molbdnilo Sep 16 '16 at 12:05
  • @molbdnilo yeah, just realized that :) – Rakete1111 Sep 16 '16 at 12:06
  • Btw what compiler are you on that actually implements those two? – Barry Sep 16 '16 at 12:17
  • @SamVarshavchik: Yes, the standard does give ordering information, even for non-standard layout types. For any types, members in the same access class (public/protected/private) are given space in their declared order. There may be padding or not, as implementation-defined. But they are in that order. – Nicol Bolas Sep 16 '16 at 14:19
  • @Barry I'm just trying to figure out their usefulness. – metalfox Sep 19 '16 at 06:42

1 Answers1

4

Your variables seem unrelated in your question, so rather than hardware_destructive_interference_size you probably want hardware_constructive_interference_size:

struct keep_together {
    std::mutex m;
    Vector v;
};

alignas(std::hardware_constructive_interference_size) keep_together k1;
alignas(std::hardware_constructive_interference_size) keep_together k2;

destructive you want to use for cases like lock-free queues, were threads are reading two different atomics and you want to make sure they both actually get loaded. If that's an issue here, you'll need to explain why false sharing is what you're avoiding.

Is it a good practice to declare the mutex right after (or before) the variable it guards to increase the chance they are on the same cache line?

That's constructive interference.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks for the clarification. I mixed both concepts. I'd like to encourage true sharing, but also to discourage false sharing in case it might happen. Imagine the mutexes are stored contiguously. Might they share a cache line? I edited the question to hopefully make it clearer. – metalfox Sep 19 '16 at 06:49