Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overhead of static constants when included into many translation units?

In a header file, global constants can be declared and (pre)defined in a single line.

// constants.h
namespace Constant{
    static const unsigned int framerate = 60;
    static const char * const windowName = "Test";
    static const unsigned char * const cursorBitmap = { lots of data };
}

I like this format because it lets me keep my constants in one place, and avoids needing to declare the constant in one file and define it in another, helping with readability. When any translation unit includes constants.h however, it expands these definitions in place, per unit.

My question is, will this cause significant overhead if I include constants.h into many translation units and, for instance, cursorBitmap and other array constants are significantly large? Will my program contain 100 copies of each string and array literal if I include it into 100 units? Or will only the pointers and values be copied?

If there is overhead, is there a way I can avoid it without needing to separate declaration and definition?

(also I'm guessing the 'static' is redundant in this usage, but I like to put it there anyway)

like image 756
Anne Quinn Avatar asked Jan 12 '16 09:01

Anne Quinn


2 Answers

Whether string literals are duplicated in the various translation units is a Quality of Implementation issue.

The directly declared objects will be duplicated in each translation unit where this header is included. It's not much though. And in a translation unit where some constant's address isn't directly or indirectly used, it may be optimized away.

If you want to ensure only one copy of each constant, or even no copy, then you can use a class template, like this:

constants.h
#pragma once

template< class Dummy >
struct Constant_{
    static const unsigned int framerate;
    static const char * const windowName;
    static const unsigned char * const cursorBitmap;
};

template< class Dummy >
const unsigned int Constant_<Dummy>::framerate = 60;

template< class Dummy >
const char * const Constant_<Dummy::windowName = "Test";

template< class Dummy >
const unsigned char * const Constant_<Dummy>::cursorBitmap = ...;

using Constant = Constant_<void>;

But it's IMHO more work than it's worth.

A similar alternative is to use inline functions, one for each constant.

like image 118
Cheers and hth. - Alf Avatar answered Nov 13 '22 23:11

Cheers and hth. - Alf


Will my program contain 100 copies of each string and array literal if I include it into 100 units? Or will only the pointers and values be copied?

The standard does not promise consolidation of string literals, so it's up to the implementation. A simple test using GCC 5.1.1 on GNU/Linux showed that the string literals are not consolidated in an unoptimized build, but are consolidated when -O or -Os is used. But that is just merging the actual char arrays. To the extent that the compiler doesn't optimize away the storage for the pointer, or for the numerical constants (if they are local to the translation unit, not ODR-used, and of POD type, they are obvious candidates for elimination under the as-if rule), however, the compiler may not be able to consolidate them easily. The standard requires them to be different objects and hence must have different addresses. The as-if rule may still allow their removal, even if you were to take their addresses, but this would generally require global program optimization aka link-time optimization, including libraries, that the implementation may not support, support only in a limited fashion, and/or only depending on compiler and linker settings. In other words, it just might happen under the right circumstances, but it is much more likely not to happen.

My own test indicates that GCC 5.1.1 does not consolidate static const unsigned int objects exposed by const ref, even with -Os -flto (optimize for size and enable link-time optimization). I would frankly be surprised if any contemporary implementation did perform this difficult and obscure optimization.

(also I'm guessing the 'static' is redundant in this usage, but I like to put it there anyway)

It's not redundant if you have multiple translation units because you would otherwise run afoul of the one definition rule (ODR). As a side note, however, static at namespace scope has been considered syntactically obsolete for a long time (consider using anonymous namespaces, introduced in C++98, instead).

(in response to Cheers and hth. - Alf)

If you want to ensure only one copy of each constant, or even no copy, then you can use a class template, like this:

No such luck. There's no guarantee in the standard about how much space is used for the templates. All the template does guarantee is that only one of the potentially many copies is used – or appears to be used, under the as-if rule. In fact, it's worse than that because at least GCC 5.1.1 in fact does not remove a redundant static const unsigned int even with -Os -flto on my system. This means with two translation units, the initializer value for the unsigned int can be found in two separate locations, even though only one of them is used (all pointers and references refer to this location only).

like image 44
Arne Vogel Avatar answered Nov 13 '22 23:11

Arne Vogel