【问题标题】:C Dynamic allocation speed questionC 动态分配速度问题
【发布时间】:2011-11-15 11:58:03
【问题描述】:

我正在使用这段代码来动态创建一个二维数组:

char **FileTables;
int rows = 1000;
int i;

FileTables = (char**)malloc(rows * sizeof(char));
for (i = 0; i < rows; i++) {
    FileTables[i] = (char*)malloc(256 * sizeof(char));
}

问题在于 1000 行,并且可能更多,分配所有内存需要几秒钟。 有没有更快/更好的方法来做到这一点?

编辑: 除了明显更简单的代码之外,使用其中一种方法是否有优势?

char **FileTables;
int rows = 1000;
int i;

FileTables = malloc(rows * sizeof(char*));
FileTables[0] = malloc(rows * 256 * sizeof(char));
for (i = 0; i < rows; i++) {
    FileTables[i] = FileTables[0] + i * 256;
}

还有..

char (*FileTables)[256];
int rows = 1000;

FileTables = malloc(rows * sizeof(*FileTables));

(是的,我修复了不必要的强制转换)

【问题讨论】:

  • 注意:您不应该在 C 中强制转换 malloc 的结果 - 这是不必要的,并且可以掩盖编译器可能会警告您的错误。
  • 保罗:+1。我真的很想知道为什么人们会这样做。他们在什么时候说:“嗯,我的代码运行良好,看起来很棒。我想知道是否可以通过插入无偿演员表来添加一些噪音。for (i = (int)0; (int)i &lt; 12; ++(int)i)
  • @Kerrek:++(int)i 无法编译。 ++ 需要左值,(int)i 不是左值。
  • 保罗:是的,确实,这是一个过度夸大的例子。公平地说,malloc-casts 通常也确实改变了类型——但没有必要这样做,因为转换已经是隐式的。

标签: c arrays performance dynamic malloc


【解决方案1】:

你可以只用两次分配和一些指针算法:

int rows = 1000;
int cols = 256;
char *data;
char **FileTables;
int i;

data = malloc(rows * cols);
FileTables = malloc(rows * sizeof(char*));
for (i = 0; i < rows; i++) {
    FileTables[i] = data + i * cols;
}

另外请注意,我修复了 malloc(rows * sizeof(char)) 中的一个错误(sizeof(char) 应该是 sizeof(char*),因为您将 指针数组 分配给 char)。

【讨论】:

  • 已注意到(并删除了评论)。为答案+1。
【解决方案2】:

只要列数不变,或者如果您使用的是 C99,您就可以使用单个 malloc 而无需自己进行丑陋的行/列寻址算术:

char (*FileTables)[256] = malloc(rows * sizeof *FileTables);

【讨论】:

  • 我打算使用 aix 发布的解决方案,但这更简单,代码也更少。谁能评论哪种方法更好,为什么?
  • 我更喜欢我的,因为FileTables 的类型是正确的,因此您可以只写FileTables[row][col],而FileTables[row] 本身会衰减到指向该行的指针(普通的@987654326 @)。您不必自己跟踪算术。
  • 我是用通常的free(FileTables); 释放它还是需要做一些特别的事情?
  • 没错,就是free(FileTables);。你也可以在上面使用realloc;只需确保正确计算新尺寸即可。
【解决方案3】:

如果数组的大小始终为row × 256,那么您可以考虑一个一维数组malloc(row * 256),并大步访问它:

char get(unsigned i, unsigned j, char * array) { return array[j + 256 * i]; }
void set(char value, unsigned i, unsigned j, char * array) { array[j + 256 * i] = value; }

这避免了多次分配并提供了更好的内存局部性。最重要的是,您可以选择行或列顺序进行微优化。

【讨论】:

    【解决方案4】:
    char **FileTables; 
    int rows = 1000; 
    int i; 
    
    FileTables = (char**)malloc(rows * sizeof(char *)); 
    char *data = (char *)malloc(256 * 1000 * sizeof(char));
    for (i = 0; i < rows; ++i) { 
        FileTables[i] = data;
        data += 256 * sizeof(char);
    }
    

    应该是更好的解决方案。

    【讨论】:

      【解决方案5】:

      我不相信你会接近几秒钟。在我的机器上将行数增加到 1000 万行仍然不到一秒。

      但是,如果您想最小化分配,您只需要一个。

      FileTables = (char**) malloc(rows * (sizeof(char *) + 256*sizeof(char)));
      FileTables[0] = (char *) &FileTables[rows];
      for (i = 1; i < rows; i++) {
          FileTables[i] = FileTables[i-1] + 256 * sizeof (char);
      }
      free(FileTables);
      

      一种更有效的方法是避免第二级间接。

      typedef char chars[256];
      
      int main(int argc, char** argv) {
          chars* FileTables;
          int rows = 100000000;
          int i;
      
          FileTables = (chars*) malloc(rows * sizeof (chars));
          free(FileTables);
      
          return (EXIT_SUCCESS);
      }
      

      这避免了指针查找,因为 C 可以计算其余部分。

      【讨论】:

        【解决方案6】:

        首先,您确定是内存分配问题吗?分配 1000 个内存块通常不会花费几秒钟。

        如果您有特殊需要,您可以研究备用 malloc 实现(例如,如果您在线程中分配内存,则可以使用 google 的 tcmalloc)。

        否则,malloc 真正的“慢”部分实际上是从操作系统获取内存(使用 sbrk() 或 mmap()),并且大多数 malloc 实现会一次获取一大块并以较小的部分返回,所以这里没有 1000 次调用来分配 1k,可能有 60 次调用来分配 16k。在 strace 或类似的程序下运行程序可能会让您了解真正进行了多少慢速系统调用。您可以自己实现类似的行为,通过一次调用来分配 256K 并将其细分为更小的块。您可以尝试分配一大块内存,然后立即对其进行 free()-ing,并希望库 malloc 保留该内存并且不会返回到操作系统中获取更多信息。

        【讨论】:

          【解决方案7】:

          这看起来真的是过早的优化;因为,您要求更快,但您没有指出多快才足够快。不过,如果你真的需要这样做......

          加快分配的技巧:

          1. 减少分配次数
          2. 进行较小的分配

          如您所见,如果您需要分配 10M,这些提示很快就会相互冲突。为了在更小和更少的分配之间确定正确的平衡,需要进行分析。

          查看您的内存块大小并立即分配整页内存。这是一个旧的硬件黑客,但它确实保证您不会一次请求多个连续内存页面(这加快了从空闲页面列表中选择的速度),并且它还保证您不会浪费一些循环地址通过请求内存管理器的块保留子系统已保留的地址来获得空间。

          如果这不能为您提供所需的性能,请重写代码,使其不需要按其呈现方式进行分配。

          无论哪种方式,如果不详细了解计算机上的内存管理子系统的实际设计方式,就不可能保证最佳分配速度。

          【讨论】:

            猜你喜欢
            • 2012-08-28
            • 2017-05-07
            • 2022-01-17
            • 2011-09-13
            • 2012-05-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-05
            相关资源
            最近更新 更多