Efficiently generating byte buffer without breakin

2020-07-24 06:38发布

问题:

This is such a simple pattern, there has to be a "nice" way of sorting it out.

I have a function that needs to generate a dynamically sized byte array containing arithmetic data.

// Given that I have a function that kinda looks like this:
void dispatch(std::vector<char> data); //Will take possesion of data.

// The behavior I want, but this breaks strict aliasing
void bad_foo(int c) {
  std::vector<char> real_data(c * sizeof(float));
  float* raw_data = reinterpret_cast<float*>(real_data.data());

  //Fill raw_data with usefull stuff...

  dispatch(std::move(real_data));
}

void correct_but_slow_foo(int c) {
  std::vector<float> raw_data(c);

  //Fill raw_data with usefull stuff...

  std::vector<char> real_data(c * sizeof(float));
  std::memcpy(real_data.data(), raw_data.data(), c * sizeof(float));

  dispatch(std::move(real_data));
}

Unfortunately, it seems even clang's heap elision is not managing to sort out what needs to be done here: see on godbolt

At the very worst, I can make dispatch() a template, but that would become very messy, and I'm curious to see if there is a way out of this mess I'm overlooking anyways.

Thanks!

Edit: A thought just crossed my mind (immediately after posting the question of course...) : I could treat real_data as an allocation pool and in-place new the arithmetic data on top of it:

void fixed_foo(int c) {
  std::vector<char> real_data(c * sizeof(float));
  float* raw_data = new (real_data.data()) float[c];

  //Fill raw_data with usefull stuff...

  dispatch(std::move(real_data));
}

This looks funky as hell, but I "think" it might be legal. Maybe?

回答1:

The safest way to get around aliasing rules is to use memcpy() but you don't need to do that on a whole second copy of the data. I'd suggest doing all your float work on a local float variable and then memcpy()ing that to the appropriate location in your real_data buffer an element at a time. Most compilers will optimize that effectively in my experience.

void better_foo(int c) {
  std::vector<char> real_data(c * sizeof(float));

  //Fill raw_data with usefull stuff...
  for (int i = 0; i < c; ++i) {
    float x = my_complicated_calculation(i);
    memcpy(&real_data[i * sizeof(float)], &x, sizeof(x));
  }

  dispatch(std::move(real_data));
}