C++ Standard (Draft n3126)
This series of notes is for C++ beginners to get more fundamental understanding of C++ by reading parts of C++ Standard (or draft), sometimes with small code experiment. C++ Standard is not a linear textbook. It often references concepts and definitions explained in later sections. And sometimes concepts or rules defined are simply reflection of underlying hardware design or constraint.
1.7 The C++ memory model
Some definitions we need to understand this section is summarised below:
Bit-fields (9.6)
Detail definition can be found in “9.6 Bit-fields”. It is always class member.
Arithmetic Types (3.9.1)
Integral and floating types.
Scalar Types (3.9)
Arithmetic types (3.9.1), enumeration types, pointer types, pointer to member types (3.9.2), std::nullptr_t, and cv-qualified versions of these types.
Key concept defined in this section is Memory Location, which is “either an object of scalar type or a maximal sequence of adjacent bit-fields all having non-zero width”, and principle of accessing separate memory locations, that is “two threads of execution (1.10) can update and access separate memory locations without interfering with each other. … It is not safe to concurrently update two bit-fields in the same struct if all fields between them are also bit-fields of non-zero width.”
It indicates that, in parallel programming, if an object is shared among threads, we don’t always need to synchronise access to it, provided that no two threads access/modify same memory location in it. However, if some contiguous memory locations are within a small enough memory area, heavily accessing them by threads will cause False Sharing.
Effectively, memory location reflects the fact that smallest access unit of memory is byte.
Below is a small test to demonstrate how memory location affects C++ compiler, using Visual Studio 2010.
void TestMemoryLocation()
{
struct BitField
{
int a;
int b:3;
int:0; // Comment out this line will have b and c in same memory location
int c:29;
};
BitField bit = {1, 3, 16};
bit.b = 5;
bit.c = 19;
}
int main(int argc, char* argv[])
{
TestMemoryLocation();
return 0;
}
// Assembly code, when zero bit-field is commented out
BitField bit = {1, 3, 16};
0010139E mov dword ptr [bit],1
001013A5 mov dword ptr [ebp-8],83h
bit.b = 5;
001013AC mov eax,dword ptr [ebp-8]
001013AF and eax,0FFFFFFF8h
001013B2 or eax,5
001013B5 mov dword ptr [ebp-8],eax // Write to b from address DWORD PTR [ebp-8]
bit.c = 19;
001013B8 mov eax,dword ptr [ebp-8]
001013BB and eax,7
001013BE or eax,98h
001013C3 mov dword ptr [ebp-8],eax // Write to c also from address DWORD PTR [ebp-8]
// Assembly code, when zero bit-field is declared
BitField bit = {1, 3, 16};
00FD13A8 mov dword ptr [ebp-14h],1
00FD13AF mov dword ptr [ebp-10h],3
00FD13B6 mov dword ptr [ebp-0Ch],10h
bit.b = 5;
00FD13BD mov eax,dword ptr [ebp-10h]
00FD13C0 and eax,0FFFFFFF8h
00FD13C3 or eax,5
00FD13C6 mov dword ptr [ebp-10h],eax // Write to b from address DWORD PTR [ebp-10h]
bit.c = 19;
00FD13C9 mov eax,dword ptr [ebp-0Ch]
00FD13CC and eax,0E0000000h
00FD13D1 or eax,13h
00FD13D4 mov dword ptr [ebp-0Ch],eax // Write to c from address DWORD PTR [ebp-0Ch]