Memcpy is not optimized during the "quick acne" attempt

I need to use a very large and complex class just for the title (I think boost :: multiprecision :: cpp_bin_float <76>, called BHPbelow), which I would like to hide behind an implementation like pimpl, to reduce compilation time in a somewhat large project (replacing the Boost class std::complex<double>reduced compilation time by about 50%).

However, I would like to avoid dynamic memory allocations. Therefore, something like this seems natural (ignoring the alignment problems at the moment, which can be avoided with aligned_storageor alignas):

struct Hidden {
  char data[sz];

  Hidden& punned(Hidden const& other);
};

Hidden::punnedcan then be defined in one translation block to be dropped datain BHP*, act on it and not pollute all other translation units with 170k LOC header files. Possible implementation may be

Hidden& Hidden::punned(Hidden const& other) {
  *(BHP*)(data) += *(BHP*)(other.data);
  return *this;
}

This, of course, is undefined behavior, because we access the type object BHPusing a type pointer char, thereby violating the strict rules of aliases. The correct way to do this is:

Hidden& Hidden::proper(Hidden const& other) {
  BHP tmp; std::memcpy(&tmp, data, sz);
  BHP tmp2; std::memcpy(&tmp2, other.data, sz);
  tmp += tmp2;
  std::memcpy(data, &tmp, sz);
  return *this;
}

Now it may seem “obvious that these challenges memcpycan be optimized. Unfortunately, this is not so, they remain and do proper()much more than that punned().

, : a) Hidden b) c) d) , .

godbolt; , (GCC 4.9 - trunk, Clang 3.9, 4.0 5.0 Intel 18) memcpy. GCC (, 5.3) , . Ive Direct, BHP , , , .

:

#include <cstring>

constexpr std::size_t sz = 64;

struct Base {
  char foo[sz];
  Base& operator+=(Base const& other) { foo[0] += other.foo[0]; return *this; }
};
typedef Base BHP;

// or:
//#include <boost/multiprecision/cpp_bin_float.hpp>
//typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<76> > BHP;

struct Hidden {
  char data[sz];

  Hidden& proper(Hidden const& other);
  Hidden& punned(Hidden const& other);
};

Hidden& Hidden::proper(Hidden const& other) {
  BHP tmp; std::memcpy(&tmp, data, sz);
  BHP tmp2; std::memcpy(&tmp2, other.data, sz);
  tmp += tmp2;
  std::memcpy(data, &tmp, sz);
  return *this;
}

Hidden& Hidden::punned(Hidden const& other) {
  *(BHP*)(data) += *(BHP*)(other.data);
  return *this;
}

struct Direct {
  BHP member;
  Direct& direct(Direct const& other);
};

Direct& Direct::direct(Direct const& other) {
  member += other.member;
  return *this;
}

struct Pointer {
  char storage[sz];
  BHP* data;

  Pointer& also_ok(Pointer const& other);
};

Pointer& Pointer::also_ok(Pointer const& other) {
  *data += *other.data;
  return *this;
}
+4
1

, , undefined, BHP char.

. char* , BHP. , :

new (data) BHP(...);

:

*(BHP*)(data) += *(BHP*)(other.data);

, char alignas(BHP).

, gcc , char[], - std::aligned_storage_t.

+1

Source: https://habr.com/ru/post/1690792/


All Articles