【问题标题】:Is using malloc to allocate a buffer faster than using a statically allocated buffer when making large buffers?在制作大缓冲区时,使用 malloc 分配缓冲区是否比使用静态分配的缓冲区更快?
【发布时间】:2021-07-21 02:02:28
【问题描述】:

我正在尝试制作这样的数组:

char tmp[4][32768];
//code

这是在一个函数中,我发现将这个数组设为全局会提高程序的速度(不幸的是,我无法保持这样的状态,因为函数在遇到某些数据时会递归调用自身)。我想让这个功能更快,但我读到malloc 可能会很慢。那么是这样留下还是这样做会更快吗?:

char** tmp;
tmp = malloc(4 * sizeof(char*));
tmp[0] = malloc(32768);
tmp[1] = malloc(32768);
tmp[2] = malloc(32768);
tmp[3] = malloc(32768);
//code
free(tmp[0]);
free(tmp[1]);
free(tmp[2]);
free(tmp[3]);
free(tmp);

【问题讨论】:

  • 无法确定。您需要进行基准测试
  • tmp = malloc(4); 分配不足。你需要做tmp = malloc(4 * sizeof (char *));
  • @mediocrevegetable1 谢谢我忘了那个大声笑(我更新了帖子)
  • 如果你在一个关键代码中并且你的函数不是递归的,你可以在堆中预先分配一个大的线程本地缓冲区来减少执行 mallocs 的成本(但不要忘记释放它) .
  • 你的递归有限制吗?然后有一个简单的方法来解决堆栈分配问题。如果没有 - 您可以通过根据需要分配新数组来全局管理这些数组,并且在退出时只释放一次。最大分配数将是您的递归数。

标签: c performance memory


【解决方案1】:

在所有条件相同的情况下,通过malloc(或callocrealloc)分配内存将比使用auto(本地)数组更慢且更费力,后者会更慢而不是使用static 数组。

不过,所有事物通常都不是平等的。正如您所发现的,static 数组在处理递归时效果不佳(或者当您只需要保留多个状态时,因为任何尝试使用 strtok 来标记像 "a=b&c=d&e=f" 这样的字符串的人都知道太好)。 128K auto 数组有点偏大,如果函数是递归的,这可能是个问题,因为堆栈空间对于单个进程通常是有限的。

这是动态内存的理想用例,它通常不像堆栈上的内存那样有限。权衡是它会比替代方案更慢,并且需要您做更多的工作。

究竟慢多少取决于你如何分配和使用它——像这样将它分配为一个连续的块只需要一个malloc(和相应的free)调用,并且应该更适合本地化:

char (*tmp)[32768] = malloc( 4 * sizeof *tmp );
if ( tmp )
{
  for ( size_t i = 0; i < 4; i++ )
    strcpy( tmp[i], some_string ); // or however you intend to use tmp;
  ...
  free( tmp );
}

但如果您的堆足够分散,您可能无法将其分配为单个连续块,而必须使用多个 malloc(和 free)调用来零碎地进行分配:

char **tmp = malloc( 4 * sizeof *tmp );
if ( tmp )
{
  for ( size_t i = 0; i < 4; i++ )
  {
    tmp[i] = malloc( sizeof *tmp[i] * 32768 );
    if ( tmp[i] )
    {
      strcpy( tmp[i], some_string );
    }
  }
  ...
  for ( size_t i = 0; i < 4; i++ )
    free( tmp[i] );
  free( tmp );
}

确定哪个更快、哪个更适合您的目的的唯一方法是编写多个版本并衡量它们的性能。

说了这么多……

如果你的代码做错了事,或者给出了错误的答案,或者在输入错误的第一个提示时吐得满地都是,或者暴露了敏感数据,或者提供了一个入口点,那么无论你的代码有多快对于恶意软件,或者无法修改或维护。代码的正确性和可维护性首先,不考虑原始执行速度,然后是上面列出的所有其他问题。除非您有 性能要求(“此操作必须在 X 毫秒内完成”),否则不要担心速度。先把它做好,如果你不满意,你可以稍后再调整性能。

此外,根据您的其余代码的作用,分配和管理该内存的不同方法之间的差异可能微不足道 - 如果您的代码大部分时间都在等待某人输入数据,那么使用malloc 而不是auto 数组所花费的额外几个时钟真的无关紧要。另一方面,如果您在紧密循环中调用此代码数千次,那么这些额外的时钟加起来会产生显着差异。

根据分析测量做出您的决定,而不是猜测。

【讨论】:

    【解决方案2】:

    这不是更快或更慢的问题。这是你是否炸毁堆栈的问题。

    您正在创建一个大小为 128K 的数组,该数组对于局部变量来说相当大,并且局部变量通常存在于堆栈中。如果您递归调用此函数,则每次调用的堆栈上还有 128K。只需几次递归调用就足以导致堆栈溢出,从而可能导致崩溃。

    动态分配内存几乎是您唯一的选择。但是,您可以将其减少到单个分配:

    char (*tmp)[32768] = malloc(4 * sizeof *tmp);
    

    这种分配是有效的,因为char [4][32768] 类型的二维数组衰减到与上面tmp 的类型匹配的char (*)[32768] 类型的指针。 malloc 返回的内存足以容纳 4 个 char[32768] 类型的对象。该内存是连续的,不像为每个子数组分别调用 4 次 malloc

    【讨论】:

    • 既然sizeof (char)保证为1,我相信malloc(4 * 32768)等价于malloc(4 * sizeof (char [32768])
    • @mediocrevegetable1 做得更好,使用取消引用的变量而不是类型。
    • @PQCraft 这听起来像是goto 的一个很好的用例。
    • @PQCraft 这将使您的阵列脱节,这可能会影响访问它的速度。答案中的方法使用单个连续的内存块,就像二维数组一样。
    • @MichaelDorgan 它不是一个指针数组,它是一个指向 32768 个数组的指针 chars,这要归功于 ()s 和运算符优先级。希望这可以解决问题(刚刚注意到答案编辑,这可以更好地解释它)。
    猜你喜欢
    • 2020-10-06
    • 1970-01-01
    • 1970-01-01
    • 2013-09-26
    • 2011-09-13
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 2014-06-26
    相关资源
    最近更新 更多