【发布时间】:2017-09-20 05:58:41
【问题描述】:
在this link 指定的编码竞赛中,您需要读取stdin 上的大量数据,进行一些计算并在stdout 上呈现大量数据。
在我的基准测试中,尽管我已尝试尽可能优化它,但几乎只有 i/o 需要时间。
您的输入是一个字符串 (1 <= len <= 100'000) 和 q 行 int 对,其中 q 也是 1 <= q <= 100'000。
我在一个大 100 倍的数据集(len = 10M,q = 10M)上对我的代码进行了基准测试,结果如下:
Activity time accumulated
Read text: 0.004 0.004
Read numbers: 0.146 0.150
Parse numbers: 0.200 0.350
Calc answers: 0.001 0.351
Format output: 0.037 0.388
Print output: 0.143 0.531
通过实现我自己的内联格式和数字解析,我设法将使用 printf 和 scanf 的时间缩短到了 1/3。
但是,当我将解决方案上传到比赛网页时,我的解决方案耗时 1.88 秒(我认为这是 22 个数据集的总时间)。当我查看高分时,有几个实现(在 c++ 中)在 0.05 秒内完成,比我的快近 40 倍!这怎么可能?
我想我可以通过使用 2 个线程来加快速度,然后我可以开始计算并写入标准输出,同时仍然从标准输入读取。然而,在我的大型数据集的理论上最好的情况下,这将减少到 min(0.150, 0.143) 的时间。我离高分还差得很远。。
在下图中你可以看到消耗时间的统计数据。
网站使用以下选项编译程序:
gcc -g -O2 -std=gnu99 -static my_file.c -lm
时间是这样的:
time ./a.out < sample.in > sample.out
我的代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN (100000 + 1)
#define ROW_LEN (6 + 1)
#define DOUBLE_ROW_LEN (2*ROW_LEN)
int main(int argc, char *argv[])
{
int ret = 1;
// Set custom buffers for stdin and out
char stdout_buf[16384];
setvbuf(stdout, stdout_buf, _IOFBF, 16384);
char stdin_buf[16384];
setvbuf(stdin, stdin_buf, _IOFBF, 16384);
// Read stdin to buffer
char *buf = malloc(MAX_LEN);
if (!buf) {
printf("Failed to allocate buffer");
return 1;
}
if (!fgets(buf, MAX_LEN, stdin))
goto EXIT_A;
// Get the num tests
int m ;
scanf("%d\n", &m);
char *num_buf = malloc(DOUBLE_ROW_LEN);
if (!num_buf) {
printf("Failed to allocate num_buffer");
goto EXIT_A;
}
int *nn;
int *start = calloc(m, sizeof(int));
int *stop = calloc(m, sizeof(int));
int *staptr = start;
int *stpptr = stop;
char *cptr;
for(int i=0; i<m; i++) {
fgets(num_buf, DOUBLE_ROW_LEN, stdin);
nn = staptr++;
cptr = num_buf-1;
while(*(++cptr) > '\n') {
if (*cptr == ' ')
nn = stpptr++;
else
*nn = *nn*10 + *cptr-'0';
}
}
// Count for each test
char *buf_end = strchr(buf, '\0');
int len, shift;
char outbuf[ROW_LEN];
char *ptr_l, *ptr_r, *out;
for(int i=0; i<m; i++) {
ptr_l = buf + start[i];
ptr_r = buf + stop[i];
while(ptr_r < buf_end && *ptr_l == *ptr_r) {
++ptr_l;
++ptr_r;
}
// Print length of same sequence
shift = len = (int)(ptr_l - (buf + start[i]));
out = outbuf;
do {
out++;
shift /= 10;
} while (shift);
*out = '\0';
do {
*(--out) = "0123456789"[len%10];
len /= 10;
} while(len);
puts(outbuf);
}
ret = 0;
free(start);
free(stop);
EXIT_A:
free(buf);
return ret;
}
【问题讨论】:
-
为什么要为单个整数分配内存?你在什么系统上?在 Linux 上,stdio 更快(并且比 Windows 上的 iostream 更快),在 Windows 上,iostream 胜过 stdio。 stdio 可以通过使用 IO 函数的未锁定变体(puts_unlocked 而不是 puts 等)来加快速度,因为 POSIX 要求 stdio 对调用使用递归锁,而 iostream (AFAIK) 不存在这样的要求。
-
看起来你每次都在循环输出。如果你用内存换取速度,分配一个更大的缓冲区,然后一次打印整个输出呢?或者,如果输出太多而无法实现,您仍然可以通过缓冲来充分整合输出。如果
puts实际上 是您的瓶颈,这将解决问题。我不确定你是如何衡量到那个时候到达的。例如,“打印输出”测量包括哪些所有操作? -
次要:
cptr = num_buf-1;是未定义的行为 - 尽管它可能按需要“工作”。 -
问题说明了每个数组的最大大小,因此消除对
malloc()和calloc()的调用,只需声明数组足够大以容纳最大数据量 -
强烈建议不要打扰
setvbuf(),
标签: c performance optimization stdout stdin