在不破坏严格别名的情况下有效地生成字节缓冲区

Posted

技术标签:

【中文标题】在不破坏严格别名的情况下有效地生成字节缓冲区【英文标题】:Efficiently generating byte buffer without breaking strict aliasing 【发布时间】:2018-04-08 04:08:04 【问题描述】:

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

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

// 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 的堆省略也无法理清这里需要做什么:see on 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));

这看起来很时髦,但我“认为”它可能是合法的。也许?

【问题讨论】:

dispatch 将如何处理 data?如果它以floatchar 访问它,我认为没有问题。 @geza 不管 dispatch 对数据做什么,bad_foo() 做什么本身就是一种违规。 我不确定情况是否如此。您只能以float 访问该内存。它真的违反了严格的别名规则吗? (我不是说没有,我只是持怀疑态度) @geza 规则很清楚,char 异常允许您将标准布局对象强制转换为char,但不能反过来,除非它是该类型的对象第一名。 @geza 我刚刚意识到我从来没有真正回答过你的问题,对不起。 dispatch() 最终会导致数据被 DMAd 到 GPU,使用类似 glBufferSubData() 的东西。 【参考方案1】:

绕过别名规则的最安全方法是使用memcpy(),但您不需要在数据的第二个副本上这样做。我建议您在本地 float 变量上完成所有 float 工作,然后将 memcpy()ing 到您的 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));

【讨论】:

很高兴您提出了这个选项,尽管如果您使用一些需要 float* 并适用于整个数据集的数字库,它可能无济于事。 谢谢你,在许多情况下这确实是一个非常有用的解决方法,但正如@BenVoigt 所提到的,这并不是我理想中需要的“完全”。

以上是关于在不破坏严格别名的情况下有效地生成字节缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

如何在不复制列标签的情况下将多个数据框写入同一张表

dequeueBuffer: 不能在不设置缓冲区计数的情况下使多个缓冲区出队

在不使用 readfile() 的情况下检测 Windows 句柄上的空缓冲区

Offbyone 缓冲区溢出有效负载中的 NULL 字节

如何可靠地使 send(2) 进行短发送?

如何在不为其分配内存的情况下将缓冲区传递给 write()