如何避免动态分配内存以节省时间?

时间:2011-09-11 06:27:21

标签: c string performance malloc

我有一个在我的项目中多次调用的函数:

void foo(int bar)
{  
    char arr[1024];
    //...do some operation on arr according to value of bar  
}

现在,在极少数情况下,当bar的值很大时,我会遇到分段错误。我需要增加arr的大小,我可以从bar的值知道其大小。

显而易见的解决方案似乎根据arr的大小为bar动态分配内存。但是,这个函数被频繁调用,我认为每次分配内存会降低性能。

我应该采取什么策略来解决这个问题?

6 个答案:

答案 0 :(得分:8)

两个建议:

  1. 动态分配,但重用缓冲区。所以foo也会从调用函数中获取一个缓冲区作为参数(例如,如果从循环中的相同函数调用,那么。如果foovoid foo(int bar) { char localArr[1024]; char* arr = localArr; if (sizeNeeded > 1024) arr = malloc(sizeNeeded); // ... use arr in logic ... if (arr != localArr) free(arr); } ,我不会在整个程序中传递缓冲区。从100个不同的地方打来的电话。这样,只需要在缓冲区需要增加后进行分配。
  2. 像现在一样声明一个大的本地数组,但要确保它在使用之前足够大。如果没有,请在堆上动态分配一个。假设本地数组对于大多数情况来说足够大,那么在极少数情况下你只会在堆上进行分配。
  3. 编辑:

    关于#2,@ David Heffernan建议使用相同的选项,因为这可能是代码的复杂问题。我认为以下内容并不复杂:

    {{1}}

    我在一些经常调用的回调中使用了类似的代码,我无法重用缓冲区,并且在大多数情况下消除malloc肯定会提高性能。这是否是OP的最佳解决方案我真的不知道。

答案 1 :(得分:3)

如果堆分配性能存在问题,那么您可以在bar< 1024时保留堆栈分配的缓冲区,否则使用堆分配的缓冲区。

我建议您分析您的应用,看看使用堆分配实际上是否明显慢于堆栈上的常量大小数组。现代堆很好。除非有明显的好处,否则不要以混淆代码的方式优化代码。

答案 2 :(得分:3)

void foo(int bar) {
    char arr[1024];
    // ...
}

首先,您需要解决运行时错误,然后解决性能问题。根据您提供的信息,bar与数组arr的大小之间没有关系。您可能因为尝试访问的位置不是 0到1023 而导致分段错误。如果你想要的是bar长度的数组,那么当你不再需要它时malloc它和free它。

答案 3 :(得分:2)

如果bar有可能大于1024,你应该坚持分配内存。此外,在堆栈中分配1kb可能不是一个好主意,特别是如果你的函数可以递归调用(我猜它没有)。保留先前调用的分配,或使用已分配缓冲区池,以避免在先前分配足够的情况下释放和重新分配内存。准备在并发呼叫foo时遇到麻烦。

答案 4 :(得分:1)

允许用户提供缓冲区,这样,如果在循环中调用此函数,用户可以构造缓冲区一次(在循环之前)并在每次迭代时传递相同的缓冲区。即,使用:

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

答案 5 :(得分:0)

我会做以下事情。

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,并通过预先分配缓冲区来优化循环内部的呼叫。