Wednesday, January 6, 2016

Achieving the ultimate date continued, good interpretations

Hello google wanderer, let me disappoint you because once more this article, although it has to do with speed dates, won't help you any to find your soul mate, and especially not how to understand her/him...

Ah, BOOST preprocessing, as explained before, will be postponed once more.

Another inexpressible thing


It turns out there is no way to tell a C++ compiler that it can see the same bits as two different types of values. reinterpret_cast and C-style casts that reinterpret the value are simply not allowed in constexprs. But we have unions, right? For example, I thought for this code
union bits64 {
    long integer;
    double floating;

    constexpr bits64(double init) noexcept: floating(init) {}
    constexpr bits64(long init) noexcept: integer(init) {}
};
that integer and floating could be used indistinctly; furthermore, for ages my code has done exactly that to significant gains of clarity and performance. However, what that code really means is you can set either and read the same; if you last set one and read the other, that’s undefined behavior. IN YOUR FACE!. In particular, for constexprs, where the compiler can track which is the last member set, the so-called active member, it blows into an error. I am discovering that unions are discriminated. If you need persuasion that this is the case, check this code:
union bits64 {
    long integer;
    double floating;

    constexpr bits64(double init): floating(init) {}
    constexpr bits64(long init): integer(init) {}

    constexpr operator long() const { return integer; }
};

constexpr bits64 fromLong{long(0)};

static_assert(0 == long(fromLong), "");

constexpr bits64 fromDouble = 0.0;
static_assert(0 == long(fromDouble), "");
There will be a compilation error, accessing fromDouble.integer when the compiler is sure that the last union member set (the active member) is other than the one being accessed.

I don’t know what is there to gain by such definition of unions. I mean, there are things that come to mind, we could put an addendum to tools such as valgrind to keep track of the last union member set and track its usage.  That’s all good, but it does not require a language feature to force it; however, what about the legitimate use of indiscriminated unions?

There is a good example for wanting to use an alternative interpretation for the same bits: A date type such as the one described above as trivial conversions to and from integer, if the user does not care about endianness:
union date_record {
    struct {
        uint8_t char day, month;
        int16_t year;
    };
    int32_t integer;

    constexpr bool operator<(date_record other) const noexcept {
        return integer < other.integer;
    }
};

bool compare(date_record dr) {
    return dr.day + dr.month * 256 + dr.year == dr.as_integer;
}
compare will always be true in little endian arithmetic (such as AMD64). This fact can be used to simplify many operations. I implemented an integer comparison which under the assumption of little endianness is unbeatable in terms of economy of programming effort, clarity, and runtime performance; precisely the kind of things a good language should encourage.

Using an alternative interpretation for the same bits through the union feature, is very well defined "undefined behavior" in all compilers I’ve ever used.

Let us see another example, a date increment by one month operation (this implementation is not complete because adding one month to a January 30 needs to change the day component too, however, for simplicity of exposition):
union date_record {
    struct {
        uint8_t char day, month;
        int16_t year;
    };
    int32_t integer;

    constexpr bool operator<(date_record other) const noexcept {
        return integer < other.integer;
    }
};

date_record incrementMonthAggregate(date_record dr) {
    if(dr.month < 12) { ++dr.month; }
    else { dr.month = 0; ++dr.year; }
    return dr;
}

date_record incrementMonthInteger(date_record dr) {
    date_record rv;
    rv.integer =
        dr.month < 12 ?
        dr.integer + 256 :
        (dr.integer & 0xFFFF00FF) + 0x10000;
    return rv;
}
Notice both functions do essentially the same work, so, programming one, even for constexprs would make the other superflous except for the apparently arbitrary rule that unions are discriminated. But since that rule is in the language, gcc honors it and I don’t know how to turn it off, then if full compile time reasoning is desired, there is no recourse but to implement all the functions as many times as there are alternative interpretations. I thought that was the end of it, and I had my article nicely tied up.

However, being thorough, I checked the assember. It turns out they are sufficiently different. In gcc, 20 assembler lines versus 8, in clang, 18 versus 12 --notice clang’s assembler for the integer operation, despite being slightly longer, is superior to gcc’s because it does not have a conditional jump–. It took me a while to figure out what’s the difference (GCC case):
incrementMonthAggregate(date_record):
        movl    %edi, %edx ;; edx <- dr
        movl    %edi, %esi ;; esi <- dr
        movzbl  %dh, %ecx  ;; ecx <- dr.month
        sarl    $16, %esi  ;; esi = dr.year
        cmpb    $11, %cl   ;; if(dr.month <= 11)
        movl    %esi, %eax ;;
        jbe     .L5        ;; { eax <- dr.year; goto .L5 }
        leal    1(%rsi), %eax ;; eax <- 1 + dr.year
        xorl    %ecx, %ecx ;; ecx <- 0
        movb    %cl, %dh   ;; dr.month = 0
        sall    $16, %eax  ;; eax <- (1 + dr.year) << 16
        movzwl  %dx, %edx  ;; edx <- dr.month * 256 + dr.day
        orl     %edx, %eax ;; eax <- dr
        ret
.L5:
        addl    $1, %ecx   ;; ecx <- 1 + dr.month
        sall    $16, %eax  ;; eax <- dr.year << 16
        movb    %cl, %dh
        movzwl  %dx, %edx  ;; edx <- dr.month * 256 + dr.day
        orl     %edx, %eax ;; eax <- dr
        ret
incrementMonthInteger(date_record):
        movq    %rdi, %rax
        cmpb    $11, %ah
        jbe     .L10
        andl    $-65281, %edi
        leal    65536(%rdi), %eax
        ret
.L10:
        leal    256(%rdi), %eax
        ret
Contrary to my expectations, quite simply all three compilers (intel’s, gcc, clang) fail to de-aggregate the day, month, year representation into a consolidated integer, so, they do the superfluous work of splitting the value into components and resynthetizing it.

Now that we have been informed that there actually is a runtime performance difference, we have to make sure we use the best interpretaton for each operation. I wish the compiler would let me use the interpretation I see as easiest to implement an operation and then give me the equivalent for the best interpretation, because I am never in the mood to do the compiler’s job while programming, but can’t anymore, there is performance to be gained.

In summary, discriminated unions mean that in practice the compile time functions have to respect which is the active member, leading to programming several versions of exactly the same operations, and more complications to indicate to the end programmer which ones to use for the runtime. IMO, this is not acceptable.

Tuesday, January 5, 2016

Achieving the ultimate date

If you stumbled upon this article from a search engine, sorry to disappoint, but this is not about dating, nor how to find your soul mate. It is about the puerile pursuit of the ultimate implementation for computer program values that hold dates.

The intellectual fertility of implementing the ultimate Date class


In the article lists of function call arguments we introduced an idiom to configure a class.

Part of the motivation for the technique is that lists of function call arguments are not expressible directly into C++, especially the very useful case of partial lists of parameters; thus, we wanted to explore for the special case of boolean parameters whether it was feasible to pack them into a C++ type. Fortunately, we saw that it was possible, without a loss of performance, and potentially a performance gain. As a matter of fact, now I recommend not using boolean parameters at all, but to group them into their own set of flags. To further make the idiom practical, I introduced supporting BOOST preprocessing macros to take care of the boilerplate.

In my outgoing remarks I mentioned in passing that the technique could be generalized to arbitrary lists of function call arguments, provided their types respected value semantics.
 
This is one aspect that has kept me busy researching. The technique works, however, implementing it, and its supporting BOOST preprocessing macros, I encountered obstacles of different natures:
  1. The compilers for reasons not known to me translate essentially the same code in different ways
  2. The language has a few rules that conspire against highly performing and easy to understand code
  3. I reached hard limitations of BOOST preprocessing
  4. Some compilers may fail to perform optimizations that GCC can.
I thought a class for dates was simple enough for illustration purposes and non-trivial enough that the idioms would be applicable.

Offline a friend pointed out the deficiency of the idiom presented of boolean flags of not being able to force a complete set of flags to initialize a class. That is, the expression of complete sets of flags, in the example we have been using, to construct a Font only if the FontOptions has all its flags set. This problem has a solution that I suspect is original.

Generalizing the conversion of boolean parameters into a consolidated aggregate type for any value-semantic argument list turned into a through and through research project, because the straightforward solution, which is expressible, still leads to very interesting aspects. In that regard, it seems the conceptualization I arrived at is at the leading edge of the capabilities of the language, compilers and supporting techniques. This makes for very interesting writing material.

In this article, I’ve decided to simply introduce the aspects, to be explained in depth in successive articles. Yet again, I am forced to postpone explaining BOOST preprocessing, actually, I am afraid this time I had to use BOOST preprocessing techniques I don’t understand too well, and even beyond that, there are techniques that I don’t look forward to explaining because they are too weird, but I'll try.

 

Complete flag sets


Let us start with recapitulating the solution for the sets of options to create a font:

#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/cat.hpp>

#define PP_SEQ_I_DECLARE_ENUMERATION_VALUE(r, dummy, i, e) e = (1 << i),
#define PP_SEQ_I_FLAG_SET_MEMBER_FUNCTIONS(r, name, i, e)\
    constexpr name e() const noexcept { return value | internal::e; }\
    constexpr name BOOST_PP_CAT(not_, e)() const noexcept { return value & ~internal::e; }\
    constexpr bool BOOST_PP_CAT(is_, e)() const noexcept { return value & internal::e; }

#define BOOLEAN_FLAG_SET(name, flags)\
    class name {\
        struct internal { enum: unsigned char {\
            EMPTY = 0,\
            BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_DECLARE_ENUMERATION_VALUE, ~, flags)\
        }; };\
        unsigned char value = 0;\
        constexpr name(unsigned v) noexcept: value(v) {}\
    public:\
        name() = default;\
        name(const name &) = default;\
        BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_FLAG_SET_MEMBER_FUNCTIONS, name, flags)\
        constexpr bool operator==(name other) const noexcept { return value == other.value; }\
        constexpr bool operator!=(name other) const noexcept { return !(*this == other); }\
    }

We can have a class Font that requires a FontOptions to be constructed, like this:

BOOLEAN_FLAG_SET(FontOptions, (bold)(italic)(underlined)(strike_through));

class Font {
    public:

    Font() = default;
    Font(const Font &) = default;

    inline constexpr Font(FontOptions fo) noexcept;
};
 

Power set covariant return type template


This example suggest that the only way to make a new Font is by first having a FontOptions. However, there is nothing to prevent a Font being constructed with FontOptions not completely specified. This may be an important feature in other examples. Without further ado, I present the solution I came up with, which potentially is a new idiom that I will call “the power-set covariant return type template”, first, in its hand coded form:

namespace zoo {
    
template<unsigned FlagsSet> class FOI {
protected:
    enum Flags { BOLD = 1, ITALIC = 2, UNDERLINED = 4, STRIKE_THROUGH = 8 };
    unsigned char flagsSet = 0;

    explicit constexpr FOI(unsigned integer) noexcept: flagsSet(integer) {}

    constexpr unsigned flagVal(bool v, unsigned flag) const noexcept {
        return v ? (flagsSet | flag) : (flagsSet & ~flag);
    }

    template<unsigned> friend class FOI;

public:
    FOI() = default;
    FOI(const FOI &) = default;

    constexpr FOI<FlagsSet | BOLD> bold(bool v) const noexcept {
        return FOI<FlagsSet | BOLD>(flagVal(v, BOLD));
    }

    constexpr FOI<FlagsSet | ITALIC> italic(bool v) const noexcept {
        return FOI<FlagsSet | ITALIC>(flagVal(v, ITALIC));
    }

    constexpr FOI<FlagsSet | UNDERLINED> underlined(bool v) const noexcept {
        return FOI<FlagsSet | UNDERLINED>(flagVal(v, UNDERLINED));
    }

    constexpr FOI<FlagsSet | STRIKE_THROUGH> strike_through(bool v) const noexcept {
        return FOI<FlagsSet | STRIKE_THROUGH>(flagVal(v, STRIKE_THROUGH));
    }

    constexpr bool getBold() const noexcept { return flagsSet & BOLD; }
    constexpr bool getItalic() const noexcept { return flagsSet & ITALIC; }
    constexpr bool getUnderlined() const noexcept { return flagsSet & UNDERLINED; }
    constexpr bool getStrikeThrough() const noexcept { return flagsSet & STRIKE_THROUGH; }
};

}

template<unsigned fs> using FontOptions = zoo::FOI<fs>;

struct Font {
    bool b, i, u, s;
    Font(FontOptions<15> o): b(o.getBold()), i(o.getItalic()), u(o.getUnderlined()), s(o.getStrikeThrough()) {}
};

Font makeFont() {
    return  FontOptions<0>().bold(true).underlined(false).italic(true).strike_through(true);
}

Go ahead and try to build a Font from less than the result of initializing all the flags, the compiler will reject the code.

The technique before lets you:
  1. To not lose any performance (if in doubt, construct a test case and see the assembler)
  2. Full battery of compile-time reasoning about the values
  3. To set the flags by name
  4. Regardless of order
  5. To have partial sets of flags and communicate them all over
  6. The hability to enforce that all the flags are needed, simply require the signature with the number that represents all bits set.
  7. Performance gains from keeping the set consolidated
In summary, at the same time greater clarity and performance.

The name “power-set covariant return type” stems for the fact that the return type of the accessors in effect traverse the power set of flags. Remember, the power set of a set is the set of all of its subsets. That is, the return types typically begin with the empty set (no flag has been set), and progress adding any flag to the current set; the possible ways are all of the power set.

Now, let us add macros to not repeat the same boilerplate code:
 
#define PP_SEQ_I_DECLARE_ENUMERATION_VALUE(r, dummy, i, e) e = (1 << i),
#define PP_SEQ_I_POWER_SET_SET_FLAG_FUNCTION(r, name, i, e)\
    constexpr name<set | internal::e> e(bool v) const noexcept { return flagVal(v, internal::e); }\
    constexpr bool BOOST_PP_CAT(get_, e)() const noexcept { return value & internal::e; }

#define POWER_SET_COVARIANT_RETURN_TYPE_FLAG_SET(name, flags)\
    template<unsigned set> class name {\
        struct internal {\
            enum {\
                BOOST_PP_SEQ_FOR_EACH_I(\
                    PP_SEQ_I_DECLARE_ENUMERATION_VALUE,\
                    ~,\
                    flags\
                )\
            };\
        };\
        unsigned char value = 0;\
        constexpr name(unsigned v) noexcept: value(v) {}\
        constexpr unsigned flagVal(bool v, unsigned flag) const noexcept {\
           return v ? (value | flag) : (value & ~flag);\
        }\
        template<unsigned> friend struct name;\
    public:\
        name() = default;\
        name(const name &) = default;\
        BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_POWER_SET_SET_FLAG_FUNCTION, name, flags)\
    }

 

But it is just a generalization!


So far, the language, compiler and preprocessor are cooperating with the idioms. Let’s now generalize the idiom of transforming the non-expressible concept of boolean argument list (potentially partial) into a configuration set for arbitrary types.

First, the input can’t be just a list of identifiers, because the members will have arbitrary types. This generalization won’t be for the uninteresting case of the members having the same type. Since the list will be pairs of name and type, and because in preprocessing it does not make a difference whether they are pairs, triplets, or larger tuples, let us make them triplets: the name of the internal member variable, the type, and the public interface accessor.

The example I’ve chosen to illustrate the generalization is that of a date, expressed either as an integer or as an aggregate of day, month, year. You’ll see below I first exemplify the technique by hand-coding an example, and then, a fairly complicated set of macros that generate the same thing. Notice that to be fair, I am also using union to compare the assembler generated to the case in which an alternate representation makes sense.

Again, the code will be published without further explanation.

Investigating on date representations such as these I stumbled upon interesting phenomena, which is not related to preprocessing. Since I do not want to bore the audience with too many preprocessing articles in a row, the next article will be on that subject matter.

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/comma_if.hpp>

namespace pp {

enum Month: unsigned char {
    JANUARY, FEBRUARY, JULY
};

#define PP_SEQ_MEMBER_OPERATOR(r, representative, op)\
    constexpr bool operator op(const self_type &arg) const noexcept { return representative(*this) op representative(arg); }

#define PP_COMPARABLE_OPERATIONS(type, representative)\
    using self_type = type;\
    BOOST_PP_SEQ_FOR_EACH(PP_SEQ_MEMBER_OPERATOR, representative, (==)(!=)(<)(<=)(>)(>=))

#define PP_EXPAND(...) __VA_ARGS__
#define MEMBERF(return_name, args) constexpr PP_EXPAND return_name (PP_EXPAND args) const noexcept

struct DateRecord {
    DateRecord() = default;
    DateRecord(const DateRecord &) = default;
    DateRecord &operator=(const DateRecord &) = default;

    constexpr operator int() const noexcept {
        using u8 = unsigned char;
        return d_ + (u8(m_) << 8) + (y_ << 16);
    }

    PP_COMPARABLE_OPERATIONS(DateRecord, int)
protected:
    union {
        struct {
            unsigned char d_;
            Month m_;
            short y_;
        };
        int integer;
    };

    constexpr DateRecord(unsigned char d, Month m, short y) noexcept:
        d_(d), m_(m), y_(y)
    {}
};

struct Date;

struct DI: DateRecord {
    using DateRecord::DateRecord;

    constexpr DI year(short y) const noexcept { return DI(d_, m_, y); }
    constexpr DI month(Month m) const noexcept { return DI(d_, m, y_); }
    constexpr DI day(unsigned char d) const noexcept { return DI(d, m_, y_); }

    constexpr inline Date date() const noexcept;
};

struct Date: DateRecord {
    Date() = default;
    Date(const Date &) = default;

    constexpr Date(DI initializer) noexcept: DateRecord(initializer) {}
};

constexpr Date DI::date() const noexcept{
    return Date(*this);
}

constexpr Date Apollo11Landing = DI().month(JULY).day(22).year(1969);
constexpr auto UnixEpoch = DI().year(1970).month(JANUARY).day(1).date();

static_assert(Apollo11Landing < UnixEpoch, "No!");

}

// The following six macros are "framework" style macros that perform
// useful work in many situations
#define PP_SEQ_METACALL(r, macro, e) macro e
#define PP_SEQ_I_ENUM_METACALL(r, macro, i, e) BOOST_PP_COMMA_IF(i) macro e

#define PP_EXPAND(...) __VA_ARGS__
#define PP_EMPTY()
#define PP_DEFER(...) __VA_ARGS__ PP_EMPTY()
#define PP_DEFERRED_BOOST_PP_SEQ_FOR_EACH_I() BOOST_PP_SEQ_FOR_EACH_I

// Now, the macros specific to the implementation of this idiom
#define PP_IMPL_DECLARE_IDENTIFIER(identifier, type, name) type identifier

/// Used for initializer lists to define constructors
#define PP_IMPL_INITIALIZER(name, type, fullname) name(name)

/// Simply add a semicolon to the declaration
#define PP_IMPL_DECLARE_MEMBER(name, type, fullname) PP_IMPL_DECLARE_IDENTIFIER(name, type, fullname);

/// Implementation detail of PP_SEQ_I_PSEUDO_CONSTRUCTOR: fragment of the setter signature
#define PP_IMPL_PSEUDO_CONSTRUCTOR_FRAGMENT(name, type, fullname) fullname(type arg) const noexcept

/// Converts the given tuple into an argument copy or \c arg depending on
/// Whether the index is the same
#define PP_IMPL_SEQ_I_PSEUDO_COPY(r, index, i, e)\
    BOOST_PP_COMMA_IF(i) BOOST_PP_IIF(BOOST_PP_EQUAL(index, i), arg, BOOST_PP_TUPLE_ELEM(3, 0, e))

/// Copies all the members except the member with the given index, which will be \c arg
#define PP_IMPL_PSEUDO_COPY(members, index)\
    PP_DEFER(PP_DEFERRED_BOOST_PP_SEQ_FOR_EACH_I)()(PP_IMPL_SEQ_I_PSEUDO_COPY, index, members)

/// This is the setter that returns a copy modified
#define PP_IMPL_SEQ_I_PSEUDO_CONSTRUCTOR(r, name_members, i, e)\
    constexpr BOOST_PP_TUPLE_ELEM(2, 0, name_members) PP_IMPL_PSEUDO_CONSTRUCTOR_FRAGMENT e {\
        return BOOST_PP_TUPLE_ELEM(2, 0, name_members) (\
            PP_IMPL_PSEUDO_COPY(BOOST_PP_TUPLE_ELEM(2, 1, name_members), i)\
        );\
    }

// Now, macros intended to be used by the end programmer
#define PP_IMPL_DECLARE_MEMBERS(members)\
    BOOST_PP_SEQ_FOR_EACH(PP_SEQ_METACALL, PP_IMPL_DECLARE_MEMBER, members)

#define PP_VALUES_CONSTRUCTOR(name, members)\
    constexpr name(BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_ENUM_METACALL, PP_IMPL_DECLARE_IDENTIFIER, members)) noexcept:\
    BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_ENUM_METACALL, PP_IMPL_INITIALIZER, members) {}

#define PP_PSEUDO_CONSTRUCTORS(name, members)\
    PP_EXPAND(BOOST_PP_SEQ_FOR_EACH_I(PP_IMPL_SEQ_I_PSEUDO_CONSTRUCTOR, (name, members), members))

#define PP_CONFIGURATOR(name, members)\
    class name {\
        protected:\
        PP_IMPL_DECLARE_MEMBERS(members)\
        PP_VALUES_CONSTRUCTOR(name, members)\
        public:\
        name() = default;\
        name(const name &) = default;\
        PP_PSEUDO_CONSTRUCTORS(name, members)\
    }

#define MEMBERS ((d_, unsigned char, day))((m_, pp::Month, month))((y_, short, year))
PP_CONFIGURATOR(dateI, MEMBERS);

struct date: dateI {
    date() = default;
    date(const date &) = default;

    constexpr date(dateI d) noexcept: dateI(d) {}

    MEMBERF((operator int),()) {
        using u8 = unsigned char;
        return d_ + (u8(m_) << 8) + (y_ << 16);
    }

    PP_COMPARABLE_OPERATIONS(date, int)
};

constexpr date Apollo11Landing = dateI().month(pp::JULY).day(22).year(1969);
constexpr date UnixEpoch = dateI().year(1970).month(pp::JANUARY).day(1);

static_assert(Apollo11Landing < UnixEpoch, "No!");

constexpr dateI dt = dateI().month(pp::JULY).day(22).year(1969);

PP_CONFIGURATOR(
    fulltime,
    ((n_, unsigned, nanos))
    ((s_, unsigned short, seconds))
    ((m_, unsigned char, minutes))
    ((h_, unsigned char, hours))
    ((d_, unsigned char, day))
    ((mo, pp::Month, month))
    ((y_, short, year))
);

constexpr fulltime ft = fulltime().nanos(2134).hours(3).minutes(22).month(pp::FEBRUARY).year(2015).seconds(33).day(3);

fulltime pairoft[2];
static_assert(sizeof(pairoft) == 24, "");

Tuesday, December 29, 2015

The path to the innermost nature of things: metaprogramming

Metaprogramming

I once had the privilege of attending a presentation by Stephen Dewhurst on the subject of metaprogramming. It is from this event that I learned metaprogramming was not designed into C++ but discovered. That is, our predecessors trying to squeeze performance out of the language developed idioms that would have the compiler go beyond merely translating to object code, to also perform useful computation and insert the results to the object code, so that they would not have to be computed at run time; these techniques grew and became a new way to program; C++'s type system is so powerful that it is “turing complete”, that is, it can be used to program anything. This does not mean that it is practical, especially not that it is easy to do it.

Expressing relationships

There are times when meta programming is helpful, not just to bring to compilation time computation that otherwise would be done at run time, but computation that helps improve software quality. Most software elements have many relationships to other software elements, better programming languages allows the expression and maintenance of more and more abstract of those relationships. It is this interest, of improving software quality by expressing sophisticated relationships between software elements, what is my main interest in metaprogramming. I have introduced BOOST preprocessing in the last two articles illustrating techniques whose aim is to express relationships between software elements that can not be expressed within the pure language, I thought on reviewing those articles that I should have begun with relationships that can be expressed, hence I allow myself to postpone once again the description of how to use boost preprocessing.
By way of example, let us discuss the data structure of the singly linked list. Not caring about the operations, the data layout may be:
template<typename T> struct SListNode { T value; SListNode *link; };
struct SList { SListNode *head; unsigned size; };
However, this can be wasteful. In today’s architectures a pointer is typically 8 bytes long, if the element type T is as small as a single byte, because of very important alignment considerations, the size of the node will be 16 bytes. That’s an overhead of 16 to 1…

Type inferences

Can this be implemented better? Most architectures will have the same performance dereferencing a pointer as dereferencing a pointer plus an offset. That is, *pointer performs as well as *(pointer + offset). This is the case on the AMD64 architectures, and in Intel’s 32 bit and AMD64, there is another mode of addressing with the same performance, the “Scale Index Base”: *(pointer + index*element_size + offset) is as performing as *pointer provided the element_size is any of 1, 2, 4, or 8. Incidentally, in intel32/AMD64, the fastest way to multiply by a constant such as 5, (or 3 or 9) is to “load the effective address” of the operand register + 4*register, for example, lea (rax + 4*rax), rbx.
If we are not going to hold more than 4 giga elements, using a pointer to discriminate is wasteful, so it may be practical to create a memory arena for our lists, just a large array of bytes. Then, our node could be represented like this:
template<typename T> struct SListNode {
    static SListNode *arena; ///< a class-member common to all lists of this type T
    T value;
    unsigned arenaIndexOfNext;

    SListNode &next() { return arena[arenaIndexOfNext]; }
    unsigned currentIndex() const noexcept { return this - arena; }
};
If the total number of nodes will be less than 2^16, thus we won’t have more than 2^16 indices, and the size of the type T is 2 bytes, our current implementation of using an unsigned (typically 4 bytes) forces the node to be of 8 bytes when it really requires 4; where I am going with this line of reasoning is that the optimal type used for the index is dependent on the size of the type T and the maximum number of nodes to be supported. Conceivably, we can manually implement the structure for each possible combination:
  1. 1 byte element, up to 256 nodes: use a byte for index
  2. up to 2 byte elements and less than 2^16 indices: use a two-byte for the index
  3. up to 4 byte elements use a four-byte index
  4. more than 4 byte elements use a pointer

Generic programming

And this is in the case we want to keep the value and the link together, if we chose to have separate arenas for the values and the links represented as indices, we have the whole cartesian product of sizes: for the element type size < 2, < 4, < 8, >= 8 and the total number of elements, < 2^8, < 2^16, > 2^16 or 12 different cases that are essentially the same code, just substituting the appropriate types and constants. The language has templates, both with type parameters and non-type parameters to accomplish coding the mechanism (singly linked list through arenas and indices). In that sense it is generic, in which the implementation may not care about the details of the parameters.
To use the generic implementation the programmer/user still has to make a choice: what is the type of the index that best suits the number of possible indices and the type of the elements; as explained in the preceding list, there is a clear way to select the right index. Since both the element type and the maximum number of indices are not known when writing the code to select the right index type, in effect that code will reason about elements that will be given to the program at a later time, that is, they work with elements of the program itself, hence metaprogramming.
So, generic is when the specific types (or constants) don’t matter; meta is when the parameters to templates will be decided based off other types the programmer gives, of course that each are the other to some degree. Alexander Stepanov, the person with the largest share of the credit for the invention of Generic Programming said in this zero-waste interview that he “[believes] it is necessary to design a programming language from the ground up to enable generic programming in a consistent way”. It is safe to say that the higher one goes into generic abstraction, that is, into proper meta-programming territory, the inconsistencies become much harder to overcome.
It begs the question, is it worth at all fighting this accidental complexity on top of the already huge inherent complexity? I would say yes, it is, because the inherent complexity (being able to program in a generic way) is the ultimate way to make software, to make something generic, one needs to understand it to its core, thus the inherent complexity of generic programming is how to capture the innermost nature of what’s being programmed. With regards to the accidental complexity, I draw from my personal experience in that I have had to exercise myself fully into getting around the inconsistencies; I have used techniques of functional programming, of logic programming, of artificial intelligence, and mathematics, more mathematics and even more mathematics. I have reached a much higher understanding of what programming is all about thanks to my interest in trying to accomplishing genericity. Surprisingly, I have actually succeeded at getting great practical results after it all. Think about all of the frustrations that needed overcoming to program the STL so that it could be approved into the 1998 standard, but then look at the monumental accomplishment… just to name one example, C++, to my knowledge, is the only language that has a managed array type (std::vector) with essentially zero performance overhead over unmanaged arrays… and in the 17 years and counting since it has been part of the standard, all other languages still “don’t get it”, for example, Java, C# have “generics” which are but pathetic syntactic sugar.
That is, the fighting itself has been an excellent school.
I find the subject so fascinating that it motivates me to make community out of sharing our findings.
Back to the pedestrian dydactic example, here’s where meta programming is helpful:
  1. lets the programmer express the relationships between the element size and the index size (or whether to use pointers),
  2. lets the compiler choose the implementation case that best suits the size of the type and the maximum number of elements,
  3. relieves the programmer from the responsability to choose the index (and thus the chance for her/him to make a poor choice)
  4. keeps the option for further refinements
I trust the reader to be able to program the generic arena solution given the optimal type for the indices, and being able to specialize for the case in which it makes more sense to use the standard node structure. For dydactical purposes, this is what will give the adequate index type for the arena implementation in which the nodes are the composition of the element and the index. The template alias meta::Index that takes a template type parameter (what is going to be held in lists) and the maximum number of nodes for lists of that type; this is defined below:
namespace meta {
    template<unsigned> struct type_size { using type = unsigned long; };
    template<> struct type_size<4> { using type = unsigned; };
    template<> struct type_size<2> { using type = short unsigned; };
    template<> struct type_size<1> { using type = unsigned char; };

    constexpr unsigned powerOf(unsigned base, unsigned exponent) noexcept {
        return exponent ? base*powerOf(base, exponent - 1) : 1;
    }

    constexpr unsigned logCeiling(unsigned base, unsigned v) noexcept {
        return v <= 1 ? 0 : 1 + logCeiling(base, (v + base - 1)/base);
    }

    template<typename T> constexpr T max(T a, T b) { return a < b ? b : a; }

    template<typename ElementType, unsigned MaxIndex> using Index =
        typename type_size<
            powerOf(
                2,
                max(
                    logCeiling(2, sizeof(T)),
                    logCeiling(256, Max)
                )
            )
        >::type;
}
Just a few definitions and the compiler will be able to pick the right index type. Provided you have an implementation of a heap manager, you’d have reduced the space overhead of containing elements in lists to its optimum. Since indexing as opposed to direct dereferencing has no penalty, the net effect of generic programming plus metaprogramming are
  1. Generics make it so that two implementations (normal single linked list and arenas) reduce the chances for errors of doing things manually or the different varieties to drift apart
  2. The space overhead is minimal,
  3. the increase in space efficiency translates into net speedup due to more effective caching
  4. No other performance penalty
  5. Increased compilation time
In essence, metaprogramming has been used in this example to express the relationship between the optimal index type to the element type and the maximum number of nodes that will be supported. The benefit or the importance of being able to code this relationship is trivial, however, the relationships that can be expressed with the mechanisms of metaprogramming are of arbitrary complexity, and they lead to truly great benefits.

Wednesday, December 23, 2015

Lists of function call arguments

Another thing not expressible in C++ are lists of arguments to functions, but curiously enough, lists of arguments to templates are actually expressible in a limited way.

Whenever I have referred to inexpressible things, what I have meant is that one loses the help from the compiler to keep track of details it should be able to keep track of, one is forced to do it manually, even resorting to very dangerous copying and pasting.

I’ve just read in the e-zine “Overload” number 130 the QM Bite on boolean parameters. The author points out three issues with boolean parameters, for example’s sake, let’s say a function that takes three flags to construct a font: bold, italic, underlined.

Without any more information, can you tell what does this fragment of code does?

auto userFont = createFont(12, false, true, false, true);

What about this other fragment?:

auto userFont = createFont(12, FontOptions().italic().underlined());

In this article we will work towards implementing a building block that will let us transform lists of boolean arguments into a call as the one just portrayed.

The author of the QM Bite (Matthew Wilson) objects to boolean parameters. The three issues he mentions are:

  1. Since they all have the same time, it is easy to to try to set boldness but make a mistake and set italic instead (you’d have to remember the exact position of each parameter)
  2. It will be difficult to ever change the order of the arguments, change their number, etc.
  3. The arguments obfuscate the code, to see what a true as argument means, the reader needs to jump to the declaration of the argument, manually count how many positions.

I would add a fourth complaint: The quantity of arguments must be minimized, the fewer the number of arguments, the easier to understand, manage what a function does. It improves quality.

Since lists of function call arguments are not expressible, try to avoid passing individual parameters, instead, group them together into sets. As always, the members of the set must be highly cohesive. If you do this right, then you’d have an abstraction improvement that will accelerate your development.

Continuing with the example by Matthew Wilson, imagine you capture the flags from the user: Preserving the list of arguments, there is no recourse but to manually copy the values of each of the flags every time something needs propagating. This makes no sense, it is clear that bold, italic, underlined, strike-through, etc. are a set of configuration options. Implement them exactly like that.

I think Mr. Wilson’s advice is no good, “enumeralization” won’t help you any: You incurr the extra work to create synonims of the type bool (which is not easily supported in C++ either), and you continue to have the problems of remembering the exact order of the arguments, the same inflexibility as to their quantity, etc. All you have helped yourself with is for the compiler catching when you accidentally set, for example, bold when italics was intended, a dubious trade for the extra work.

Here’s a hopefully better solution, if we could get for free the following code, the implementation of the set of options:

struct FontOptions {
    enum Options: unsigned char {
        UNMODIFIED = 0,
        BOLD = 1,
        ITALIC = 2,
        UNDERLINED = 4,
        STRIKE_THROUGH = 8
    };

    unsigned char value = 0;

    FontOptions() = default;
    FontOptions(const FontOptions &) = default;

    FontOptions unmodified() const { return FontOptions(0); }
    FontOptions bold() const { return value | BOLD; }
    bool is_bold() const { return value & BOLD; }
    FontOptions not_bold() const { return value & ~BOLD; }
    FontOptions italic() const { return value | ITALIC; }
    bool is_italic() const { return value & ITALIC; }
    FontOptions not_italic() const { return value & ~ITALIC; }
    FontOptions underlined() const { return value | UNDERLINED; }
    bool is_underlined() const { return value & UNDERLINED; }
    FontOptions not_underlined() const { return value & ~UNDERLINED; }
    FontOptions strike_through() const { return value | STRIKE_THROUGH; }
    bool is_strike_through() const { return value & STRIKE_THROUGH; }
    FontOptions not_strike_through() const { return value & ~STRIKE_THROUGH; }

    // The usual equality, difference operators, perhaps even the relational
    // not implemented for brevity of exposition

private:
    FontOptions(unsigned v): value(v) {}
};

then creating a font could have this signature: Font createFont(unsigned size, FontOptions options). And it could be called like this:

auto myFont = createFont(12, FontOptions().italic().underlined());

As you can see, instead of suffering of problems mentioned, this example:

  • shows how easy it is to understand exactly what are the options set,
  • there is no need to remember the order,
  • introducing new options won’t even necessarily require recompilation, and in particular no code change,
  • and should you want to change the meaning of any of the options, a simple grep of the desired setter will tell you reliably all the call sites so that you change accordingly.

Very probably inside the Font implementation you’d want to have a set of options. You could use ‘FontOptions’ exactly as it is inside Font. You could communicate internally the whole set of options using FontOptions. It is useful.

Observe that the implementation of FontOptions is very repetitive. And incomplete, since the constructors, setters, etc, are not constexpr noexcept, there is no operator for comparison, etc; it is error prone too, at any moment one could have written ‘italic’ meaning ‘bold’. These things are best left to something automated. Unfortunately, we begin with a list of identifiers (bold, …) and as already explained in the previous article, there is no support in pure C++ for them; we have turned the inexpressible function call argument list into a list of identifiers, thus, as done in the previous article, the next best choice is to use preprocessing:

#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/cat.hpp>

#define PP_SEQ_I_DECLARE_ENUMERATION_VALUE(r, dummy, i, e) e = (1 << i),
#define PP_SEQ_I_FLAG_SET_MEMBER_FUNCTIONS(r, name, i, e)\
    constexpr name e() const noexcept { return value | internal::e; }\
    constexpr name BOOST_PP_CAT(not_, e)() const noexcept { return value & ~internal::e; }\
    constexpr bool BOOST_PP_CAT(is_, e)() const noexcept { return value & internal::e; }

#define BOOLEAN_FLAG_SET(name, flags)\
    class name {\
        struct internal { enum: unsigned char {\
            EMPTY = 0,\
            BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_DECLARE_ENUMERATION_VALUE, ~, flags)\
        }; };\
        unsigned char value = 0;\
        constexpr name(unsigned v) noexcept: value(v) {}\
    public:\
        name() = default;\
        name(const name &) = default;\
        BOOST_PP_SEQ_FOR_EACH_I(PP_SEQ_I_FLAG_SET_MEMBER_FUNCTIONS, name, flags)\
        constexpr bool operator==(name other) const noexcept { return value == other.value; }\
        constexpr bool operator!=(name other) const noexcept { return !(*this == other); }\
    }

The use of the macro BOOLEAN_FLAG_SET is very easy, for example:

BOOLEAN_FLAG_SET(FO, (bold)(italic)(underlined)(strike_through));

This would declare and define a class FO that does the same things as FontOptions. I hope you’d agree that declaration and definition takes very little effort and prevents lots of errors. Also, we could improve this so that we also get inserter and extractor operators with minimal effort.

Before explaining the implementation, as a conclusion to this article, I advocate for the following:

  • Because there is no support for list of arguments, avoid them
  • Strive to minimize the number of arguments in your function calls
  • Grouping parameters into a single aggregate tends to capture useful semantics of the application domain that will speed up development and improve quality
  • Pure C++ offers limited support for expressing the groups of parameters identified, typically, the preprocessor may palliate this defficiency.

The same technique illustrated in this example can be generalized for arbitrary collections of parameters; for example, to build a date:

Date().month(FEBRUARY).year(2015).day(28)

That is, the same pattern can be used but the setters may take a value argument. Expressing the type of these arguments make the macro more complicated, hence its exposition will be postponed.

Also, there is no need to worry about returning by value and potentially doing wasteful copies of returned values, observe that the compiler has full visibility into what is going on, all the intermediate values are temporaries, and the “return value optimization” can be applied recursively from last to first (in this case from calling day back to Date()); hopefully this will be illustrated in the real world. Also notice that this pattern of acting on a value and not changing it but returning a modified copy is a tried and true technique in functional programming languages.

Monday, November 30, 2015

Introspection, preprocessing and boost preprocessing

I was reading our blogspot neighbor Scott Meyers, “The Brick Wall of C++ Source Code Transformation”, where he discusses one of the worst problems in C++: The overreliance on the preprocessor. For a few years now I have been using the preprocessor to palliate the deficiencies of the introspection capabilities. I hope that would be another class of legitimate use of the preprocessor such as to #include headers because there is no concept of modules and simple management of versions through conditional compilation, like when one asks if the code is being compiled with G++, #if defined __GNUG__. I am not entirely sure this use is legitimate, but it seems the least bad of the practical options. Let us get to know the animal --monster-- of the preprocessor in more detail, to appreciate its cost this time. In later articles I hope to make good exhibits for the zoo, in which this animal shows its potential.

What is the problem?

The preprocessor is a source code transformation language, the problems are that it is a very poor language and inserting a source code transformation layer between the programmer and the compiler makes their lives much harder. Concretely, it makes making tools hard to nearly impossible, makes compilation much slower, it makes source code much harder to understand and it invites problems.

Headers

Although the language does not support modules we still need to import their interfaces, that’s why we speak of declarations and definitions. For example, #include is equivalent to copying and pasting the included file into the current file. However, the declarations in the included header are affected by the context carried over, and this opens a can of worms. Let’s say in file header1.h there is this code:
namespace library1 {

// some declarations
#define ITEM product
// more declarations, ITEM is never undefined

}
Later, another header may have this content:
namespace library2 {

// some declarations
struct ITEM {
    void not_inline_function();
    // …
};

// more declarations
}

Then, if a file.cpp has this content:
#include “header1.h”
#include “header2.h”

Every mention to “ITEM” will in reality refer to a product. The program may even compile, since the declaration struct ITEM will be silently converted to struct product in a different namespace, and within that namespace, all the mentions to ITEM will be consistently changed to product. It may even link!, if all the uses of ITEM are for data members and inlined functions. However, months later some change happens to the code, somebody uses the member function not_inline_function, and gets stomped with the link error undefined reference to not_inline_function

Back when people were switching from 32 bits to 64 bits, it was frequent for code to assume a pointer and an integer were of the same size, so, there was lots of people that would #define int long and rebuild. Of course, things may appear to work, the developer may be praised for how fast he did the migration, but things will eventually explode catastrophically because some “who knows what” library function not recompiled assumed 32 bits for an integer and it got 64.

Perhaps my examples are not good, in any case, the point I am trying to make is that a header and its corresponding binary library must be exactly in sync, otherwise, things may seem to work until they explode; because headers are vulnerable to the context in which they are #included, going through the preprocessor introduces all sorts of nasty possibilities for subtle errors.

Another problem, of a practical nature, is that the compiler is forced to compile the #included headers every time they appear, because since the interpretation of a header depends on the exact code that precedes its inclusion, there is no remedy but to do it all over! Note: this is what makes precompiled headers worthless: To guarantee the headers will all have the same meaning, the programmers must put together all the headers they could potentially include into a single ‘universe’ header, which is big, fat, greasy with leaking references to implementation details, couplings to implementation details and very low cohesiveness. Then, in all places were well thought-off headers would be included, to include the universe instead.

Versions

The other legitimate preprocessor case I mentioned of conditional compilation and versioning suffers from not having a way to guarantee that a prebuilt binary is compatible with the current source code and compilation options, leading in practice to the wasteful re-compiling and re-linking; or much worse, that apparently things work but in reality have catastrophic bugs: Let’s say a.cpp gets compiled assuming struct Foo has a member int member but because of subtle interplay of conditional compilation, in b.cpp struct Foo may have a member long member. Things may appear to be fine, but the layout of Foo is different, somewhere some code may think it is X bytes long, and some other Y bytes long, and then all bets are off.

These are not new problems, they are actually over forty years old. The culprit is that the language does not offer any way to guarantee declarations for library users will respect the assumptions made in their compiled/linked binary implementations. It is truly embarrassing, and I’ve not seen any prospects for a solution, not even for C++17 when some form of modules are being discussed.

Toolability

Because of language deficiencies, we still need to use the preprocessor, and this means that the only tools that can be developed for the language must in some way or other be also compilers. For example, imagine an IDE tool that wants to help with autocompletion of function calls. To be useful, the tool must be able to add applicable functions that are the result of macro expansions. Thus the tool must be able to preprocess, on top of all the other pure language requirements. It also must be able to know which macro expansions correspond to what places in the source code. What about function-like macros? when doing auto completion, is it going to offer the macro or only its expansion?

Introspection

I hope the claim that the preprocessor is a real problem has been substantiated, however, for all of its expressiveness the language has deficiencies that are covered by the preprocessor, thus it may be legitimate to continue to alleviate the deficiencies through the preprocessor. A consistent set of introspection features is sorely lacking. By introspection I mean what commonly is referred to as “reflection”, when there are ways in which programming constructs can reason about themselves. For example, in Java it is trivial to ask a class for its data and function members; it is possible to know everything there is to know about them. In C++ there is a collection of ad-hoc introspection capabilities, such as whether dynamic_cast of a pointer returns zero or not, the typeid, the sizeof operator and the very powerful collection of pattern matching capabilities of templates. For all of their might, it is simply not possible to know even the number of data members in a plain structure. It is not possible to know, from within the program itself, the count of enumerations in an enumerated type, nor what are the identifiers used, nor the mapping of enumerations to integer values, nor their reverse mapping.

The only option to accomplish introspection capabilities is to develop idioms or conventions so that we can plug some of the capabilities mentioned above.

The use case that led me down the path of using boost preprocessing was the simple need of converting enumerated values to strings. For example:
enum Enumeration {
    VALUE1 = 9,
    VALUE2 = 8,
    VALUE3 = 2
};

How to implement something like Enumeration::count() that will tell you the number of enumerated values, or Enumeration::values(), or Enumeration::identifiers()? This would need to be done manually. Typically, one wants to make something like std::ostream &operator<<(std::ostream &output, Enumeration value);, this is what we repeatedly waste our time doing:
std::ostream &operator<<(std::ostream &output, Enumeration e) {
    switch(e) {
        case VALUE1: output << “VALUE1”; break;
        case VALUE2: output << “VALUE2”; break;
        case VALUE3: output << “VALUE3”; break;
    }
    return output;
}

It does not look like much work, and it isn’t, but the real problem is that some day somebody will want to put VALUE4=13, forget to map it, and then the code won’t work. Also, somebody will just copy and paste forgetting to change the number:
std::ostream &operator<<(std::ostream &output, Enumeration e) {
    switch(e) {
        case VALUE1: output << “VALUE1”; break;
        case VALUE2: output << “VALUE1”; break;
        case VALUE3: output << “VALUE1”; break;
    }
    return output;
}

The compiler is happy. Your tests will be happy too, but in the critical log entry for your application, whenever VALUE2 appeared you’ll see VALUE1 written and be mystified…

Doctrine

I don’t like guranteeing relationships between software components manually, it is the same thing as using explicit new and delete instead of smart pointers; it is error prone. I like C++ because it lets me express fairly non-trivial relationships so that the compiler will guarantee them for me. When pure C++ is not sufficient, I try hard to find the least meta-C++ before resorting to do things manually. This element of my doctrine has served me very well. Even if my choices to represent relationships look very ugly, at least they are expressed, subject to improvement, and handled automatically; over time, things look less ugly, because you learn how to express yourself better, reducing accidental complexity and the inherent complexity is reduced once you realize it is necessary.

None of these happen with manual coding of relationships.

In general, I have reached to the conclusion that lists of identifiers are inexpressible in pure C++. Thus we have to look beyond for ways to handle lists of identifiers. Is it possible to express them using the preprocessor?

Boost preprocessing

Four years ago I discovered boost preprocessing while getting acquainted with the code of a system new to me, a genius coworker had coded a general solution to declare, define and print enumerations which used boost preprocessing, and at the same time, before C++11 his solution was capable of expressing enumerations in their own scope and based off user-specified integer types.

My old coworker got to different choices to what I will propose, but the idea is essentially the same. Let’s work toward the refined solution I made, illustrating each objective and solution.

Let us begin with the enumeration shown above. To be able to “reason” about that enumeration, at the bare minimum we need something that converts enumerated values to their identifier as a string. We need little else; these few things can be put together in an structure, the way in which we typically bind together related compile-time constructs. Let us associate the enumerated values to their strings by using a simple std::array:
#include <array>

struct demo {
    using integral_type = unsigned char;

    enum enumeration {
        VALUE1 = 8,
        VALUE2 = 9,
        VALUE3 = 2
    };

    using map_type = std::unordered_map<integral_type, const char *>;

    static constexpr unsigned count = 3;

    static constexpr std::array<typename map_type::value_type, count> values =
    {{
        { VALUE1, "VALUE1"}, { VALUE2, "VALUE2" }, { VALUE3, "VALUE3" }
    }};
};

demo has integral_type, enumeration, map_type, count and values, that I think serve a very clear role. Pure C++ won’t let us express a relationship between enumeration and values, but there is a way, which we will illustrate momentarily. In any case, with these we can implement any number of interesting introspection capabilities:
  1. Represent enumerated values using the integral_type
  2. All of the value-type services expected: construction, comparison, etc.
  3. It is possible to convert enumerated values to strings and viceversa
  4. Iteration over all the valid values of the enumeration
  5. All of these operations can be implemented to run very fast and potentially as compile-time constructs, that is, as if the language provided these features out of the box.
Let us augment the demo struct with many straightforward features through a template that combines some of the members into useful things. Please observe that indeed we got away with making almost all the things constexpr, noexcept:
#include <unordered_map>

namespace zoo {

template<typename E> struct smart_enumeration: E {
    using typename E::integral_type;
    using typename E::enumeration;

    integral_type code;

    constexpr smart_enumeration() noexcept: code(E::values[0].first) {}

    explicit constexpr
    smart_enumeration(integral_type v) noexcept: code(v) {}
    
    smart_enumeration(const smart_enumeration &) = default;
    
    explicit constexpr
    smart_enumeration(enumeration e) noexcept: code(e) {}

    smart_enumeration &operator=(enumeration e) noexcept
    { code = e; return *this; }

    constexpr operator integral_type() const noexcept { return code; }

    constexpr bool operator==(smart_enumeration e) const noexcept
    { return code == e.code; }
    constexpr bool operator!=(smart_enumeration e) const noexcept
    { return not (*this == e); }

    operator const char *() const {
        static const auto copy = E::values;
            // Note: this copy prevents having to "define" E::values
        static const std::unordered_map<integral_type, const char *>
            mapping(copy.begin(), copy.end());
        auto resultFind = mapping.find(code);
        if(mapping.end() == resultFind) { return nullptr; }
        return resultFind->second;
    }

    bool valid() const { return nullptr == static_cast<const char *>(*this); }
};

}

Note: Yeah, none of the constexpr, noexcept, and even const above are superflous: The compiler does not automatically allow the use of a non-constexpr function in compile-time expressions, nor deduces noexcept automatically; and because it was deemed a small mistake of C++ 11 that constexpr implies const in C++ 14 it doesn’t. It is a shame since all of these annotations add clutter and are deducible by the compiler.

Note2: The conversion to string is implemented not as a compile-time function because it uses an unordered_map; however, with enough effort it is possible to implement a compile-time map, even in C++11, just that it is not practical.

Now, the implementation of things like the insertion operation:
#include <istream>

namespace zoo {

template<
    typename E
> std::ostream &operator<<(std::ostream &output, zoo::smart_enumeration<E> e) {
    const char *ptr = e;
    if(ptr) { output << ptr; }
    return output;
}

}

Let us write a non-inline function that the compiler is forced to translate to drive enough of the implementations we have, also a “main” to prove we don’t have linking issues:
#include <iostream>

void drive(zoo::smart_enumeration<demo> d) {
    using se = zoo::smart_enumeration<demo>;
    static_assert(se().code == 8, "");
        // constexpr default constructor
    static_assert(noexcept(se()), "");
        // the default constructor is noexcept
        // Proven the constructor by integral and by enumeration are
        // constexpr and noexcept
    static_assert(noexcept(se(d)), ""); // default copy constructor is noexcept
    static_assert(9 == se(se(demo::VALUE2)).code, ""); // also constexpr

    static_assert(noexcept(se(8) == se(9)), ""); // equality comparison noexcept
    static_assert(noexcept(se(8) != se(9)), ""); // different is also noexcept
    static_assert(!noexcept(static_cast<const char *>(se())), "");
        // however, the conversion to const char * can throw
    std::cout << d << d.valid(); // All compiles
}

int main(int argc, const char *argv[]) { return 0; }

So, if we are able to supply the members that smart_enum requires, the same we put in demo, then forever we will be able to automatically get all the other stuff, and use our proto-introspection to implement lots of other things. For example, the backward map (string to enumeration) implemented in construct_smart_enum(std::string):
namespace zoo {

template<
    typename F, std::size_t N
> std::unordered_map<std::string, F> backward_map(
    std::array<std::pair<F, const char *>, N> argument
) {
    std::array<std::pair<std::string, F>, N> initializer;
    for(unsigned i = argument.size(); i--; ) {
        initializer[i].first = argument[i].second;
        initializer[i].second = argument[i].first;
    }
    return
        std::unordered_map<std::string, F>(
            initializer.begin(), initializer.end()
        );
}

template<typename E> smart_enumeration<E> construct_smart_enum(std::string s) {
    using se = smart_enumeration<E>;
    const static auto copy_for_gcc = E::values;
    const static auto reverted_map = backward_map(copy_for_gcc);
    return se(reverted_map.find(s)->second);
}

}

And the test, that also shows a possible way to use it:
smart_enumeration<demo> something()
{ return construct_smart_enum<demo>("VALUE2"); }

There only remains to show the magical incantation that will bind the identifiers to the enumeration values:
PP_SMART_ENUMERATION(
    demo,
    unsigned char,
    ((VALUE1, 8))((VALUE2, 9))((VALUE3, 2))
);

The expansion of that macro call will generate the code we wrote for demo above. This is the prestidigitation I just did in slow motion:
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>

#define PP_SEQ_META_CALL(r, MACRO, element) MACRO element

#define PP_MAKE_MAP_PAIR(identifier, value) { value, #identifier },
#define PP_MAKE_ENUMERATION_PAIR(identifier, value) identifier = value,

#define PP_SMART_ENUMERATION(name, integer_type, identifiers)\
struct name {\
    using integral_type = integer_type;\
    enum enumeration {\
        BOOST_PP_SEQ_FOR_EACH(PP_SEQ_META_CALL, PP_MAKE_ENUMERATION_PAIR, identifiers)\
    };\
    using pair_type = std::pair<integral_type, const char *>;\
    static constexpr unsigned count = BOOST_PP_SEQ_SIZE(identifiers);\
    static constexpr std::array<pair_type, count> values = {{\
        BOOST_PP_SEQ_FOR_EACH(PP_SEQ_META_CALL, PP_MAKE_MAP_PAIR, identifiers)\
    }};\
}

Not entirely self-explanatory? of course, and I have not even gone over the things that make this code break, but this is a great opportunity to leave the article in a cliff hanger…

Wednesday, November 4, 2015

Gaining clarity and performance

We will now look into the implementation of several argument passing choices.

Most of the argument passing is governed by the ABI, the “Application Binary Interface” which is the convention compiler vendors agree on so that code compiled with one vendor can be used by code compiled by another. Since I am primarily an x86-64 on Linux programmer I will concentrate almost exclusively on this platform, the most recent ABI can be seen here.

We saw in the article “Aliasing and autovectorization” how aliasing disrupts autovectorization. We will focus now on a simpler case, when passing by const reference versus passing by value only lead to the apparently small difference of using a parameter from memory versus using it from register.

Again we have to reduce the real life complications such as alignment. Previously I used the GCC extension __builtin_assume_aligned, which is also supported by Clang versions 3.5 and higher; however, that extension can only be applied to pointers, and I would like to move away from pointers. I will use GCC’s extension to define SIMD (Single Instruction Multiple Data) vector types, which is also supported by Clang. I don’t recommend using GCC’s SIMD vectors, I think they are actually broken. The situation is better with Clang. This is a subject matter that will be revisited when discussing the emerging superiority of Clang over GCC. Be it as it may, if the vector size is supported by the platform, SIMD vectors implicitly set the alignment to the vector size. By the way, if you can successfully run SIMD vectors on x86 using GCC’s extension, you can almost rest assured your code will be just as vectorized in any other platform because x86’s SSE instruction set is probably the hardest to use.

In the same example I’ve been using, this time I forced the basic data type to be a vector of 4 doubles, see this code that simply adds a value to the each element of the vector:
#include <vector>
 
// typedefs v4d as a vector of 32 bytes in which elements are doubles
// that is, a vector of 4 doubles
using v4d = double __attribute__((vector_size(32)));

void addVector(std::vector<v4d> &v, v4d value) {
    for(auto &e: v) { e += value; }
}

void addVectorByCR(std::vector<v4d> &v, const v4d &value) {
    for(auto &e: v) { e += value; }
}
The awesome tool Online GCC Explorer lets us see what GCC 5.2 --among other compilers and versions--, with 256-bit vector registers enabled (compiler options -mavx and -mavx2), produces:
addVector(std::vector<double __vector(4), std::allocator<double __vector(4)> >&, double __vector(4)):
    movq    8(%rdi), %rdx
    movq    (%rdi), %rax
    cmpq    %rdx, %rax
    je  .L7
.L5:
    vaddpd  (%rax), %ymm0, %ymm1
    addq    $32, %rax
    vmovapd %ymm1, -32(%rax)
    cmpq    %rax, %rdx
    jne .L5
.L7:
    vzeroupper
    ret
addVectorByCR(std::vector<double __vector(4), std::allocator<double __vector(4)> >&, double __vector(4) const&):
    movq    8(%rdi), %rdx
    movq    (%rdi), %rax
    cmpq    %rdx, %rax
    je  .L15
.L13:
    vmovapd (%rax), %ymm0
    addq    $32, %rax
    vaddpd  (%rsi), %ymm0, %ymm0
    vmovapd %ymm0, -32(%rax)
    cmpq    %rax, %rdx
    jne .L13
    vzeroupper
.L15:
    rep ret
We can see that there is only a deceptively small difference between passing by value the addendum (addVector) and passing it by const reference (addVectorByCR), let us explain the generated code for both versions at the same time since they are almost identical, which was the point:
  • The first two lines (2-3, and 16-17) are just loading the vector begin and end into rdx and rax (by the way, this clearly shows that the memory layout of vectors begins with two pointers, the buffer and its end),
  • Lines 4-5 or 18-19 are simply a check for empty vector
  • lines 7-11 or 21-26 are the main loop:
    • line 7 adds *rax to ymm0 into ymm1, that is, value was passed by value into ymm0.
    • Since the addendum is passed by const reference into addVectorByCR, the first thing is to load *rax into a vector register, ymm0, at line 21
    • lines 8 and 22 simply makes rax point to the next element in the vector
    • line 23 performs the addition from memory through the pointer rsi, this is the only difference
    • lines 10-11 and 25-26 just decide whether to loop back.
  • lines 13 and 27 are just an ABI artifact. Perhaps I will devote an article to “VZEROUPPER” after some preliminaries of SSE have been exposed.
From the point of view of performance, I would say the gcc 5.2 thinks adding from memory is about as fast as adding from a register, and it probably is, since value will be in the L1 cache for most of the iterations. But what would happen if another thread writes, not even to the actual location of &value, but just to the same cache line as &value?: The cache line for &value will be invalidated, thus, the thread iterating will stall while bringing the value of the addendum from memory to the L1 cache, this will have a latency equal to the latency of the cache level at which the cache line for &value is shared with the modifier, potentially all the way to main memory, which is about 100 times slower than L1. For sure it will be L2 at least (since in all x86 implementations the L1 cache is exclusive) which is around 4 times slower than L1. What if the thing the other thread is writing to is something like an accumulator of all the values in a vector? The other value will be changing very often, thus the cache line will be invalidated often and things may run 4, 10, 100 times slower… This problem is referred to as “false sharing”.

This is not an hypothetical scenario, false sharing is illustrated in plenty of good resources available online, including this presentation, around minute 8 by Scott Meyers.

So, we have another set of cases in which the apparently innocuous difference between passing by value and by const reference may lead to dramatic performance differences.

For small data types, that hopefully have copy constructors without side effects, it is clear one ought to pass them by value as much as possible.

There is some support to discover both things in templates: The operator sizeof will tell you the size of the type, and a stronger set of conditions on the nature of the copy constructor can be queried through is_trivially_copyable, so, with some effort (not illustrated now, but it will be in the future), it is possible to design templates that make an informed decision on whether to accept arguments by value or const reference.

Having the small size covered, let us explore the argument passing with increasingly larger types. If a type is sufficiently large, the ABI will prescribe the passing by value to be implemented not by register, but by stack, that is, as a “hidden reference” to the stack. This is illustrated by creating a structure big enough, Big, and using the same algorithm, add.
#include <vector>

// typedefs v4d as a vector of 32 bytes in which elements are doubles
// that is, a vector of 4 doubles
using v4d = double __attribute__((vector_size(32)));

struct Big {
    int otherStuff[32]; // 32 members of 4 bytes each
    v4d a, b, c, d; // 4 members of 32 bytes each

    Big &operator+=(const Big &v) { a += v.a; b += v.b; c += v.c; d += v.d; }
};

void add(std::vector<Big> &v, Big addendum) {
    for(auto &e: v) { e += addendum; }
}

void addByCR(std::vector<Big> &v, const Big &addendum) {
    for(auto &e: v) { e += addendum; }
}

Big opaqueFunction();

void opaqueAdd(std::vector<Big> &v, Big value);

void informedCall(std::vector<Big> &v) {
    Big addendum = opaqueFunction();
    add(v, addendum);
}

void uninformedCall(std::vector<Big> &v) {
    Big addendum = opaqueFunction();
    opaqueAdd(v, addendum);
}
Which gives this result:
add(std::vector<Big, std::allocator<Big> >&, Big):
    movq    (%rdi), %rax
    movq    8(%rdi), %rdx
    cmpq    %rdx, %rax
    je  .L6
    vmovapd 136(%rsp), %ymm4
    vmovapd 168(%rsp), %ymm3
    vmovapd 200(%rsp), %ymm2
    vmovapd 232(%rsp), %ymm1
.L3:
    vaddpd  128(%rax), %ymm4, %ymm0
    addq    $256, %rax
    vmovapd %ymm0, -128(%rax)
    vaddpd  -96(%rax), %ymm3, %ymm0
    vmovapd %ymm0, -96(%rax)
    vaddpd  -64(%rax), %ymm2, %ymm0
    vmovapd %ymm0, -64(%rax)
    vaddpd  -32(%rax), %ymm1, %ymm0
    vmovapd %ymm0, -32(%rax)
    cmpq    %rax, %rdx
    jne .L3
    vzeroupper
.L6:
    rep ret
addByCR(std::vector<Big, std::allocator<Big> >&, Big const&):
    movq    (%rdi), %rax
    movq    8(%rdi), %rdx
    cmpq    %rdx, %rax
    je  .L14
.L12:
    vmovapd 128(%rax), %ymm0
    addq    $256, %rax
    vaddpd  128(%rsi), %ymm0, %ymm0
    vmovapd %ymm0, -128(%rax)
    vmovapd -96(%rax), %ymm0
    vaddpd  160(%rsi), %ymm0, %ymm0
    vmovapd %ymm0, -96(%rax)
    vmovapd -64(%rax), %ymm0
    vaddpd  192(%rsi), %ymm0, %ymm0
    vmovapd %ymm0, -64(%rax)
    vmovapd -32(%rax), %ymm0
    vaddpd  224(%rsi), %ymm0, %ymm0
    vmovapd %ymm0, -32(%rax)
    cmpq    %rax, %rdx
    jne .L12
    vzeroupper
.L14:
    rep ret
informedCall(std::vector<Big, std::allocator<Big> >&):
    leaq    8(%rsp), %r10
    andq    $-32, %rsp
    pushq   -8(%r10)
    pushq   %rbp
    movq    %rsp, %rbp
    pushq   %r10
    pushq   %rbx
    movq    %rdi, %rbx
    leaq    -272(%rbp), %rdi
    subq    $256, %rsp
    call    opaqueFunction()
    movq    (%rbx), %rax
    movq    8(%rbx), %rdx
    vmovapd -144(%rbp), %ymm4
    vmovapd -112(%rbp), %ymm3
    cmpq    %rdx, %rax
    vmovapd -80(%rbp), %ymm2
    vmovapd -48(%rbp), %ymm1
    je  .L21
.L19:
    vaddpd  128(%rax), %ymm4, %ymm0
    addq    $256, %rax
    vmovapd %ymm0, -128(%rax)
    vaddpd  -96(%rax), %ymm3, %ymm0
    vmovapd %ymm0, -96(%rax)
    vaddpd  -64(%rax), %ymm2, %ymm0
    vmovapd %ymm0, -64(%rax)
    vaddpd  -32(%rax), %ymm1, %ymm0
    vmovapd %ymm0, -32(%rax)
    cmpq    %rax, %rdx
    jne .L19
.L21:
    vzeroupper
    addq    $256, %rsp
    popq    %rbx
    popq    %r10
    popq    %rbp
    leaq    -8(%r10), %rsp
    ret
uninformedCall(std::vector<Big, std::allocator<Big> >&):
    leaq    8(%rsp), %r10
    andq    $-32, %rsp
    pushq   -8(%r10)
    pushq   %rbp
    movq    %rsp, %rbp
    pushq   %r12
    pushq   %r10
    pushq   %rbx
    leaq    -304(%rbp), %rbx
    movq    %rdi, %r12
    subq    $280, %rsp
    movq    %rbx, %rdi
    call    opaqueFunction()
    subq    $256, %rsp
    movq    %rbx, %rsi
    movl    $32, %ecx
    movq    %rsp, %rdi
    rep movsq
    movq    %r12, %rdi
    call    opaqueAdd(std::vector<Big, std::allocator<Big> >&, Big)
    addq    $256, %rsp
    leaq    -24(%rbp), %rsp
    popq    %rbx
    popq    %r10
    popq    %r12
    popq    %rbp
    leaq    -8(%r10), %rsp
    ret
The resulting lines 6 to 9 showing registers ymm4 to ymm1 initialized to a, b, c, d through stack addresses prove that the argument addendum to add was copied to the stack for the call. addByCR shows what we expect, the additions are performed directly from memory all the time, but there is no copying (the lines that follow the pattern vaddpd 128(%rsi), %ymm0, %ymm0, with constants 160, 192, 224. Is this good? Before answering, notice that there are two differences between passing by value and by const reference in this case of big arguments:
  • There is at least some copying to the stack versus no copying,
  • and the compiler may decide to use registers locally (as shown in lines 6 to 9) despite the fact that the arguments are in memory to do the work in the case of passing by value but not in the case of passing by reference. That is, with large types, passing by value is actually a hybrid between theoretical passing by value and passing by const reference.
What about the other 32 integers in the member otherSuff? those are 128 bytes that must be copied to the stack, right? this is what passing by const reference tries to avoid, it has got to be expensive.

We don’t yet see anything about otherStuff because the callee does not care about it, then let us study what the compiler did for two cases of callers:
  • when the caller knows the implementation of the function, informedCall,
  • and when it does not, uninformedCall.
informedCall calls add, and it knows its implementation, as well as the implementation of the copy constructor for Big, which lets it perform the inlining we see, where those 128 bytes are not copied, all that is done with memory is to load to registers the members ad produced by opaqueFunction in lines 63, 64, 66 and 68. In the ‘uninformed’ case, lines 105 to 107 show the copy of the whole value, 32 8-byte things (this is using the move string operation, which roughly translated means while(rcx--) { *rdi++ = *rsi++; }, with pointed-to sizes of 8, remember the register rsi contains the address of the addendum). But the informed call did not copy everything, thus, passing by value a type that respects value semantics to a function whose implementation is known by the compiler won’t suffer from unnecessary copying nor aliasing! this is especially important because the great mayority of template function calls are such that the caller has the implementation available!

This observation can be leveraged in practice very easily:
  • In template implementations residing in headers that users will #include, if the copy constructor is sane, like it does not have side effects, you can almost be certain at most the things that matter will be copied and this only after the compiler determines the copy necessary or advantageous. I have gotten confident about passing by value to implementation-known templates because I am very strict about the copy constructors I program --and lazy!, since I very seldom use anything but the default!–
  • If you want to keep your implementation secret, before falling back to const referencing, you can ask:
    • If the function messes around with all or almost all of the argument, then pass by value since the copy construction prevented by const referencing will be done piece-meal anyway in the body
    • If the function will use only a small subset of the fields, isn’t the function telling you that its interface may be wrong, because it receives too much?
      • If the function is indeed receiving the whole tropical rainforest when it only requires a banana you have a problem.
      • But sometimes your interface can legitimately require the whole tropical rainforest because whether you only use a banana may be an implementation detail of no concern to the user, or something that may change in the future. Let us explore this case in more detail, because there may be a way:
Let’s say you have a big type, WholeKitchen, and your design legitimately requires your secretSauce function to receive it all. Furthermore, you neither want to provide the implementation. This is the traditional signature one would arrive at:
Something secretSauce(const WholeKitchen &kitchen);
And it may be fine, but could this be acceptable?:
Something notAsSecretASauce(Stove s, Water w, TomatoContainer tc, Oil o, Salt s);

inline Something secretSauce(WholeKitchen kitchen) {
    auto &k = kitchen;
    return notAsSecretASauce(k.stove, k.water, k.tomatoes, k.oliveOil, k.salt);
}
That way you can call ‘secretSauce’ repeatedly, never suffer aliasing, decimate the chances for false sharing, be assured the value won’t change and keep the option to change the implementation without breaking user code. It works, because as illustrated above, the inlining will extract or copy in the most advantageous way the members actually used in the function that does the work. Observe also that the extra work performed to pass by value may actually improve performance, since the split between secretSauce and notAsSecretASauce did not introduce any performance penalty, but allows the compiler to optimize for the members stove, …, salt; furthermore, if some type in notAsSecretASauce should be expensive to copy, the same technique could be applied, potentially ending up with only small parameters fully optimized, simply a side effect of letting the compiler know more.

Summarizing, for value semantics types and templates, one ought to ask oneself, “do I really want to pass by const reference?”

I discovered these facts about a couple of years ago, and got serious about it especially after I read “Want Speed? Pass by Value” by Dave Abrahams --the original is not available anymore, but the WayBackMachine has a copy. It’s not that it is a done deal, the language semantics today sometimes conspire against passing by value, and there are other issues, the thing is that I have gained experience at aggressively switching from the traditional passing by const reference to passing by value, and I can say that without a doubt, I’ve gotten code that is much easier to understand, because nothing beats passing by value with regards to clarity, especially because it is of great assistance to reduce mutability, and performs better.

When I have had to keep const references it usually is because of code I can’t change that relies on side effects, my function arguments need to reflect changes made elsewhere or indirectly.

I have developed aversion to code that unnecessarily uses references and mutability; it used to be equivalent, but not anymore. I don't think I am alone in this, so, if you want to make not just future-proof, but present-proof code, be more strict about your use of references and mutability.

Also, C++ 11 improves dramatically the support for passing by value, all of these things make for lots of things to talk about in the following articles.

It may be easy to misinterpret a point of this article as saying that passing by value works better if things are inlined aggressively. Inlining aggressively might be performance-detrimental, the warning to not be penny-wise on the passing by value and dollar-foolish on over-inlining needs mentioning. What I have seen, from the point of view of passing by value, is that it reinforces mutually with value semantics, in value semantics the copy constructor tends to be the default bitwise copy, that allows the compiler to reason about the members and do very clever optimizations. As always, inline only what makes sense.

Monday, November 2, 2015

Aliasing and autovectorization

In the previous article, “Complications” I mentioned an example of a serial algorithm, incrementAll, that supposedly gets converted by the compiler into some code that operates in parallel.

We will see that such a thing happens with two of the best compiler suites of all time, CLANG and GCC.

Modern architectures have vector capabilities. For example, the Intel x86-64 has the SSE extensions, which currently have reached 32 registers of 512 bits (16 single-precision floating point elements). Most x86-64 in use today have 16 256 bit registers. I should say Intel’s SSE is the weirdest SIMD instruction set that I know of and that it has severe design errors, at the opportune moment I hope to comment on this in full, but the most popular architecture in the world today has the vector instruction set that it has and because despite design errors it is highly performing, we ought to learn about it.

Modern compilers also know how to automatically transform serial code into vector operations, even with the notoriously hard SSE. Let us see a version of incrementAll more suitable to illustrate autovectorization:
using BigThing = float;

#include <vector>

// note: passing by value, not const reference ==> no aliasing
void incrementAll(BigThing *ptr, BigThing value) {
    ptr = reinterpret_cast<BigThing *>(__builtin_assume_aligned(ptr, 32));
        // note: it is not safe to make the assumption, done here to prevent
        // the complications of handling the alignment
    for(auto i = 0; i < 1024; ++i) { ptr[i] += value; }
}

There are two changes: An array is passed instead of a vector, which is assumed to be aligned to 32 byte boundaries, and the length of the vector is fixed at 1024 (2^10) elements. Still, the source code as written specifies a serial application of += to each element in the array.

Using the stupendous tool “Online Interactive Compiler Explorer” by M. Godbolt we can readily see what GCC 5.2 does with that code, with color highlighting and all, repeated here:
incrementAll(float*, float):
 vbroadcastss %xmm0, %ymm1
 leaq 4096(%rdi), %rax
.L2:
 vaddps (%rdi), %ymm1, %ymm0
 addq $32, %rdi
 vmovaps %ymm0, -32(%rdi)
 cmpq %rdi, %rax
 jne .L2
 vzeroupper
 ret

Which I can traduce for you if you want:
  • Line 2 means copy the least significant float in vector register 0 into the 8 positions of register ymm1, xmm0 is obviously the argument value, as the x86-64 ABI specifies it should be (first SSE parameter)
  • Line 3 calculates ptr + 4096 and puts it in scalar register rax (rdi is the first scalar parameter, ptr)
  • Line 4 adds 8 floats at a time from *ptr into ymm0 (xmm0 and ymm0 are the same register, just viewed as 16 or 32 bytes)
  • Since the iterations happen 8 floats at a time, the pointer is incremented by 4*8 = 32 in line 6
  • The result is copied back
  • Lines 9-10 control the loop
  • Line 10 is an artifact of the ABI, that it is required to clear the upper 128 bits of all YMM registers
As you can see, only asking GCC to optimize for speed (-03) and let it use AVX, the name for the 256 revision of SSE (-mavx, -mavx2), it goes and transform the serial code into doing 8 floats in parallel.  AVX is not needed, you may remove those options and you'd still get vectorization on 128 bit registers.

Clang is even more radical, because it will also unroll the loop four times, that is, its iterations consume 32 floats!. This is a digression, but I suspect this unrolling is actually a pessimization: The processor has only so many execution units, I don’t think it is capable of performing four 256-wide vector additions in parallel, but even if it is capable, the rules of branch prediction say the execution will loop back, which means that as a matter of course the processor itself will pipeline the execution of many iterations simultaneously, which in effect is executing as many additions in parallel as it is capable, but without unnecessarily longer assembler code that reduces the effectiveness of the code cache.

If the assumption for aligment at 32 bytes is removed, you can also see the output from the ICC, which will also vectorize automatically. So, it is proven that the compilers are capable of automatic vectorization.

Just for kicks, let’s do a very simple change, instead of passing value by value, let us pass it by const reference: Clang simply turns off vectorization.

GCC still vectorizes, but only in the optimistic case of the location of value not within the span of the array:
incrementAll(float*, float const&):
 leaq 4(%rsi), %rdx
 leaq 4096(%rdi), %rax
 cmpq %rdx, %rdi
 jnb .L7
 cmpq %rax, %rsi
 jb .L6
.L7:
 vbroadcastss (%rsi), %ymm1
.L5:
 vaddps (%rdi), %ymm1, %ymm0
 addq $32, %rdi
 vmovaps %ymm0, -32(%rdi)
 cmpq %rax, %rdi
 jne .L5
 vzeroupper
 ret
.L6:
 vmovss (%rdi), %xmm0
 addq $4, %rdi
 vaddss (%rsi), %xmm0, %xmm0
 vmovss %xmm0, -4(%rdi)
 cmpq %rdi, %rax
 jne .L6
 rep ret
  • Line 2 means rdx = &value + 1
  • Line 3: rax = ptr + 1024
  • Lines 4-5 mean if(&value < ptr) { goto L7; } // <- do the vector loop
  • Lines 6-7 mean if(&value < ptr + 1024) { goto L6; } // <- do the scalar loop
  • lines 9-17 are the vectorized loop
  • lines 19-25 are the serial loop
Unlike the code that I suggested in my previous article, you can see that GCC gives up on vectorization if &value is within the span of the array. According to Eric Brumer, a developer of the Microsoft compiler, at his “Compiler Confidential” presentation, the Microsoft compiler will keep trying to vectorize, getting to perform very complicated case analysis.

Of course that one should never pass by const reference what could be passed by value without incurring the cost of a copy, but this shows what happens if you do, the compiler is forced to deal with aliasing and no matter what, performance is lost, even if just to make the now necessary checks to detect aliasing.

There is one case in which C++ programmers routinely indicate passing by const reference, even when the arguments may be as small as simple floating point:  In templates, when the actual type is not known at programming time; to prevent problems of potentially very expensive copy constructors we have been defaulting for more than 20 years to const references (or pointers).

What is the solution?  It is not practical to cover the known cases in which passing by value is superior with template specializations.  The answers I have devised thus far are incomplete, but lead to code that is both higher performing and easier to understand.  I will be writing about them.