Pages

Thursday, February 25, 2016

C++11 and C++14

The C+03 standard stayed among us for a long long time, perhaps a decade or so.
While we had plenty of time to get to know it reasonably well this led to stagnation.
But away from the eyes of the crowd, C++ continued continued its evolution.
It's true that there were some clues from good libraries, most notably Boost.
In fact, Boost is a very good library with significant influence on the C++ standards.
But Boost wasn't the standard itself and so was usually treated as experimental.
When the new C++11 standard finally saw the light of the day it was overwhelming.
The more reasonable generally available C++11 compilers appeared 2 to 4 years later.
Anyway, the multitude of new stuff of C++11 standard is great and much welcome.
But I risk to say it's almost impossible to absorb it in less than a year or so.
What seems to be true is that a wealth of previous limitations are gone.
The C++14, as far as I know, is a "slight" refinement of C++11.

The ISO C++ 11 standard is officially known as ISO International Standard ISO/IEC 14882:2011(E) and the ISO C++ 14 standard is officially known as ISO International Standard ISO/IEC 14882:2014(E).

Instead of mastering every corner of the new standards, it seems better to concentrate just on the enabling new features particular to a certain objective at hand, while being aware that later on one may surprisingly come to discover some built-in ready-made industrial-strength feature that is highly portable and efficient and does exactly what you're trying to do as well as or better than your individual efforts.

So, it will take a lot of time to tackle the C++11 and C++14 standards.
Hopefully much less than it took to know C++03.
Let's see, C++17 seems to be under way!
   

Alignment - second thoughts

In fact this post is sort of an extension from the previous Alignment - first thoughts.
In general, sizeof( T ) is not the optimal alignment modulus for type T.
It's coarse even for some basic types and specially for user-defined types.
One can verify this even on a Windows XP and DJGPP C++11:
  
 sizeof( long double ) = 12
alignof( long double ) =  4


 sizeof( aggregate< double > ) = 8
alignof( aggregate< double > ) = 4


with

template< typename T >
struct aggregate
{
   T element;
};


The larger in size T gets, the coarser is the alignment based on its full size.
For aggregate types larger than the most stringent type the waste can be well over 100%.
For example:
 
struct T
{
   char        member_1[ 5 ];
   long double member_2;
   char        member_3[ 2 ];
   void *      member_4;
};


           TYPE        OFFSET  SIZE  ALIGN
member_1 : char [ 5 ]       0     5      1
member_2 : long double      8    12      4
member_3 : char [ 2 ]      20     2      1
member_4 : void *          24     4      4

                                 28      4

28 is a valid, although sub-optimal, alignment modulus for type T.
align< T * >( (void *) 1 ) yields 28, thus wasting 27 bytes!
This problem only gets worse and worse as the size of T gets bigger and bigger.
Had we known that the optimal alignment modulus is 4 we would waste just 3 bytes!
Had we known the size of the most stringent type that fits inside T we'd waste 11 bytes.

As suggested above, there are 2 solutions for this problem.
One of them is optimal and the other although sub-optimal is bounded.
They are respectively:

  • The advent of C++11 alignof() which is a portable and standard way of querying the optimal alignment modulus for a given type. Up to C++03, as far as I know, the only way out is by using some non-standard non-portable compiler extension, such as the GNU's __align__(). The importance of this case is for obtaining the optimal alignment for a specific data type. A specialized memory allocator would benefit from this.
     
  • The advent of C++11 alignof( std::max_align_t ) or the even coarser but still bounded sizeof( std::max_align_t ). As far as I know, there's no GNU extension supporting this. The importance of this case is for obtaining the most general alignment suitable to any data type. A generalized memory allocator would benefit from this. 

Friday, February 19, 2016

The smallest 2n-multiple ≥ B

When dealing with quantities that are powers of 2, the binary (base-2) arithmetic as well as the typical binary representation of integer values can allow interesting alternatives to the usual base-10 arithmetic we are commonly used to.
 
The obvious prerequisites are binary arithmetic and logical operations.
The relevant formula is:
[ B +  ( A - 1 ) ] & [ ~( A - 1 ) ], where A = 2n for some n ≥ 0.
The above formula will compute the smallest multiple of A greater than or equal to B.
B is a positive integer value which, if large enough, could represent a pointer.
In this process it's imperative that A be a (positive) power of 2.

One particular application is adjusting C++ pointers to a certain alignment modulus.
In other words, this technique is very important to C++ memory management.
B would be the value of a pointer to be aligned to an A-multiple boundary.
If an alignment modulus is 4, then A = 4 and thus n = 2.
You know: alignment is vital.

Of course I shall prove the formula with a minimal (but enough) formalism.
But first let's get to know better how it works.

The trivial case is when n = 0 ( A = 1 ), which just yields B, no big deal.
In this case we are just aligning to a single byte boundary as A = 1.
So no adjustment to B is required and hence B itself is fine.

For a 4-bytes (32-bits) boundary alignment we would use n = 2.
That means we are interested in multiples of 4, that is, A = 4 and hence n = 2.

To get the whole idea of the formula, let's just work with n = 3, that is, A = 8.
Let's assume, for the sake of simplicity, a single-byte magnitude for the B value.
In a memory alignment scenario, B would be the value of a multi-byte pointer.
As a power of 2, the binary representation of A is a 1 followed by n zeros.
As a direct consequence ( A - 1 ) is always a sequence of n ones.
Hence ~( A - 1 ) is a bit-mask to clear out the first n least significant bits.

Look:
If n = 3,
then A = 8 = 10002 and ( A - 1 ) = 1112 and ~( A - 1 ) = 111110002.
And so what?

Alignment - first thoughts

Memory alignment is an important and unavoidable low-level fact.
The trade-off is space overhead due to padding which is to be minimized.
Managing alignment presents challenges, specially before C++11.
As you know, C++11 is a major advance since C++03.

A few important things to keep in mind are:
 
  • In main memory, every data type must (as an absolute requirement) or should (at least for optimization) be located at a characteristic address multiple, aka the alignment modulus due to hardware design and engineering issues. In fact, it's not uncommon, with exceptions, to have the arithmetic data types' alignments and the void * type alignment dictating all the other dependent data types' alignments.
      
  • Type T is more restrictive (or stringent) then type S if the alignment modulus for type T is greater than (or equal --- a technicality for the reflexive property) to the alignment modulus for type S.
      
  • In general, if type T is more restrictive than type S, then converting T * to S * and back won't cause addressing misalignment or exception in detriment of a program or application.
      
  • By means of an appropriate C or C++ typecast, a void * can be converted into a valid T * whatever be the type of T. Similarly, a const void * can be safely be assigned to a const T *. Hence, void * is more restrictive than T * for any type T (including function pointers void (*)() ). These facts are quite powerful because (low-level) allocators generally return a void * which in turn they obtain from a platform low-level system-call including ::shmat() and malloc(3C) variants such as: mtalloc(3ALLOC), umem_alloc(3ALLOC), and bsdmalloc(3MALLOC), not excluding their possible and respective implementation of memalign(). Either the allocator will return an address suitable to the most restrictive type of the compiler will adjust it according to the typecast.
     
  •  An aggregate data type alignment modulus is frequently a multiple of the alignment modulus of the more stringent of its members, which ultimately is the alignment modulus of the more stringent primitive data type involved. Aside from hardware constrains, the basic fact behind this is the language requirements to well-formed arrays.
 
Naturally, I understand that platform and/or compiler specifics pragmas, such as pragma align and pragma pack, as well as unions including the most restrictive type are very useful tools in assuring a certain alignment constraint, but they consist on a different approach to what I'm talking about here, which (although not space efficient) is more portable in all aspects. My goal is to obtain a properly aligned value of a type-specific pointer (T *) within an arbitrary raw chunk of memory (a raw block of memory pages), which, after all, is typical in the context of memory allocators.
  
Thus, the trick is how to obtain proper alignment and avoid any portability problems.
I'd say the answer is based on the following equivalent rules:
  1. Rely on the standards and be aware of industry-wide facts.
  2. Assume nothing too specific about a platform and its system and compilers.
I always kept in mind these rules, but in practice I never really found a sufficiently clear example of how to adhere to them. Perhaps it is a matter of highly confidential treasures of a few. At the same time, the scenarios were apparently so ugly that I was discouraged to try to tackle anything by myself. In addition, third parties solutions claiming to address the issue were so clumsy that I became uninterested on embracing any of them. I just wanted some really simple approach that could get the job done on most straightforward scenarios.

Thursday, February 18, 2016

Kicking off

This web log is to help myself and perhaps contribute to demystify some C++ topics.
The key to success is to balance performance, simplicity and elegance.

I've been trying a few other posts on C++ mixed up with other subjects on another blog of mine, but I intent to gradually revise and migrate all of them to this new blog I'm just kicking off dedicated to C++.

C++ is really great but I'm still skeptical about many corners of it, specially its libraries. My opinion is that enabling industrial-strength technologies is "more interesting" than attempting to provide ready-made solutions for every imaginable thing or fashion.