frlock and barrier discussion
Stephen wrote:
[snip - memory barrier for fr_write_begin]
Quoted 1 line
Using
mb()is more paranoid than necessary.
What about the memory barrier in fr_read_begin?
If I understand the Intel documentation correctly, then i386 doesn't need them:
"Writes by a single processor are observed in the same order by all processors"
I think "smp_read_barrier_depends()" (i.e. a nop for i386) is sufficient. Attached is a test app - could someone try it? I don't have access to a SMP system right now.
What about permitting arch overrides for the memory barriers? E.g. ia64 has acquire and release memory barriers - it doesn't map to the Linux wmb()/rmb() scheme.
--
Manfred
/*
* frlock: test for Intel memory ordering.
* Copyright (C) 1999,2003 by Manfred Spraul.
*
* Redistribution of this file is permitted under the terms of the GNU
* Public License (GPL)
* $Header: /pub/home/manfred/cvs-tree/movopt/frlock.cpp,v 1.2 2003/01/26 10:41:39 manfred Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
static volatile int g_val1;
static volatile int g_val2;
static volatile int g_seq1;
static volatile int g_seq2;
static volatile int start;
#define MB() __asm__ __volatile__ ("lock;addl $0,(%%esp)\n\t" \
:/* no output*/ \
:/* no input*/:"cc","memory")#define DELAY() do { int i; for(i=0;i<1000;i++); } while(0)
void* threadfnc(void* param)
{
while(!start);
if(1 == (int)param)
goto cpu1;
if(2 == (int)param)
goto cpu2;
assert(0);
cpu1:
{ // reader:
for(;;) {
int x1,x2,val1,val2;
x1 = g_seq1;
val1 = g_val1;
val2 = g_val2;
x2 = g_seq2;
if (x1 == x2) {
if (val1 != val2) {
printf("Bad! memory ordering violation with %d/%d: %d/%d.\n", x1, x2, val1, val2);
}
}
}
}
cpu2:
{ // writer:
int target = 0;
for (;;) {
// write 1:
target++;
g_seq1 = target;
g_val1 = target;
g_val2 = target;
g_seq2 = target;
DELAY();
// write 2:
target++;
g_seq1 = target;
g_val1 = target;
MB();
g_val2 = target;
g_seq2 = target;
DELAY();
// write 3:
target++;
g_seq1 = target;
g_val2 = target;
g_val1 = target;
g_seq2 = target;
DELAY();
// write 4:
target++;
g_seq1 = target;
g_val2 = target;
MB();
g_val1 = target;
g_seq2 = target;
DELAY();
// write 5:
target++;
g_seq1 = target;
g_val1 = target;
MB(); MB();
g_val2 = target;
g_seq2 = target;
DELAY();
// write 6:
target++;
g_seq1 = target;
g_val1 = target;
MB(); DELAY();
g_val2 = target;
g_seq2 = target;
DELAY();
// write 7:
target++;
g_seq1 = target;
g_val2 = target;
MB(); MB();
g_val1 = target;
g_seq2 = target;
DELAY();
// write 8:
target++;
g_seq1 = target;
g_val2 = target;
MB(); DELAY();
g_val1 = target;
g_seq2 = target;
DELAY();
}
}
}void start_thread(int id)
{
pthread_t thread;
int res;
res = pthread_create(&thread,NULL,threadfnc,(void*)id);
if(res != 0)
assert(false);
}int main()
{
printf("movopt:\n");
start_thread(1);
start_thread(2);
printf(" starting, please wait.\n");
fflush(stdout);
start = 1;
for(;;) sleep(1000);
}Quoted 7 lines · Manfred Spraul
Stephen wrote:
[snip - memory barrier for
fr_write_begin]Quoted 1 line
Using
mb()is more paranoid than necessary.
Quoted 7 lines
What about the memory barrier in
fr_read_begin?
If I understand the Intel documentation correctly, then i386 doesn't need them:
"Writes by a single processor are observed in the same order by all processors"I think "
smp_read_barrier_depends()" (i.e. a nop for i386) is sufficient.
I don't see what you mean, there is no dependency we can rely on between the read of the sequence number and the critical section reads, the critical section reads has nothing to do with the sequence number reads and the frlock itself.
Quoted 167 lines
Attached is a test app - could someone try it? I don't have access to a SMP system right now.
What about permitting arch overrides for the memory barriers? E.g. ia64 has acquire and release memory barriers - it doesn't map to the Linux wmb()/rmb() scheme.
--
Manfred/*
* frlock: test for Intel memory ordering.
* Copyright (C) 1999,2003 by Manfred Spraul.
*
* Redistribution of this file is permitted under the terms of the GNU
* Public License (GPL)
* $Header: /pub/home/manfred/cvs-tree/movopt/frlock.cpp,v 1.2 2003/01/26 10:41:39 manfred Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>static volatile int
g_val1;
static volatile intg_val2;
static volatile intg_seq1;
static volatile intg_seq2;static volatile int start;
#define MB() __asm__ __volatile__ ("lock;addl $0,(%%esp)\n\t" \ :/* no output*/ \ :/* no input*/:"cc","memory")#define
DELAY()do { int i; for(i=0;i<1000;i++); } while(0)void*
threadfnc(void* param){ while(!start); if(1 == (int)param) goto cpu1; if(2 == (int)param) goto cpu2; assert(0); cpu1: { // reader: for(;;) { int x1,x2,val1,val2; x1 = g_seq1; val1 = g_val1; val2 = g_val2; x2 = g_seq2; if (x1 == x2) { if (val1 != val2) { printf("Bad! memory ordering violation with %d/%d: %d/%d.\n", x1, x2, val1, val2); } } } } cpu2: { // writer: int target = 0; for (;;) { // write 1: target++; g_seq1 = target; g_val1 = target; g_val2 = target; g_seq2 = target; DELAY(); // write 2: target++; g_seq1 = target; g_val1 = target; MB(); g_val2 = target; g_seq2 = target; DELAY(); // write 3: target++; g_seq1 = target; g_val2 = target; g_val1 = target; g_seq2 = target; DELAY(); // write 4: target++; g_seq1 = target; g_val2 = target; MB(); g_val1 = target; g_seq2 = target; DELAY(); // write 5: target++; g_seq1 = target; g_val1 = target; MB(); MB(); g_val2 = target; g_seq2 = target; DELAY(); // write 6: target++; g_seq1 = target; g_val1 = target; MB(); DELAY(); g_val2 = target; g_seq2 = target; DELAY(); // write 7: target++; g_seq1 = target; g_val2 = target; MB(); MB(); g_val1 = target; g_seq2 = target; DELAY(); // write 8: target++; g_seq1 = target; g_val2 = target; MB(); DELAY(); g_val1 = target; g_seq2 = target; DELAY(); } } }void
start_thread(int id){ pthread_t thread; int res; res = pthread_create(&thread,NULL,threadfnc,(void*)id); if(res != 0) assert(false); }int
main()
Quoted 9 lines
{
printf("movopt:\n");
start_thread(1);
start_thread(2);
printf(" starting, please wait.\n");
fflush(stdout);
start = 1;
for(;;) sleep(1000);
}Andrea
Quoted 16 lines · Andrea Arcangeli
Quoted 13 lines · Manfred Spraul
Stephen wrote:
[snip - memory barrier for
fr_write_begin]Quoted 3 lines
Using
mb()is more paranoid than necessary.What about the memory barrier in
fr_read_begin?
If I understand the Intel documentation correctly, then i386 doesn't need them:
"Writes by a single processor are observed in the same order by all processors"
Quoted 9 lines
Quoted 3 lines
I think "
smp_read_barrier_depends()" (i.e. a nop for i386) is sufficient.I don't see what you mean, there is no dependency we can rely on between the read of the sequence number and the critical section reads, the critical section reads has nothing to do with the sequence number reads and the frlock itself.
You are right - "observed in the same order by all processors" only means that the memory interface of the cpus see all writes in order, not that instruction executed by the cpus will observe the writes in order.
That leaves ia64 with the acquire/release barriers.
--
Manfred
Quoted 3 lines
Attached is a test app - could someone try it? I don't have access to a SMP system right now.
Try to put [g_val1, g_seq1] and [g_val2, g_seq2] on two different cache lines and run it on a SMP system using CPUs with a partitioned cache architecture. Even if you do an WMB on writer side, you might see a different order w/out an RMB on the reader side. This because the two cache lines might be committed to different partitions with different loads, and the latest ( in time order ) commit might see a fastest path due a lower traffic. An RMB on the reader side ( that is usually expensive ) wait for all CPUs's memory controllers to flush their stuff before resuming execution.
- Davide
Quoted 2 lines · Manfred Spraul
If I understand the Intel documentation correctly, then i386 doesn't need them:
"Writes by a single processor are observed in the same order by all processors"
See the PPro errata. There are some constraints on this in the real world. You may need locked ops on the ppro and earlier cpus while being able to do it the fast way on PII and higher