[英]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.