在C语言中输出一个二维字符数组的最快方法是什么?

4

我正在开发一款贪吃蛇游戏,但是我发现旧的printf打印一个15*45的二维数组太慢了。

如果您知道任何可以更快输出的I/O,请帮助我!

我的目标是0.10 - 0.15秒。

我也接受使用库,但我想要完全的版权:p :p。

for (y = 0; y < MAX_Y ; y++) 
 {
  printf ("\t");
  for (x = 0; x < MAX_X; x++)
   {
    printf ("%c", base[y][x]);  
   }
  printf ("\n");
 }

3
printf()速度较慢。你最好将单个字符构建为一个更长的字符串,然后一次性输出大字符串,而不是逐个输出每个单独的字符。 - Marc B
你可以尝试仅打印已更改的数组部分,而不是每次想要更新屏幕时都打印整个数组。 - SSC
你可能已经在优化了,但如果没有,请尝试使用-O3-Ofast编译,让编译器尝试将输出最大程度地优化。唯一的其他选择是编写一个快速的assembly函数来写出所需的字符 - 无论哪种方式都会有函数调用开销(printfputcharyourAssemblyFunc),你可以在两种方式中任选其一来削减显著的百分比。(虽然printf等应该已经被很好地优化了) - David C. Rankin
@Jean-BaptisteYunès,我不确定你具体指的是什么(“几乎消耗CPU”?),但我的推理是,实际计算printf所需的每个字符可能需要0.02微秒,而putchar只需要0.01微秒,但两者都写入一个被锁定的缓冲区,该缓冲区由某些设备驱动程序读取,该设备驱动程序与某些外围设备通信,等待中断然后写入一个缓冲区,其中设备仿真读取数据并根据tty设置查找字形...你明白了。在您的情况下,putchar更快是有趣的。您报告的是用户时间(“u”)吗? - Peter - Reinstate Monica
没有 printfputchar 几乎总是使用用户空间中的缓冲区(这仅涉及 CPU)。当然,该缓冲区有时会被写入设备,但即使在这种情况下,这也涉及调用系统函数(在 Unix 下为 write),而该函数反过来只会写入系统空间中的缓冲区!系统调用很慢,但主要是因为它们在真正运行之前验证其参数的有效性。真正的 I/O 是在幕后异步完成的。 - Jean-Baptiste Yunès
显示剩余8条评论
2个回答

3

由于您不使用真正的格式,并且只打印单个字符,因此可以使用简单的putchar()函数:

for (y = 0; y < MAX_Y ; y++) 
 {
  putchar ('\t');
  for (x = 0; x < MAX_X; x++)
   {
    putchar(base[y][x]);  
   }
  putchar('\n');
 }

在我的机器上,迭代100,000次循环,进行3次测试并将输出重定向到/dev/null,得到以下结果:
  • 使用printf:6.679u、6.663u和6.766u
  • 使用putchar:3.309u、3.315u和3.312u
  • 使用putchar_unlocked:0.263u、0.261u和0.263u。
如果使用终端输出,则有:
  • 使用printf:8.153u
  • 使用putchar:3.862u
  • 使用putchar_unlocked:0.634u。
经过的时间为:
  • 使用printf:0:09.46
  • 使用putchar:0:07.75
  • 使用putchar_unlocked:0:05.06。
-编辑-单个write-
您还可以将所有内容分组成一个字符串,并使用puts,如下所示:
 char baseString[MAX_Y*(MAX_X+2)+1];
 int p = 0;
 for (int y = 0; y < MAX_Y ; y++) 
   {
     baseString[p++] = '\t';
     for (int x = 0; x < MAX_X; x++)
       {
         baseString[p++] = base[y][x];  
       }
     baseString[p++] = '\n';
   }
 baseString[p] = 0;
 puts(baseString); // or fwrite(baseString,p,1,stdout);

在这种情况下,测试结果如下:

  • 0.448u、1.155s和0:04.99(puts)
  • 0.418u、1.077s、0:04.81(fwrite)

这比putchar_unlocked稍微好一些。

所有测试都在OSX 10.9、Intel Core i7 2.3Ghz上进行。


你测量了速度增益吗? - Peter - Reinstate Monica
在我的机器上,printf 版本大约慢了两倍。printf 至少必须解码两个参数,检测字符串的结尾,在格式字符串中测试 %;因此必然会更慢。 - Jean-Baptiste Yunès
这是用户时间(“u”)吗?如果是,实时时间会有什么变化吗?你的时间很快,你在本机Linux上吗?当你重定向到/dev/null时是否有任何变化(这会使cygwin上的速度快10倍)? - Peter - Reinstate Monica
我测试了将所有重定向到/dev/null,而不是测量设备I/O。我将给你终端输出时间... - Jean-Baptiste Yunès
哦。那么,我建议你不要使用100000。 - Peter - Reinstate Monica
我认为OP对真实的终端输出时间感兴趣。我们已经有了“终端”部分;)。那么“真实”呢?我的用户时间是0.5秒,10k的真实时间是2.0秒。当然,cygwin可能在其中扮演了一定的角色。正如我所说的,DOS在命令窗口中比cygwin慢10倍,但在重定向到NUL时比cygwin快,这可能表明cygwin在管道方面存在开销。 - Peter - Reinstate Monica

0

怎么样?

fwrite(base[y], MAX_X, 1, stdout)

一次写入整行

不,我还没有测试过它的速度


fwrite在txt文件中没有写入? - Lorand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接