С точки зрения ввода-вывода, я ожидаю, что Python и C будут иметь одинаковую производительность, но я вижу, что C в 1,5-2 раза быстрее, чем Python для аналогичной реализации.
Задача проста: объединить тысячи ~ 250 байтов текстовых файлов, каждый из которых содержит две строки:
Header1 \t Header2 \t ... HeaderN
float1 \t float2 \t ... floatN
Заголовок одинаков для всех файлов, поэтому он читается только один раз, и выходной файл будет выглядеть так:
Header1 \t Header2 \t ... HeaderN
float1 \t float2 \t ... floatN
float1 \t float2 \t ... floatN
float1 \t float2 \t ... floatN
... thousands of lines
float1 \t float2 \t ... floatN
Вот моя реализация в 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
и fgets
читать файл построчно, так что я ожидаю более сопоставимой производительности.
Итак, на мой вопрос: в данном конкретном случае, почему python намного медленнее, чем C для ввода / вывода?