我有一个函数的两个实现,它打印给定文件中的行数,一个用C编写,另一个用Ruby编写。出于某些奇怪的原因,Ruby版本快了2倍!这是代码:
linecount.c (使用gcc linecount.c -o linecount
编译)
#include <stdio.h>
int main(int argc, char **argv) {
FILE *fp;
int c;
int count;
fp = fopen(argv[1], "r");
while ((c = getc(fp)) != EOF) {
if (c == '\n') {
count++;
}
}
fclose(fp);
printf("%d\n", count);
return 0;
}
ruby_linecount.rb
#!/usr/bin/env ruby
puts File.open(ARGV[0]).lines.count
这些是基准:
time (for i in {1..100}; do ./linecount /usr/share/dict/words; done)
real 0m14.438s
user 0m14.041s
sys 0m0.298s
time (for i in {1..100}; do ./ruby_linecount.rb /usr/share/dict/words; done)
real 0m6.910s
user 0m5.917s
sys 0m0.734s
为什么C版这么慢?如何提高C代码的性能?是否有任何编译器标志可以提供帮助?
答案 0 :(得分:2)
您可以使用编译器选项-O3
来优化性能。您也可以考虑使用fgets来避免逐个字符地读取文件。
答案 1 :(得分:1)
从文件中读取是一种高延迟操作。最有可能的是,通过从文件中读取更大的数据块,可以提高C版本的速度。
我提供两个例子。
第一个使用16K缓冲区。可以更改缓冲区大小以查看更好的性能。
示例1
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define BUFSIZE (16 * 1024)
int main(int argc, char **argv)
{
int rCode;
FILE *fp = NULL;
char *buf = NULL;
int count = 0;
size_t bufLen;
errno=0;
fp = fopen(argv[1], "r");
if(NULL == fp)
{
rCode=errno;
fprintf(stder, "fopen() failed. errno:%d\n", errno);
goto CLEANUP;
}
errno=0;
buf = malloc(BUFSIZE);
if(NULL == buf)
{
rCode=errno;
fprintf(stder, "malloc() failed. errno:%d\n", errno);
goto CLEANUP;
}
bufLen = fread(buf, 1, BUFSIZE, fp);
while(bufLen)
{
char *cp;
for(cp=buf; (cp < buf + bufLen); ++cp)
if('\n' == *cp)
++count;
bufLen = fread(buf, 1, BUFSIZE, fp);
}
printf("%d\n", count);
CLEANUP:
if(fp)
fclose(fp);
if(buf)
free(buf);
return(rCode);
}
下一个将文件映射到进程内存映射(或地址空间)。然后,寻找新行是内存search for newlines
操作。
示例2
#include <errno.h> /* errno, ... */
#include <fcntl.h> /* open(), O__RDONLY, ... */
#include <stdio.h> /* fprintf(), stderr, printf(), ... */
#include <sys/mman.h> /* mmap(), PROT_READ, MAP_SHARED, ... */
#include <sys/stat.h> /* fstat(), struct stat, ... */
#include <unistd.h> /* close(), ... */
int main(int argc, char **argv)
{
int rCode=0;
int fd = (-1);
struct stat statBuf;
char *fileBuf=NULL;
char *cp;
int count=0;
errno=0;
fd = open(argv[1], O_RDONLY);
if((-1) == fd)
{
rCode=errno;
fprintf(stderr, "open() failed. errno:%d\n", errno);
goto CLEANUP;
}
errno=0;
if((-1) == fstat(fd, &statBuf))
{
rCode=errno;
fprintf(stderr, "fstat() failed. errno:%d\n", errno);
goto CLEANUP;
}
errno=0;
fileBuf = mmap(
NULL, /* preferred start address, normally NULL (system chooses) */
statBuf.st_size, /* length of the mapped region */
PROT_READ, /* memory protection */
MAP_SHARED, /* private/shared */
fd, /* fd of mapped file */
0 /* file offset (should be a multiples of a page) */
);
if((void *)(-1) == fileBuf)
{
rCode=errno;
fprintf(stderr, "mmap() failed. errno:%d\n", errno);
goto CLEANUP;
}
for(cp=fileBuf; cp < fileBuf + statBuf.st_size; ++cp)
if('\n' == *cp)
++count;
printf("%d\n", count);
CLEANUP:
if(fileBuf)
munmap(fileBuf, statBuf.st_size);
if((-1) != fd)
close(fd);
return(rCode);
}
修改强>
我同意Neil Slater's
评论。虽然上面的例子可以提高操作的速度(与问题代码中的例子相比);也许Ruby会很快。
答案 2 :(得分:0)
在MacBook Pro上使用您的代码显示C版本的错误结果。我建议将while
循环更改为:
while ( (c=fgetc(fp)) != EOF )
使用fgetc
代替getc
。此外,您可以使用前面建议的-O3
标志优化编译器。
对于ruby代码,最好使用each_line
而不是lines
(这是一个弃用的别名)。
根据您使用的ruby版本,您可以获得不同的结果。在我的机器上,ruby比相应的C代码慢大约50倍。
答案 3 :(得分:0)
你要做的事情应该是严重的IO限制。换句话说,大部分时间用于读取文件而不是进行实际计算。您应该尝试使用另一种方式在C版本中读取文件,并且可以查看Ruby正在使用的内容。
我猜mmap
可以为您提供更好的表现getc
。
另外,请注意基准程序的顺序。在第一次运行其中一个程序后,该文件可能会在内存缓存中,使另一个程序更快。您应该多次运行每个程序,以获得更准确的计时。