Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2011-07

SMP > 2 core Cache issue with Posix threads

14 messages · 7 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Michael Schnell

In another topic (now closed) Andrew described that a code similar to HansPeter's example did run correctly on a dual core machine, but produced errors on a machine with more cores.

Now I understand that threaded FPC user programs are supposed to be done in a Posix compliant way and FPC/RTL/LCL provide Posix compatible MUTEX behavior e.g. with TCtriticalSection.

I understand that regarding threads and SMP, Posix (while not explicitly handling SMP) requires a user program to assume that unsynchronized code sequences in multiple threads can work truly in parallel (an indefinite number of cores needs to be assumed, priority does not enforce a sequential behavior).

Moreover regarding memory / cache synchronization Posix (not explicitly handling SMP and cache) requires the user program not to assume that any variable is set by another thread unless some synchronization ensures that the appropriate code sequences that sets it already has been executed..

OTOH if some synchronization ensures that the appropriate code sequences that sets it has been executed, IMHO, Posix compatibility of the infrastructure (compiler, libraries, OS, Hardware) ensures that another thread in fact gets that correct value of that variable (i.e. SMP Cache synchronization after the synchronization is granted).

Hans-Peter Diettrich wrote 10 lines
Consider the shareable bi-linked list, where insertion requires code like this: list.Lock; //prevent concurrent access ... //determine affected list elements new.prev := prev; //prev must be guaranteed to be valid new.next := next; prev.next := new; next.prev := new; list.Unlock;

in the other topic we found that
- my original "volatile" question is invalid, as the function calls done with list.lock and list.unlock are a "volatile barrier" preventing the compiler from caching some value inappropriately in a register, simply because they are function calls.
- the MUTEX operations done with list.lock and list.unlock (at least on PCs in Linux and Windows) use library calls that includes memory barriers
- if the potential cache incoherency would not be handled by Hardware / OS / Libraries on behalf of user land programs (not done with FPC), I feel that this would so disastrous and ubiquitous that it results in so many programs not working on SMP systems that it would be a really well known issue in the (C-) programmer community.

So, if Andrew's test case really fails, I think, there is some kind of bug _somewhere_ and I think it should be located to verify that it is not in the FPC compiler or the FPC RTL.

(It it fails only on Andrew's machine, my suggestion is a hardware problem.)

Who has a >2 Core machine to test and debug this ? -Michael

Reply by email

Mark Morgan Lloyd
to Michael Schnell
Michael Schnell wrote
In another topic (now closed) Andrew described that a code similar to HansPeter's example did run correctly on a dual core machine, but produced errors on a machine with more cores.

I've not been reading every message. Definitive URL?

Show quoted text 5 lines
OTOH if some synchronization ensures that the appropriate code sequences that sets it has been executed, IMHO, Posix compatibility of the infrastructure (compiler, libraries, OS, Hardware) ensures that another thread in fact gets that correct value of that variable (i.e. SMP Cache synchronization after the synchronization is granted).

So the really big question is whether any given computer has a robust SMP implementation, or if in actual fact it's some species of NUMA with unreliable cache coherency.

It might also be necessary to distinguish between SMP, multicore and hyperthreading.

(It it fails only on Andrew's machine, my suggestion is a hardware problem.) Who has a >2 Core machine to test and debug this ?

Biggest x86 system I've got here is a Compaq with 2x Xeons which I think are 2x multicore rather than HT. More interestingly I've got a 12-way SMP Sun, and they've been in this game for a while. Linux-only in both cases.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Michael Schnell
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 6 lines
Michael Schnell wrote: > In another topic (now closed) Andrew described that a code similar to > HansPeter's example did run correctly on a dual core machine, but > produced errors on a machine with more cores. I've not been reading every message. Definitive URL?

Topics:
[fpc-devel] volatile variables
and over in the lazarus mailing list:
[Lazarus] thread safe

Show quoted text 8 lines
So the really big question is whether any given computer has a robust SMP implementation, or if in actual fact it's some species of NUMA with unreliable cache coherency. It might also be necessary to distinguish between SMP, multicore and hyperthreading.

While this of course is correct, this is not something a user program should know about. It the user program adheres to the Posix specs, I think the infrastructure (compiler, libraries, OS, Hardware) is supposed to correctly handle the dirty details.

Show quoted text 6 lines
Biggest x86 system I've got here is a Compaq with 2x Xeons which I think are 2x multicore rather than HT. More interestingly I've got a 12-way SMP Sun, and they've been in this game for a while. Linux-only in both cases.

Sound like interesting test cases. Andrew himself did not provide the code that failed. Maybe some information from the other topics might be helpful.

Thanks,
-Michael

Reply by email

Vincent Snijders
to Michael Schnell

2011/7/1 Michael Schnell :

Show quoted text 14 lines
On 07/01/2011 11:26 AM, Mark Morgan Lloyd wrote: > > Michael Schnell wrote: >> >> In another topic (now closed) Andrew described that a code similar to >> HansPeter's example did run correctly on a dual core machine, but produced >> errors on a machine with more cores. > > I've not been reading every message. Definitive URL? Topics:   [fpc-devel] volatile variables and over in the lazarus mailing list:   [Lazarus] thread safe

I won't call that a definitive URL, but some vague indication. That is more than 200 'd expected you to give a link to the email in the archives where "Andrew described a code". So, which link to click on
http://lists.freepascal.org/lists/fpc-devel/2011-June/subject.html or which link to click on
http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-June/subject.html ?

Vincent

Reply by email

Michael Schnell
to Vincent Snijders
Vincent Snijders wrote 9 lines
I won't call that a definitive URL, but some vague indication. That is more than 200 'd expected you to give a link to the email in the archives where "Andrew described a code". So, which link to click on http://lists.freepascal.org/lists/fpc-devel/2011-June/subject.html or which link to click on http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-June/subject.html ?

I have no idea how to find a certain mail in the backlog. But it would not help anyway, as AFAIR, no additional information can be found there.
Maybe Andrew can jump in and give some hints. (I cc'ed him).

-Michael

Reply by email

Vincent Snijders
to Michael Schnell

2011/7/1 Michael Schnell :

Show quoted text 13 lines
On 07/01/2011 02:00 PM, Vincent Snijders wrote: > > I won't call that a definitive URL, but some vague indication. That is > more than 200 'd expected you to give a link to the > email in the archives where "Andrew described a code". > So, which link to click on > http://lists.freepascal.org/lists/fpc-devel/2011-June/subject.html > or which link to click on > > http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-June/subject.html > ? > I have no idea how to find a certain mail in the backlog.

So how you expect us to find the description *you* want us to read in all those mails, if even you cannot find it.

But it would not help anyway, as AFAIR, no additional information can be found there.

Oh, it was just another piece of useless information...

Vincent

Reply by email

Michael Schnell
to Vincent Snijders
Vincent Snijders wrote
So how you expect us to find the description *you* want us to read in all those mails, if even you cannot find it.

I can't find it in the backlog website. I did find it in my mailstore (no idea if this helps, though):

This is the message of Andrew's (06/28/2011 02:47 PM):

Show quoted text 18 lines
On Tue, Jun 28, 2011 at 7:40 AM, Michael Schnell wrote: > > All references I read today say that pthread_mutex (on which supposedly > > TCriticalSection is based) and the appropriate Windows stuff does contain an > > MB. But there might be issues with other OSes and Archs. > > Yes, any object that requires atomic features will employ a memory barrier. That is to say the MB is employed in order for the spincount to be accurate across all cores. > > If they would not do so, the complete plain old threaded application > > paradigm would be invalid, and tons of applications would need to be > > trashed. > > -Michael Probably right here. My engine had worked fine on a tripple core AMD. It wasn't until I upgraded to the 6 core system did I have to start looking into what was causing random problems with pointer assignments.

------------------------------------------------------------------------

This is the initial message of Andrew's (06/23/2011 02:02 PM):

Show quoted text 28 lines
Partially. Read barriers may not be necessary. It depends on the implementation. By having barriers placed at strategic locations in the code, you can make an implementation thread safe. Add, Delete, and Clear would be some. List iterations would be difficult if not unsafe outside a lock but if accessed under a manager thread you can schedule and operation and it will wait done until conditions are safe to perform. Getting an item from the list rather than locking the entire list and having an inUse boolean set to true for that item, would allow for any deletion to be blocked until another time (by a manager thread). With multi-core systems simply adding a mutex/lock is not enough. A manager is needed to be in place to safely provision, delegate, and use items. IMO, this forces efficiency in a system design. The basic logic for a manager thread is that the manager thread is accepting commands, and scheduled execution happens automatically. Further, you could create a command system for each list so when you add/remove an item from the list you are merely scheduling that item for deletion. I would avoid polling for large lists. The command should have a completion routine like Item.onComplete(Item) where the item is passed for use in the application. This way there would be absolutely no waiting for data in code and the system in general at rest until needed.

Here he goes on (06/27/2011 04:58 PM):

Show quoted text 23 lines
You're totally underestimating the need for a memory barrier : "Multithreaded programming and memory visibility See also: Memory model (computing) Multithreaded programs usually use synchronization primitives provided by a high-level programming environment, such as Java and .NET Framework, or an application programming interface (API) such as POSIX Threads or Windows API. Primitives such as mutexes and semaphores are provided to synchronize access to resources from parallel threads of execution. These primitives are usually implemented with the memory barriers required to provide the expected memory visibility semantics. In such environments explicit use of memory barriers is not generally necessary. " Focus on the "These primitives are usually implemented with the memory barriers required to provide the expected memory visibility semantics. " These primitives include TCrticialSection. It's not enough to ensure that someone from another thread is reading a it would safe to assume that CPU management systems can switch code execution on any core at any time.

(06/27/2011 08:03 AM):

Show quoted text 17 lines
On Mon, Jun 27, 2011 at 12:52 PM, Hans-Peter Diettrich wrote: > > You forget the strength of protection. A MB will disallow immediately any > > concurrent access to the memory area - no way around. Protection by other > > means only can work in*perfect* cooperation, a very weak model. Absolutely incorrect. entercricitalsection(); loop a:=b+c; end loop; leavecriticalsection(); thread 2 can read a and b and c at any state. If you want an accurate view of a,b,c you need to employ interlocked statements:-)

(06/28/2011 01:41 AM):

Show quoted text 32 lines
2011/6/27 Malcom Haak: > > Tell me then why any of what you have said is relevant. In fact in cases > > this this the use of CriticalSections would be sensible and would not cause > > 'tons of wait' as you have all your worker threads off doing things 99% of > > the time. Thread 1: a=b+c a2=a+c2 SignalEvent(E1) Thread 2: repeat WaitForEvent(E1,120); We can read anything now until terminated or complete This the prime example. On a 6 core system a looks like one value to one core than it does to another core. It's that simple. No getting around this issue. While spinlocks can block a entrance - it cannot guarantee memory order / code execution order. Therefore it is good practice to adopt interlocked assignments to guarantee memory is what it is. Core X computes a=b+c Core X+Y computes a2 This is relatively new theory which required low-level CPU code to perform such locks. This was never needed until the introduction of multi-core systems. Of which I did extensive tests on AMD via FPC/Lazarus.

(06/28/2011 04:41 AM):

Show quoted text 7 lines
On Tue, Jun 28, 2011 at 9:47 AM, Hans-Peter Diettrich wrote: > > I don't see anything like memory barriers here. Compare and swap mechanisms aren't quite like memory barriers but they to get the CPU to send a "fresh" copy of a variable to all cores' cache...

Reply by email

Mark Morgan Lloyd
to Vincent Snijders: 2011/7/1 Michael Schnell : So how you expect us to find the description *you* want us to…
Vincent Snijders wrote 21 lines
2011/7/1 Michael Schnell : > On 07/01/2011 02:00 PM, Vincent Snijders wrote: >> I won't call that a definitive URL, but some vague indication. That is >> more than 200 'd expected you to give a link to the >> email in the archives where "Andrew described a code". >> So, which link to click on >> http://lists.freepascal.org/lists/fpc-devel/2011-June/subject.html >> or which link to click on >> >> http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-June/subject.html >> ? >> > I have no idea how to find a certain mail in the backlog. So how you expect us to find the description *you* want us to read in all those mails, if even you cannot find it. > But it would not help anyway, as AFAIR, no additional information can be > found there. Oh, it was just another piece of useless information...

Fair play, he /has/ said he's mailed Andrew looking for source.

However under the circumstances we've got to have something definitive to inspect and test- I for one would be very unhappy if I spent my time running up a rarely-used system and updating its compiler, only to be told that I didn't have the right version of the test program.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Andrew Brunner
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote
Fair play, he /has/ said he's mailed Andrew looking for source.

I never received such email. But to Michael's defense, Google mail isn't the product it once was.

However under the circumstances we've got to have something definitive to inspect and test- I for one would be very unhappy if I spent my time running up a rarely-used system and updating its compiler, only to be told that I didn't have the right version of the test program.

I did all of my research on memory barriers a while ago. The best site that describes this "phenomena" is
http://en.wikipedia.org/wiki/Memory_barrier

There are others here that helped walk me through this a while ago, but to me, this is a non-issue. I intend to use interlocked statements for mission critical memory assignments.

I would suggest the poster do more questions than suggest there is a problem with either FPC, POSIX, or pthreads implementations, or kernel bugs, or OOP principals regarding inter-thread memory access to LCL objects. Anyone designing systems should already know how to employ techniques.

Reply by email

Hans-Peter Diettrich
to Vincent Snijders: 2011/7/1 Michael Schnell : I won't call that a definitive URL, but some vague indication.…
Show quoted text 5 lines
>> Michael Schnell wrote: >>> In another topic (now closed) Andrew described that a code similar to >>> HansPeter's example did run correctly on a dual core machine, but produced >>> errors on a machine with more cores. >> I've not been reading every message. Definitive URL?

I cannot provide an URL (from old Thunderbird), so let me cite the essential part of Andrew's message:

>

Simply protecting a loop with a futex is not sufficient. Assigning variables in a multi-threaded environment is not sufficient.

Interlocked/Exchange/ExchangeAdd/Increment features must be used whenever dealing with variables (with at least one exception) In my experience arrays of boolean are atomic.

I ran tests on a HexCore AMD system which had unexplained anomalies with pointers in an bidirectional linked list. I replaced all the pointer assignments with InterlockedExchange and the system worked flawlessly. Some of the assignments were taking place in a criticalsection (btw).
<<

This scenario was reflected in my example (bi-linked list update).

The very last sentence deserves clarification. When not *all* assignments are protected by a CS, the use of Interlocked assignments only can improve cache coherence, but it IMO *can not* prevent (logical) race conditions. IMO Andrew observed just such race conditions, resulting from a mix of sequential writes from multiple threads.

IMO a linked list can not work flawlessly, until at least a multiple-read-exclusive-write lock is used. While an exclusive-write lock can ensure list consistency, every unsynchronized reader will be fooled by concurrent changes to the list structure :-(

Consider what will happen when an (unsynchronized) reader determined an list node, before or after which a new node shall be inserted. When the write-synchronized update will happen, the list structure already can have changed, so that the referenced node can have changed its position in the list, or can have been moved into a very different list :-(

DoDi

Reply by email

Andrew Brunner
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote
IMO a linked list can not work flawlessly, until at least a multiple-read-exclusive-write lock is used. While an exclusive-write lock can ensure list consistency, every unsynchronized reader will be fooled by concurrent changes to the list structure :-(

I agree. Without a memory barrier, coupled with interlocked exchanges per node attachment, it would make memory synchronization impossible.

Show quoted text 5 lines
Consider what will happen when an (unsynchronized) reader determined an list node, before or after which a new node shall be inserted. When the write-synchronized update will happen, the list structure already can have changed, so that the referenced node can have changed its position in the list, or can have been moved into a very different list :-(

Impossible to comprehend, that's why we have barriers. But in one specific case, Interestingly enough - the problem I experienced was with the 1 owning thread accessing the list. There was no subsequent access via additional threads, but across cores on a single cpu.

Reply by email

José Mejuto
to Hans-Peter Diettrich: I cannot provide an URL (from old Thunderbird), so let me cite the essential part of…

Hello FPC,

Friday, July 1, 2011, 6:44:44 PM, you wrote:

HPD> This scenario was reflected in my example (bi-linked list update). HPD> The very last sentence deserves clarification. When not *all* HPD> assignments are protected by a CS, the use of Interlocked assignments HPD> only can improve cache coherence, but it IMO *can not* prevent (logical) HPD> race conditions. IMO Andrew observed just such race conditions, HPD> resulting from a mix of sequential writes from multiple threads.

When not all assignements/reads are not protected by a CS (or similar) there is not a problem of coherence, there is a logical problem. In that case the use of interlocked operations only add overhead without adding any kind of security/stability.

You can write lock free algorithms with interlocked operations, but you can not write any other kind of procedures, usually more complex, without a CS.

Tiny example:

Core/Thread 1:
--------------------
while true do begin
  Critical.Enter;
  a:=b;
  b:=b+1;
  if a=b then Raise Exception.Create('KBOOM!');
  Critical.Leave;
end;
Core/Thread 2:
-------------------
while true do begin
  InterlockedDecrement(b);
end;

This code will crash at a given time, maybe 1 millisecond, maybe 2 days, but it will crash.

Cache coherence is maintained by the hardware, interlocked only provide atomicity. How the atomicity is provided is beyond the scope of pascal and even the OS.

Best regards, José

Reply by email

Andrew Brunner
to José Mejuto
José Mejuto wrote
This code will crash at a given time, maybe 1 millisecond, maybe 2 days, but it will crash.

Yes. It will fail. Access to a,b are forbidden without acquiring a lock.

Cache coherence is maintained by the hardware, interlocked only provide atomicity. How the atomicity is provided is beyond the scope of pascal and even the OS.

IMO, this statement is pretty bold/broad. Meaning interlocked assignments as defined will ensure cache coherence as apposed to CrticialSections which are meant to minimally block *write* access if not read/write access.

I can tell you that a,b in my case were not so trivial. a,b happen to be reference pointers in a collection that without going into too much detail were certainly paged since the collection they resided in was large.

Reply by email