【问题标题】:Read adjacency matrix from file - Competitive programming从文件中读取邻接矩阵 - 竞争性编程
【发布时间】:2021-09-06 11:48:03
【问题描述】:

我必须做一个任务,我必须读取一个包含邻接矩阵的文件,然后再做一些事情。 我已经完成了所有工作,但我的代码非常慢,至少对于基准测试系统而言是这样。

我正在读取下面这段代码 sn-p 中的所有文件行:

while (fgets(buf, sizeof(buf), stdin) != NULL) {
    parse(buf);
    i++;
}

然后我使用 strtok 和 atoi 初始化我的二维数组:

void parse(char *str, int count, char *sep) {
    //char *aux = malloc(count * sizeof(char*));
    char *aux;
    aux = strtok(str, sep);

    int j = 0;

    while (aux) {
         array[(i*DIM) + j] = atoi(aux);
         j++;
         aux = strtok(NULL, sep);
    }
    //free(aux);
}

数组是 DIM*DIM 大小,每个 INT 用逗号分隔。

3*3 矩阵的样本输入:

1,20,1
0,111,3
4,7,10

如何改进这一点以获得更好的性能?

编辑:

数组定义:

array = malloc(DIM*DIM*sizeof(int));

【问题讨论】:

  • array 是如何声明的,您不使用二维数组的原因是什么?另外,当您只使用一个aux 时,为什么还要为多个aux 分配空间?
  • 无论如何,我认为您正在寻找的解决方案是 strtol 在循环中重复调用。不需要 strtok 和 malloc。并且永远不应该使用 atoi。
  • 发布 cmets 时,请查看框右下角的小帮助链接以获取格式提示。无论如何,您应该通过编辑来澄清问题本身。
  • 我之前主要是用strtol,这比atoi和strtok快很多吗?
  • @sarcokiller strtol 优于 atoi 的主要优点是它具有错误处理功能,因此您可以在循环中检查其结果。动态内存分配很慢,并且同时使用 strtok + 其他一些解析函数会多次循环相同的数据。我可以用一些示例代码来制作答案。

标签: c performance gcc


【解决方案1】:

malloc 部分没有意义,因为您只需要一个指向strtok 的字符指针。同样,strtolatoi 之类的函数已经解析了数据,因此您甚至不需要 strtok - 在这种情况下它只会占用额外的时间。此外,atoi 没有任何错误处理,因此永远不应使用它。

所以你可以在一个循环中调用strtol,它会做你想做的事。通过检查endptr 参数,您可以查看每次读取是否成功(man strtol)。然后在循环的下一圈,从endptr + 1 重新开始。

如果将此与您的 2D int 数组要求相结合,该函数可能如下所示:

#include <stdio.h>
#include <stdlib.h>

void csv_to_int (size_t col, size_t row, int dst[col][row], const char* str)
{
  const char* ptr = str;
  char* end;

  for(size_t c=0; c<col; c++)
  {
    for(size_t r=0; r<row; r++)
    {
      int val=strtol(ptr,&end,10);
      if(ptr==end)
      {
        return ;
      }
      dst[c][r]=val;
      ptr = end+1;
    }
  }
}

int main (void)
{
  const char* input = "1,20,1\n0,111,3\n4,7,10\n";
  int arr[3][3];
  
  csv_to_int(3, 3, arr, input);

  for(size_t i=0; i<3; i++)
  {
    for(size_t j=0; j<3; j++)
    {
      printf("%3d ", arr[i][j]);
    }
    puts("");
  }
}

输出:

 1  20   1
 0 111   3
 4   7  10

这当然是假设输入适合 3x3 格式——这段代码几乎没有错误处理。

【讨论】:

  • 为了快速检查,我实现了您解决方案的一半,并且代码的运行速度已经快了 2 倍!我基本上只使用它而不是 strtok 和 atoi 但我仍然使用 fgets 逐行解析文件!我会尽快更新你!
  • @sarcokiller 值得注意的是,文件处理将成为您代码中的瓶颈,因此如果您可以使用 fread 等一次性读取文件,那将是您可以做的最重要的优化。跨度>
【解决方案2】:

如何改进这一点以获得更好的性能?

因此,如果您认为它们很慢,请不要使用这些功能。限制您的要求 - 不要处理特定于语言环境的数字。因此,请自行阅读并转换。公然无视错误检查。顺带一提:

#define _GNU_SOURCE 1
#include <stdio.h>

int main() {
    char data[] ="1,20,1\n0,111,3\n4,7,10\n";
    FILE *f = fmemopen(data, sizeof(data), "r");
    int i = 0, j = 0;
    #define DIM 3
    int array[20];

    // this reading part
    int buf = 0;
    for (int c; (c = fgetc(f)) != EOF; ) {
        if (c == '\n') {
            array[i * DIM + j] = buf;
            buf = 0;
            ++i;
            j = 0;
        } else if (c == ',') {
            array[i * DIM + j] = buf;
            buf = 0;
            ++j;
        } else {
            buf *= 10;
            buf += (c - '0');
        }
    }
    array[i * DIM + j] = buf;
    ++j;

    // checking
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            printf("%d %d = %d\n", i, j, array[i * DIM + j]);
        }
    }
}

更进一步,忽略可移植性,并使用系统调用(在 unix - read(STDIN_FILENO))而不是 C API。

【讨论】:

  • 在我看来这是个坏建议;由于函数调用开销,逐字符读取速度很慢。使用read() 也不是让事情变得更快的魔杖。当然,OP 的问题也缺乏足够的细节,所以很难给出一个好的答案。理想情况下,您将能够针对您的解决方案对 OP 的代码进行基准测试。
  • @G.Sliepen 使用 read() 也不是让事情变得更快的魔杖。 不过,它是一个很好的一阶近似值,只是因为它避免读取过多的字符。
  • @AndrewHenle 我不确定我明白你的意思是避免阅读比需要更多的字符吗?我唯一能想象的是它避免了不必要的复制;使用fgets(),它可能会在幕后将read() 放入一个大缓冲区,然后复制到调用者提供的缓冲区中。但是,如果您无论如何都在阅读整个文件,那么您使用fgets() 读取的数量与手动调用read() 的数量相同。我的观点是,在不知道fgets() 是否是这里的问题的情况下进行低级处理并不是一个好建议。
  • This is bad advice in my opinion; reading character by character is slow due to function call overhead 问题是How can I improve this for better performances?。使用的原始代码fgets。我提议的版本是否没有改进以获得更好的性能?
  • @wildplasser getc() 可能由于同步而变慢。试试getc_unlocked 看看效果如何
猜你喜欢
  • 2018-12-07
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多