digitalmars.D - Possible bug in atomicOp
- Benjamin Thaut (44/44) Oct 23 2010 The following testcase (when executed on dual core at least) results in
- dsimcha (6/44) Oct 23 2010 http://d.puremagic.com/issues/show_bug.cgi?id=4782
- Benjamin Thaut (7/51) Oct 23 2010 So shouldn't there be a static assert to prevent one from using atomicOp...
- Sean Kelly (2/59) Oct 23 2010 The former in the short term and the latter in the long term.
- Robert Jacques (9/68) Oct 24 2010 Well, here's the assembler to load a value onto the FP stack:
- Don (4/28) Oct 24 2010 That should be:
- Don (9/39) Oct 25 2010 Wait a minute. x86 has no read-modify-write instructions for x87, or for...
- Sean Kelly (2/11) Oct 25 2010 atomicOp does most of its RMW operations using a CAS loop, so I think it...
- Fawzi Mohamed (10/27) Oct 26 2010 I use atomic op casting pointers as integer pointers ( http://github.com...
- Sean Kelly (2/14) Oct 26 2010 ref: http://dsource.org/projects/druntime/browser/trunk/src/core/atomic....
- Robert Jacques (4/27) Oct 25 2010 Opps. I should have named them __int_as_float and __long_as_double. The ...
The following testcase (when executed on dual core at least) results in a endless loop inside atomicOp. import std.stdio; import std.concurrency; enum Messages { GO, END } shared class Account { private double amount = 0; double getAmount() const { return amount; } void change(double change){ atomicOp!"+="(amount,change); } } shared Account bank = null; void otherThread(Tid father){ send(father,Messages.GO); for(int i=0;i<1000;i++) bank.change(-100); send(father,Messages.END); } void main(string[] args) { bank = new Account(); spawn(&otherThread,thisTid); receiveOnly!(Messages)(); for(int i=0;i<1000;i++) bank.change(+100); receiveOnly!(Messages)(); writefln("Program finished. Amount is %s",bank.getAmount()); } Is this a bug, or am I doing something wrong here? If it is a bug it is kind of critical because people which are reading the "the D programming language" book won't be happy to find out that some of the given examples do not work yet. -- Kind Regards Benjamin Thaut
Oct 23 2010
== Quote from Benjamin Thaut (code benjamin-thaut.de)'s articleThe following testcase (when executed on dual core at least) results in a endless loop inside atomicOp. import std.stdio; import std.concurrency; enum Messages { GO, END } shared class Account { private double amount = 0; double getAmount() const { return amount; } void change(double change){ atomicOp!"+="(amount,change); } } shared Account bank = null; void otherThread(Tid father){ send(father,Messages.GO); for(int i=0;i<1000;i++) bank.change(-100); send(father,Messages.END); } void main(string[] args) { bank = new Account(); spawn(&otherThread,thisTid); receiveOnly!(Messages)(); for(int i=0;i<1000;i++) bank.change(+100); receiveOnly!(Messages)(); writefln("Program finished. Amount is %s",bank.getAmount()); } Is this a bug, or am I doing something wrong here? If it is a bug it is kind of critical because people which are reading the "the D programming language" book won't be happy to find out that some of the given examples do not work yet.http://d.puremagic.com/issues/show_bug.cgi?id=4782 Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.
Oct 23 2010
Am 23.10.2010 14:52, schrieb dsimcha:== Quote from Benjamin Thaut (code benjamin-thaut.de)'s articleSo shouldn't there be a static assert to prevent one from using atomicOp with floats and doubles? Or should atomicLoad be implemented to support floats and doubles? -- Kind Regards Benjamin ThautThe following testcase (when executed on dual core at least) results in a endless loop inside atomicOp. import std.stdio; import std.concurrency; enum Messages { GO, END } shared class Account { private double amount = 0; double getAmount() const { return amount; } void change(double change){ atomicOp!"+="(amount,change); } } shared Account bank = null; void otherThread(Tid father){ send(father,Messages.GO); for(int i=0;i<1000;i++) bank.change(-100); send(father,Messages.END); } void main(string[] args) { bank = new Account(); spawn(&otherThread,thisTid); receiveOnly!(Messages)(); for(int i=0;i<1000;i++) bank.change(+100); receiveOnly!(Messages)(); writefln("Program finished. Amount is %s",bank.getAmount()); } Is this a bug, or am I doing something wrong here? If it is a bug it is kind of critical because people which are reading the "the D programming language" book won't be happy to find out that some of the given examples do not work yet.http://d.puremagic.com/issues/show_bug.cgi?id=4782 Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.
Oct 23 2010
Benjamin Thaut <code benjamin-thaut.de> wrote:Am 23.10.2010 14:52, schrieb dsimcha:The former in the short term and the latter in the long term.== Quote from Benjamin Thaut (code benjamin-thaut.de)'s articleSo shouldn't there be a static assert to prevent one from using atomicOp with floats and doubles? Or should atomicLoad be implemented to support floats and doubles?The following testcase (when executed on dual core at least) results in a endless loop inside atomicOp. import std.stdio; import std.concurrency; enum Messages { GO, END } shared class Account { private double amount = 0; double getAmount() const { return amount; } void change(double change){ atomicOp!"+="(amount,change); } } shared Account bank = null; void otherThread(Tid father){ send(father,Messages.GO); for(int i=0;i<1000;i++) bank.change(-100); send(father,Messages.END); } void main(string[] args) { bank = new Account(); spawn(&otherThread,thisTid); receiveOnly!(Messages)(); for(int i=0;i<1000;i++) bank.change(+100); receiveOnly!(Messages)(); writefln("Program finished. Amount is %s",bank.getAmount()); } Is this a bug, or am I doing something wrong here? If it is a bug it is kind of critical because people which are reading the "the D programming language" book won't be happy to find out that some of the given examples do not work yet.http://d.puremagic.com/issues/show_bug.cgi?id=4782 Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.
Oct 23 2010
On Sat, 23 Oct 2010 15:50:30 -0400, Sean Kelly <sean invisibleduck.org> wrote:Benjamin Thaut <code benjamin-thaut.de> wrote:Well, here's the assembler to load a value onto the FP stack: float __int2float (ref int x) { asm { fld float ptr [EAX]; } } double __long2double(ref long x) { asm { fld double ptr [EAX]; } } Unfortunately, direct loading from a register doesn't seem to be supported. So writing wrapper code which uses a union would be almost as fast. I know there is an SSE instruction to load directly from registers, but we can't assume SSE support.Am 23.10.2010 14:52, schrieb dsimcha:The former in the short term and the latter in the long term.== Quote from Benjamin Thaut (code benjamin-thaut.de)'s articleSo shouldn't there be a static assert to prevent one from using atomicOp with floats and doubles? Or should atomicLoad be implemented to support floats and doubles?The following testcase (when executed on dual core at least) results in a endless loop inside atomicOp. import std.stdio; import std.concurrency; enum Messages { GO, END } shared class Account { private double amount = 0; double getAmount() const { return amount; } void change(double change){ atomicOp!"+="(amount,change); } } shared Account bank = null; void otherThread(Tid father){ send(father,Messages.GO); for(int i=0;i<1000;i++) bank.change(-100); send(father,Messages.END); } void main(string[] args) { bank = new Account(); spawn(&otherThread,thisTid); receiveOnly!(Messages)(); for(int i=0;i<1000;i++) bank.change(+100); receiveOnly!(Messages)(); writefln("Program finished. Amount is %s",bank.getAmount()); } Is this a bug, or am I doing something wrong here? If it is a bug it is kind of critical because people which are reading the "the D programming language" book won't be happy to find out that some of the given examples do not work yet.http://d.puremagic.com/issues/show_bug.cgi?id=4782 Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.
Oct 24 2010
Robert Jacques wrote:On Sat, 23 Oct 2010 15:50:30 -0400, Sean Kelly <sean invisibleduck.org> wrote:That should be: fild dword ptr [EAX]; fild qword ptr [EAX];Well, here's the assembler to load a value onto the FP stack: float __int2float (ref int x) { asm { fld float ptr [EAX]; } } double __long2double(ref long x) { asm { fld double ptr [EAX]; } }The former in the short term and the latter in the long term.Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.So shouldn't there be a static assert to prevent one from using atomicOp with floats and doubles? Or should atomicLoad be implemented to support floats and doubles?Unfortunately, direct loading from a register doesn't seem to be supported. So writing wrapper code which uses a union would be almost as fast. I know there is an SSE instruction to load directly from registers, but we can't assume SSE support.
Oct 24 2010
Don wrote:Robert Jacques wrote:Wait a minute. x86 has no read-modify-write instructions for x87, or for SSE. So I don't think it's possible to implement atomic floating-point ops, apart from assignment. (Of course, you can fake it with a mass of casts and a CAS, but that doesn't seem helpful). It should just be prevented, except possibly for assignment. (Is an 80-bit float assignment atomic? Maybe not, since it would need special logic).On Sat, 23 Oct 2010 15:50:30 -0400, Sean Kelly <sean invisibleduck.org> wrote:That should be: fild dword ptr [EAX]; fild qword ptr [EAX];Well, here's the assembler to load a value onto the FP stack: float __int2float (ref int x) { asm { fld float ptr [EAX]; } } double __long2double(ref long x) { asm { fld double ptr [EAX]; } }The former in the short term and the latter in the long term.Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.So shouldn't there be a static assert to prevent one from using atomicOp with floats and doubles? Or should atomicLoad be implemented to support floats and doubles?Unfortunately, direct loading from a register doesn't seem to be supported. So writing wrapper code which uses a union would be almost as fast. I know there is an SSE instruction to load directly from registers, but we can't assume SSE support.
Oct 25 2010
Don Wrote:Wait a minute. x86 has no read-modify-write instructions for x87, or for SSE. So I don't think it's possible to implement atomic floating-point ops, apart from assignment. (Of course, you can fake it with a mass of casts and a CAS, but that doesn't seem helpful). It should just be prevented, except possibly for assignment. (Is an 80-bit float assignment atomic? Maybe not, since it would need special logic).atomicOp does most of its RMW operations using a CAS loop, so I think it should work. The redo occurs when the memory location being written to changed since it was read, and that shouldn't be any different for floating point values vs. integers.
Oct 25 2010
On 26-ott-10, at 05:13, Sean Kelly wrote:Don Wrote:I use atomic op casting pointers as integer pointers ( http://github.com/fawzi/blip/blob/master/blip/sync/Atomic.d which is also tango one), and I haven't encountered any problem yet, but I haven't checked in detail if some x87 or SSE load/store might potentially give problems, but as sean says they should not (as the value should be transferred from register to register, not going through the memory, and memory accesses are controlled by CAS, and memory barriers (if used to separate subsequent "normal" ops) should be valid also for x87/SEE. FawziWait a minute. x86 has no read-modify-write instructions for x87, or for SSE. So I don't think it's possible to implement atomic floating- point ops, apart from assignment. (Of course, you can fake it with a mass of casts and a CAS, but that doesn't seem helpful). It should just be prevented, except possibly for assignment. (Is an 80-bit float assignment atomic? Maybe not, since it would need special logic).atomicOp does most of its RMW operations using a CAS loop, so I think it should work. The redo occurs when the memory location being written to changed since it was read, and that shouldn't be any different for floating point values vs. integers.
Oct 26 2010
Sean Kelly Wrote:Don Wrote:ref: http://dsource.org/projects/druntime/browser/trunk/src/core/atomic.d#L127Wait a minute. x86 has no read-modify-write instructions for x87, or for SSE. So I don't think it's possible to implement atomic floating-point ops, apart from assignment. (Of course, you can fake it with a mass of casts and a CAS, but that doesn't seem helpful). It should just be prevented, except possibly for assignment. (Is an 80-bit float assignment atomic? Maybe not, since it would need special logic).atomicOp does most of its RMW operations using a CAS loop, so I think it should work. The redo occurs when the memory location being written to changed since it was read, and that shouldn't be any different for floating point values vs. integers.
Oct 26 2010
On Mon, 25 Oct 2010 00:08:03 -0400, Don <nospam nospam.com> wrote:Robert Jacques wrote:Opps. I should have named them __int_as_float and __long_as_double. The point was to find an efficient way to return an int or double in register on the x87 stack.On Sat, 23 Oct 2010 15:50:30 -0400, Sean Kelly <sean invisibleduck.org> wrote:That should be: fild dword ptr [EAX]; fild qword ptr [EAX];Well, here's the assembler to load a value onto the FP stack: float __int2float (ref int x) { asm { fld float ptr [EAX]; } } double __long2double(ref long x) { asm { fld double ptr [EAX]; } }The former in the short term and the latter in the long term.Basically, atomicLoad (which atomicOp uses) always returns in ALU registers. Floating point numbers need to be returned in floating point registers. Therefore, a NaN always gets returned from atomicLoad!double, and a NaN isn't equal to anything.So shouldn't there be a static assert to prevent one from using atomicOp with floats and doubles? Or should atomicLoad be implemented to support floats and doubles?
Oct 25 2010