在I/O方面,我希望Python和C具有相似的性能,但是对于类似的实现,C的速度是Python的1.5到2倍。在
任务很简单:连接数千个~250字节的文本文件,每个文件包含两行:
Header1 \t Header2 \t ... HeaderN
float1 \t float2 \t ... floatN
所有文件的头都是相同的,因此只读取一次,输出文件将如下所示:
^{pr2}$以下是我在C中的实现:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <time.h>
#define LINE_SIZE 300
#define BUFFER_SZ 5000*LINE_SIZE
void combine(char *fname) {
DIR *d;
FILE * fp;
char line[LINE_SIZE];
char buffer[BUFFER_SZ];
short flagHeader = 1;
buffer[0] = '\0'; // need to init buffer befroe strcat to it
struct dirent *dir;
chdir("runs");
d = opendir(".");
if (d) {
while ((dir = readdir(d)) != NULL) {
if ((strstr(dir->d_name, "Hs")) && (strstr(dir->d_name, ".txt")) ) {
fp = fopen (dir->d_name, "r");
fgets(line, LINE_SIZE, fp); // read first line
if (flagHeader) { // append it to buffer only once
strcat(buffer, line);
flagHeader = 0;
}
fgets(line, LINE_SIZE, fp); // read second line
strcat(buffer, line);
fclose(fp);
}
}
closedir(d);
chdir("..");
fp = fopen(fname, "w");
fprintf(fp, buffer);
fclose(fp);
}
}
int main() {
clock_t tc;
int msec;
tc = clock();
combine("results_c.txt");
msec = (clock() - tc) * 1000 / CLOCKS_PER_SEC;
printf("elapsed time: %d.%ds\n", msec/1000, msec%1000);
return 0;
}
在Python中:
import glob
from time import time
def combine(wildcard, fname='results.txt'):
"""Concatenates all files matching a name pattern into one file.
Assumes that the files have 2 lines, the first one being the header.
"""
files = glob.glob(wildcard)
buffer = ''
flagHeader = True
for file in files:
with open(file, 'r') as pf:
lines = pf.readlines()
if not len(lines) == 2:
print('Error reading file %s. Skipping.' % file)
continue
if flagHeader:
buffer += lines[0]
flagHeader = False
buffer += lines[1]
with open(fname, 'w') as pf:
pf.write(buffer)
if __name__ == '__main__':
et = time()
combine('runs\\Hs*.txt')
et = time() - et
print("elapsed time: %.3fs" % et)
每个基准测试运行10次-文件在繁忙办公室的本地网络驱动器中,所以我想这解释了变化的原因:
Run 1/10
C elapsed time: 9.530s
Python elapsed time: 10.225s
===================
Run 2/10
C elapsed time: 5.378s
Python elapsed time: 10.613s
===================
Run 3/10
C elapsed time: 6.534s
Python elapsed time: 13.971s
===================
Run 4/10
C elapsed time: 5.927s
Python elapsed time: 14.181s
===================
Run 5/10
C elapsed time: 5.981s
Python elapsed time: 9.662s
===================
Run 6/10
C elapsed time: 4.658s
Python elapsed time: 9.757s
===================
Run 7/10
C elapsed time: 10.323s
Python elapsed time: 19.032s
===================
Run 8/10
C elapsed time: 8.236s
Python elapsed time: 18.800s
===================
Run 9/10
C elapsed time: 7.580s
Python elapsed time: 15.730s
===================
Run 10/10
C elapsed time: 9.465s
Python elapsed time: 20.532s
===================
另外,python实现的概要文件运行确实表明70%的时间花在io.open
上,其余的时间花在readlines
上。在
In [2]: prun bc.combine('runs\\Hs*.txt')
64850 function calls (64847 primitive calls) in 12.205 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1899 8.391 0.004 8.417 0.004 {built-in method io.open}
1898 3.322 0.002 3.341 0.002 {method 'readlines' of '_io._IOBase' objects}
1 0.255 0.255 0.255 0.255 {built-in method nt.listdir}
即使readlines
比fgets
慢得多,但python只使用io.open
所花的时间比C中的总运行时要长。而且,最终,readlines
和{
所以,我的问题是:在这个特殊的例子中,为什么python的I/O速度比C慢得多?在
可以归结为以下几点:
最重要的是,Python版本使用文本模式(即}。
r
和w
),这意味着处理str
(UTF-8)对象,而不是{有很多小文件,我们很少使用它们,Python自身的开销(例如在
open
中设置file对象)变得很重要。Python必须为大多数事情动态分配内存。
另外请注意,如果使用本地文件并执行多次运行,则此测试中的I/O与此无关,因为这些文件将已缓存在内存中。唯一真正的I/O将是最终的
write
(即使这样,您也必须确保正在刷新/同步到磁盘)。在现在,如果处理文本模式(即使用
rb
和wb
),并且减少分配(在本例中不太重要,但也很明显),您会得到如下结果:那么Python完成任务的时间已经比C版本快了一半:
^{pr2}$您可能仍然可以稍微改进一下时间,例如,通过避免
glob
。在但是,如果您对C版本也应用了一些类似的修改,那么您将获得比Python三分之一的时间更好的时间:
看看:
相关问题 更多 >
编程相关推荐