可变长度数组对性能的影响(C/C++)

3
我正在编写一个相当简单的函数,将数组发送到文件描述符。但是,为了发送数据,我需要附加一个一字节的标头。
以下是我正在做的简化版本,它似乎可以工作:
void SendData(uint8_t* buffer, size_t length) {
  uint8_t buffer_to_send[length + 1];
  buffer_to_send[0] = MY_SPECIAL_BYTE;
  memcpy(buffer_to_send + 1, buffer, length);
  // more code to send the buffer_to_send goes here...
}

就像我之前所说的,代码似乎运行良好,但是最近我开始使用Google C++风格指南,因为我的当前项目没有针对它的设定样式指南(实际上,我是该项目中唯一的软件工程师,我想使用一些在工业中使用的东西)。我运行了Google的cpplint.py,它捕捉到了我正在创建buffer_to_send的那一行,并且发出了一些关于不使用可变长度数组的评论。具体来说,这是Google的C++风格指南对可变长度数组的看法...

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Variable-Length_Arrays_and_alloca__

根据他们的评论,似乎我已经找到了代码中看似随机但却很少发生的崩溃的根本原因(尽管这些崩溃令人烦恼)。然而,我还是有些犹豫如何修复它。
以下是我的解决方案:
  1. 将buffer_to_send制作成一个定长的固定长度数组。可能会遇到的问题是,为了发送理论上最大的缓冲区大小,我必须将缓冲区设得足够大。在平均情况下,缓冲区要小得多,每次调用函数时都会浪费大约0.5KB。请注意,程序必须在嵌入式系统上运行,虽然我不一定计算每个字节,但我希望尽可能少地使用内存。

  2. 使用new和delete或malloc/free来动态分配缓冲区。这里的问题是,该函数被频繁调用,会有一些开销,因为需要不断向操作系统请求内存,然后释放。

  3. 使用两个连续的write()调用,以便将数据传递给文件描述符。也就是说,第一个write()只传递一个字节,下一个write()会发送剩余的缓冲区。虽然看起来很简单,但我需要更深入地研究代码(请注意,我从前任工程师手中接过这段代码,他已经离开了我们公司),以确保这两个连续的写操作是原子性的。此外,如果这需要锁定,则它实际上比方案#2更复杂,并且具有更大的性能影响。

请注意,由于在任何给定时间内可能会从各个线程调用该函数,因此无法使buffer_to_send成为成员变量或将其范围限定在函数外部。
请告诉我您的意见以及我应采取的首选方法。感谢您的时间。

文件描述符背后是什么?多个写入调用的影响取决于目标。此外,您是否有长度上的“硬”限制? - DrC
我的长度硬限制很大,我不想每次调用函数时都分配大量不必要的内存。此外,文件描述符本质上是与串行设备建立的连接。 - It'sPete
1
你考虑过使用 writev() 吗?它旨在将多个缓冲区拼接成一个,使用与 write() 函数相同的方式。 - Dietmar Kühl
这是一个叶子函数吗?如果是的话,您就不必太担心避免大量堆栈分配。 - Ben Voigt
2个回答

3

正是我所需要的!感谢你向我介绍了另一个我从未知道存在的内置函数,但现在我绝对离不开它! - It'sPete

0
我会选择选项1。如果你知道数据的最大长度,那么可以使用固定大小的数组,在堆栈上分配那么多空间(再加上一个字节)。这比你展示的可变长度数组并不差,因为你必须始终在堆栈上留有足够的空间,否则你就无法处理你的最大长度(在最坏的情况下,你的代码会在较大的缓冲区大小上随机崩溃)。在调用此函数时,没有其他东西会使用你堆栈上的进一步空间,因此安全起见可以分配固定大小的数组。

“int x[length+1]”被分配在栈上还是堆上? - Mike Makuch
1
@koodawg:像那样的可变长度数组是在自动存储(堆栈)中分配的。 - Greg Hewgill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接