我有一个函数在我的项目中被调用了很多次:
void foo(int bar)
{
char arr[1024];
//...do some operation on arr according to value of bar
}
现在,在一些罕见的情况下,当bar
的值较大时,我会遇到分割错误。我需要增加arr
的大小,我可以从bar
的值中知道它的大小。
显而易见的解决方案似乎是根据bar
的大小为arr
动态分配内存。然而,这个函数调用频率很高,我认为每次分配内存都会降低性能。
我应该采取什么策略来解决这个问题?
两个建议:
- 动态分配,但重用缓冲区。所以
foo
也会从调用函数中获得一个缓冲区作为参数(例如,如果在循环中从同一个函数调用。如果从100个不同的地方调用foo
,我不会在整个程序中传递缓冲区(。这样,您只需要在需要增加缓冲区时进行分配 - 像现在一样声明一个大的本地数组,但在使用之前要确保它足够大。如果不是,请在堆上动态分配一个。假设本地数组在大多数情况下都足够大,那么您只会在极少数情况下在堆上进行分配
编辑:
关于#2,@David Heffernan因提出相同的选项而受到指责,因为这可能是代码的复杂性。我不认为以下内容很复杂:
void foo(int bar)
{
char localArr[1024];
char* arr = localArr;
if (sizeNeeded > 1024)
arr = malloc(sizeNeeded);
// ... use arr in logic ...
if (arr != localArr)
free(arr);
}
我在一些频繁调用的回调中使用了类似的代码,在这些回调中我无法重用缓冲区,在大多数情况下消除malloc无疑提高了性能。我真的不知道这是否是OP的最佳解决方案。
如果堆分配性能是个问题,那么您可以在bar<1024,但在其他情况下使用堆分配的缓冲区。
我建议分析你的应用程序,看看使用堆分配是否真的比堆栈上的常量大小数组慢。现代的土堆堆得很好。除非有显著的好处,否则不要以混淆代码的方式优化代码。
void foo(int bar) {
char arr[1024];
// ...
}
首先,您需要解决运行时错误,然后再处理性能问题。根据您提供的信息,bar
和数组arr
的大小之间没有关系。您遇到分段错误,可能是因为您试图访问的位置不是从0到1023。如果你想要的是bar
长度的数组,那么malloc
是它,free
是它,当你不再需要它时。
如果bar可能大于1024,则应坚持分配内存。此外,在堆栈中分配1kb可能不是一个好主意,特别是如果你的函数可能被递归调用(我想它不是(。保留以前调用的分配,或者使用已分配缓冲区池,以避免在以前的分配足够的情况下释放和重新分配内存。准备在并发调用foo时遇到麻烦。
允许用户提供缓冲区,这样,如果在循环中调用此函数,则用户可以构造一次缓冲区(在循环之前(,并在每次迭代中传递相同的缓冲区。即,用途:
// Preconditions:
// buffer_ptr MUST NOT be NULL.
// buffer_len_ptr MUST NOT be NULL.
//
// Parameters:
// bar -- Whatever bar does
// buffer_ptr -- Points to a pointer to a malloc-allocated buffer.
// buffer_len_ptr -- Points to a variable indicating the size of *buffer_ptr
//
// Side-Effects:
// May expand the buffer, causing *buffer_ptr to point to a new, larger
// buffer, in which case *buffer_len_ptr will have the new size.
//
void foo(int bar, char** buffer_ptr, size_t* buffer_len_ptr);
我将执行以下操作。
void foo_inner(size_t bar, char arr[bar]);
在原型中声明arr
为数组只是化妆品,但它清楚地表明了您的意图。(另一种可能性是让char arr[static 1]
强制它成为非空指针。(
然后,我会为调用方手头没有数组的用例编写一个宏
#define foo(BAR)
if (1) {
size_t foo_bar = BAR;
char foo_arr[foo_bar];
foo_inner(foo_bar, foo_arr);
} else (void)0
或
#define foo(BAR)
if (1) {
size_t foo_bar = BAR;
char * foo_arr = malloc(foo_bar);
if(foo_arr) foo_inner(foo_bar, foo_arr);
free(foo_arr);
} else (void)0
这取决于你是否认为VLA对你来说是安全的。
然后,在调用站点上,您可以选择在您确信不会造成太大伤害的地方调用foo
,并通过预先分配缓冲区来优化循环内的调用。