[英]Why is my line count function written in C slower than Ruby?
我有一个函数的两种实现,可打印给定文件中的行数,一种用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代码的性能? 是否有任何有助于编译的标志?
您可以使用编译器选项-O3
来优化性能。 您也可以考虑使用fgets来避免逐字符读取文件。
从文件读取是高延迟操作。 最有可能的是,通过从文件中读取较大的数据块,可以提高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
就是search for newlines
进行内存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会很快。
在MacBook Pro上使用代码会显示C版本的错误结果。 我建议将while
循环更改为此:
while ( (c=fgetc(fp)) != EOF )
使用fgetc
而不是getc
。 而且,您可以按照前面建议的-O3
标志来优化编译器。
对于ruby代码,最好使用each_line
而不是lines
(这是已弃用的别名)。
根据所使用的红宝石版本,可以获得不同的结果。 在我的机器上,ruby比相应的C代码慢大约50倍。
您正在尝试做的事情应该是严重的IO约束。 换句话说,大部分时间都花在读取文件而不是进行实际计算上。 您应该尝试使用另一种方式来读取C版本中的文件,并且也许看看Ruby正在使用什么。
我猜想mmap
可以给您比getc
更好的性能。
另外,请注意基准测试程序的顺序。 在其中一个程序第一次运行后,该文件可能会在内存缓存中,从而使另一个程序更快。 您应该多次运行每个程序,以获得更准确的时间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.