高效生成字节缓冲区,而不会破坏严格的混叠



这是一个如此简单的模式,必须有一种"好"的方法来整理它。

我有一个函数需要生成一个包含算术数据的动态大小的字节数组。

// 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));
}

不幸的是,似乎即使是 clang 的堆 elision 也无法理清这里需要做什么:见 godbolt

在最坏的情况下,我可以dispatch()做一个模板,但这会变得非常混乱,我很好奇是否有办法摆脱我所忽略的这个混乱。

谢谢!

编辑:我脑海中闪过一个想法(当然是在发布问题后立即......):我可以real_data视为分配池,并在它上面就地添加新的算术数据:

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));
}

这看起来很时髦,但我"认为"这可能是合法的。或?

绕过别名规则的最安全方法是使用memcpy()但不需要在数据的整个第二个副本上执行此操作。我建议在局部float变量上完成所有float工作,然后将其memcpy()real_data缓冲区中的适当位置,一次缓冲一个元素。根据我的经验,大多数编译器都会有效地优化它。

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));
}

最新更新