9
$\begingroup$

I'm having trouble in comparing these two memory consistency models.

Essentially for sequential consistency I think of real code like this:

int x, y;

void ThreadA()
{
   x = 20; //Write
   int a = y; //Read
}

void ThreadB()
{
    y = 20;
    int b = x;
}

In a sequential consistency environment it's impossible for a or b to not be 20. That is either a = 20, b = 20 or a = 20 && b = 20.

But how does quiescent consistency fit in to this example, and why is it compositional?

$\endgroup$

1 Answer 1

13
$\begingroup$

Sorry for the late reply, but I've just found the question (questions, indeed). I am studying concurrency as well and I'll try to share some ideas with you.

First, let's start with sequential consistency. A model has this property if operations appear to take effect in program order. In other words, the order in which lines of code are executed is the one specified in the source file. This prerequisite is thread-specific: operations involving different threads are unrelated by the program order. So, the property grants the following: operations issued by the same thread are executed in the same order specified by the thread source code. Operations issued by different threads can happen in any order. This prerequisite may seem obvious, but it is not always the case (compiler optimizations may change the order in which operations are issued, thus making the program order different from the source).

Your example is right, but let me give you some others. Consider this program P1 (I'll tag each line for an easy reference):

   int x = 1;

   void ThreadA()
   {
A1:    x = x * 2;
A3:    int a = x;
   }

   void ThreadB()
   {
B2:    x = 20;
   }

Is there a sequential execution in which, at the end, a = 40? Yes: B2, A1, A3.

B2 and A1 can be executed in any order (they belong to different threads). A1 is executed before A3 (program order = source code order).

Now consider this program P2:

   int y = 1;

   void ThreadA()
   {
A2:    y = 40;
   }

   void ThreadB()
   {
B1:    y = y / 2;
B3:    int b = y;
   }

Is there a sequential execution in which, at the end, b = 20? Yes: A2, B1, B3.

What about composition? Let's compose P1 and P2. We get P1∘P2:

   int x = 1;
   int y = 1;

   void ThreadA()
   {
A1:    x = x * 2;
A2:    y = 40;
A3:    int a = x;
   }

   void ThreadB()
   {
B1:    y = y / 2;
B2:    x = 20;
B3:    int b = y;
   }

If sequential consistency were compositional, then there should be an execution in which, at the end, a = 40 and b = 20. Is this the case? No. Let's give a formal proof oh why it can't be the case. I'll write "o1 → o2" to say that the operation o1 must be executed before operation o2.

In P1∘P2, due to sequential consistency, the following must holds: A1 -> A2 and B1 -> B2 (per-thread program order). In order to get a = 40 also B2 -> A1 must hold. In order to get b = 20 also A2 -> B1 must hold. Can you see the precedence chain? A1 -> A2 -> B1 -> B2 -> A1 -> ...

P1 and P2 were sequentially consistent, but their composition P1∘P2 was not. Sequential consistency is not compositional. The example you provided was not tricky enough to show this fact.

Now, let's consider quiescent consistency. It's difficult for me to explain this property without using the object-oriented paradigm. In fact, quiescent consistency can be easily understood in terms of pending method calls on objects. Nevertheless, I'll try to stick with the imperative paradigm (pending method calls become functions started but not completed, objects become variables involved in functions).

A function call is composed by an invocation and a response. A function is pending if it has been invoked by a thread but it has not returned a response yet to such thread. A period of quiescence for a variable is a time interval in which there are no pending function calls (by any thread) operating on it. A model has the quiescent consistency property if function calls operating on the same variable and separated by a period of quiescence appear to take effect in their real-time order (the one specified by source code). From the converse point of view, the definition states that operations on the same variable executed by functions called concurrently by different threads (not separated by quiescence) can happen in any order.

I've tried for many hours to design a meaningful example using only read-write operations to show you the difference between quiescent and sequential consistency, but I didn't succeed. Let me use another example involving sets. I will use a bit vector to keep track of which integers (from 0 to 4) are in the set:

   int set[5] = {0, 0, 0, 0, 0};  // 0 if i-th item is absent, 1 otherwise

   void add(int number) {
L1:    set[number] = 1;
L2:    temp foo = set[0];
   }

   void remove(int number) {
       set[number] = 0;
   }

   int contains(int number) {
       return set[number] == 1;
   }

   void ThreadA()
   {
A1:    add(1);
   }

   void ThreadB()
   {
B1:    add(2);
B2:    remove(2);
B3:    int res = contains(2);
   }

Is there a sequential execution in which, at the end, res = 1? No: due to sequential consistency B1 -> B2 and B2 -> B3, so B1 -> B3. So, remove(2) is always executed after add(2) and contains(2) will always returns 0.

Is there a quiescent execution in which, at the end, res = 1? Yes! Consider this execution:

Thread A invokes add(1) at A1.

Thread A executes L1 (but not L2). // since there is a pending call on the set, now Thread B is free to execute B2 before B1 because these calls involve the set as well

Thread B calls B2. // invocation + response

Thread B calls B1. // invocation + response

Thread A executes L2 and add(1) responds. // now there is quiescence on the set

Thread B executes B3. // invocation + response

Now res = 1.

Unfortunately, I still can't answer the latest question on why quiescent consistency is compositional: I found your question while I was looking for this exact answer indeed. If I come up with something I'll let you know.

For a good reading on the topic have a look at chapter 3.3 of the book "The Art of Multiprocessor Programming" by Herlihy and Shavit.

EDIT: I've just found a great page explaining why quiescent consistency is compositional. And this is another very good reading!

EDIT2: Fixed little error in the sequential consistency composability example.

$\endgroup$
5
  • $\begingroup$ Great answer for a tough question cheers! (I figured it was touch at no one answered for many months :P). $\endgroup$
    – William
    Jun 16, 2016 at 18:57
  • $\begingroup$ I can get my head around quiescent consistency and sequential consistency individually, (aswell as strict consistency) but I find it extremely difficult when relating them to one-and-other. Although in in your first example if the execution was B2, A1, A3.in real time then anything other than a = 40 would be impossible under strict consistency. $\endgroup$
    – William
    Jun 16, 2016 at 18:57
  • $\begingroup$ But I just don't think I understand what is truly meant by quincent consistency being 'compositional'. I've read your 3rd example like 30 times(and the description of course) but just struggling to understand! $\endgroup$
    – William
    Jun 16, 2016 at 18:59
  • $\begingroup$ First, I've fixed a little error in the description of the P1∘P2 example. But I think it was already clear to you why sequential consistency (SC) isn't compositional. Secondly, a = 40 isn't the only possible result in the first example provided [that assumes a SC model], because A1, A3, B2 is legit and leads to a = 20. My last example goal was to highlight differences between SC and quiescent consistency (QC) (res=1 is possible in one model and impossible in the other). I didn't cover the topic of QC composability: please read the link provided in the first edit to gain insights on the subject $\endgroup$ Jun 17, 2016 at 10:10
  • $\begingroup$ @William, I did not get why you have forced the object not to have a period of quiesence between add(2) and remove(2) (by assuming that thread A has a pending method call on the object). Alternatively, would it be an issue if I allowed Thread A to complete its execution of the add() method and then Thread B executed B2, and then B1. Would this sequence be quiescentially consistent? $\endgroup$ Nov 7, 2016 at 18:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.