10-19-2010 07:14 PM
Does anyone know if the native CVI compiler always properly aligns doubles? I.e., if I declare a double will the memory location always be on an 8 byte boundary?
The Intel architecture docs say that if they are, reads and writes of double can be considered atomic.
Thanks.
10-20-2010 09:42 AM
menchar:
I'm not sure if this answers your question, but in CVI 9.0.1, it is possible to force a double off of an 8 byte boundary using some pointer manipulation. I haven't caught one off an 8 byte boundary as natively assigned.
The attached project assigns a bunch of different variable types, some doubles interspersed with other types, and checks the addresses and byte boundaries of the variables. Then it forces a double off of an 8 byte boundary by forcing its pointer into the middle of a string.
Why does it matter?
10-20-2010 10:17 AM
An atomic operation cannot be interrupted. This has advantages for example in those cases where multiple threads can access the same variable - you will never catch an atomic variable with one half of it set to a value by one thread and the other half set by a different one. There is a fuller description in Wikipedia.
JR
10-20-2010 01:00 PM
exactly jr.
I have a multi threaded application with a measurement thread that stores measurements as doubles in a common area where they can be read and used by other threads. If I know the doubles writes/reads are atomic, then there's no chance that a consumer thread will see a partial value and I don't have to go through the hassle of protecting the variable when it's accessed.
I didn't doubt that you could force a double off of 8 byte alignment if you wanted to, I was mostly curious if anyone could vouch for the compiler when doing simple double declarations.
Thanks to everyone.
Menchar
10-20-2010 03:02 PM
I've used this trick pretty much form day one with integers that match the word length of the system but have never been bold enough to try it on doubles as some compliers may used enhanced 10-byte doubles.
I suspect it should work with doubles on a 64-bit machine with 64-bit OS but I doubt any benefits over using proper critical section locks would be noticeable on a modern multi-core CPU.
Aligning structures across different compilers has always been an issue the #pragma pack(n) can be the solution if it actually works for your set of compilers. I usually go old school when designing cross-platform structures and manually add "spare" or "reserved" entries to make sure things align.
10-20-2010 03:26 PM
I spoke a couple years ago to one of the original Intel x86 architecture designers and it was always Intel's intent that aligned double ops be atomic.
Writing critical sections / mutexes is a hassle, I don't do it for integer ops and I won't for doubles either.
I don't have to worry about multiple platforms or compilers.
But I can see where if you were concerned about maximum portability you might want to use protection.
10-21-2010 05:27 AM - edited 10-21-2010 05:30 AM
Be warned that if the pack pragma is used to pack structures (as is quite often the case when creating code to split comms packets, or when saving space), doubles within a structure may not be on 8-byte boundaries. e.g. in:
#pragma pack(1)
struct {
char x;
double y;
} fred;
fred.y is unlikely to be on an 8-byte boundary.
Also, if you're not going to bother with critical sections or other locking mechanisms, variables shared between threads should be declared volatile to prevent compiler optimisation from caching the variable in a register.
10-21-2010 06:50 PM
Good points.
But, if the double is a simple top level variable (i.e. global), or a top level static variable, I wouldn't think the compiler could use a register. For multi-thread access, I'm trying to think of any other scenario where the double would be visible to more than one thread without being a top level variable for a writer-reader problem (one thread writing while another reading).
A block scope variable goes on the stack, and each thread gets its own stack so no problems there.
And with multi-core machines it can get trickier - you can have true concurrency, where as with single core or hyperthreaded machines you get only pseudo-concurrency.
10-22-2010 02:07 AM
The compiler can use registers to cache variables. Consider a simple program like:
double fred = 9999.0;
void myfunc(void)
{
for (fred=0.0; fred<100.0; fred+=0.5)
printf("%g\n", fred);
}
An optimising compiler might choose to cache the value of fred in a register whilst in myfunc(), so that the value of fred in memory only changes from the initial value of 9999.0 to 100.0 when myfunc() returns. A second thread would not see the value of fred change until myfunc exits. Declaring fred as volatile would cure this problem (but might result in a reduction in speed of execution, of course).
10-22-2010 03:47 PM
I might buy your argument if the top level identifier fred was static, but it's not.
The compiler can't know at the time it compiles that module, what other modules might be accessing fred. fred gets exported to the linker, and other modules can be bound to it.
The compiler can't know when compiling this module whether or not another module accessing fred is compiled with the same compiler, or if the same compiler, the same optimizations.
I don't believe any well designed compiler is going to optimize a non-static top level identifier into a register.
Some sources claim the volatile type qualifier isn't of any real use in multi-threading situations, that it's not portable.