Some of nowaday C++ code tend to be template-loaded in greatest extent. They are libraries: STL, Boost.Spirit, Boost.MPL, etc among many other. They encourages users to declare functional objects in form struct S { /* presence of non-virtual member functions and operators, but absense of non-static data members or non-empty base classes */ }; S const s{};
. Most of them is stateless (i.e. static_assert(std::is_empty< S >{});
holds). For those of them, which is ODR-used, regardless of theirs emptyness data
section of file growth by 1 byte (sizeof(S) == 1
for empty type S
because all addresses should be different). Even in simple grammars of Boost.Spirit there is a plenty of such ODR-used empty classes. But it is absolutely of no sense to keep space for them.
I tried to test clang
on coliru using following code (-Ofast
):
#include <utility>
#include <type_traits>
#include <cstdlib>
#include <cassert>
template< std::size_t index >
struct S {};
namespace
{
template< std::size_t index >
S< index > value = {};
}
template< typename lhs, typename rhs >
std::ptrdiff_t
diff(lhs & l, rhs & r)
{
return (static_cast< char * >(static_cast< void * >(&r)) - static_cast< char * >(static_cast< void * >(&l)));
}
template< std::size_t base, std::size_t ...indices >
std::ptrdiff_t
bss_check(std::index_sequence< indices... >)
{
return (diff(value< (base + indices) >, value< (base + indices + 1) >) + ...);
}
template< std::size_t size, std::size_t base >
bool
enumerate()
{
return (bss_check< base >(std::make_index_sequence< size >{}) + 1 == size);
}
template< std::size_t size, std::size_t ...bases >
bool
expand(std::index_sequence< bases... >)
{
return (enumerate< size, (bases * size) >() && ...);
}
template< std::size_t size = 100, std::size_t count = size >
bool
check()
{
return expand< size >(std::make_index_sequence< count >{});
}
int
main()
{
static_assert(std::is_empty< S< 0 > >{});
assert((check< DIM >()));
return EXIT_SUCCESS;
}
and get the result (output of size
utility for DIM == 100
, i.e. 100 * 100 classes):
text data bss dec hex filename
112724 10612 4 123340 1e1cc ./a.out
If I change signature of diff(lhs & l, rhs & r)
to diff(lhs l, rhs r)
in order to suppress ODR-using, then result is:
text data bss dec hex filename
69140 608 8 69756 1107c ./a.out
Is almost equal to (data
section is only of interest) the case of simple commenting of assert((check< DIM >()));
line (major part of text
section is predictable DCE-optimized out):
text data bss dec hex filename
1451 600 8 2059 80b ./a.out
Hence I conclude that there is no optimization for ODR-used empty classes.
For explicitly specified template parameters there is possibility to use a simple typefilter:
template< typename type >
using ref_or_value = std::conditional_t< std::is_empty< std::decay_t< type > >{}, std::decay_t< type >, type && >;
But there is no simple workaround for deduced template types at my mind.
Is there described above optimization in modern compilers? If yes, how to enable it? If no, is there a technique to achieve desired behaviour at the moment?
I know somtimes addresses of objects mutters much, but it is not the case in described above situation.
I think something like attribute for variable or type (e.g. [[immaterial]]
) would be handy.
Aucun commentaire:
Enregistrer un commentaire