Как быстро считать строки?

Я попробовал unxutils' wc -l, но это привело к сбою для файлов размером 1 ГБ. Я пробовал этот код С#

long count = 0;
using (StreamReader r = new StreamReader(f))
{
    string line;
    while ((line = r.ReadLine()) != null)
    {
        count++;
    }
}

return count;

Он читает файл размером 500 МБ за 4 секунды.

var size = 256;
var bytes = new byte[size];
var count = 0;
byte query = Convert.ToByte('\n');
using (var stream = File.OpenRead(file))
{
    int many;
    do
    {
        many = stream.Read(bytes, 0, size);
        count += bytes.Where(a => a == query).Count();                    
    } while (many == size);
}

Читает за 10 секунд

var count = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(file))
{
    int current;
    do
    {
        current = stream.ReadByte();
        if (current == query)
        {
            count++;
            continue;
        }
    } while (current!= -1);
}

Занимает 7 секунд

Есть ли что-нибудь быстрее, что я еще не пробовал?


person Jader Dias    schedule 23.05.2011    source источник
comment
@nCdy добавлен в качестве ответа   -  person Jader Dias    schedule 23.05.2011
comment
Что ваш профиль определил как горячую точку в каждом из них?   -  person Eric Lippert    schedule 23.05.2011
comment
Вы уверены, что тестируете подсчет строк, а не файловую систему? Если первый тест загружает файл таким образом, что он кэшируется, то последующие тесты будут выполняться намного быстрее. Убедитесь, что вы действительно тестируете то, что, по вашему мнению, тестируете.   -  person Jim Mischel    schedule 23.05.2011
comment
+1 Джиму Мишелю. Тестирование производительности сложнее, чем думает большинство людей!   -  person Cheeso    schedule 24.05.2011
comment
Некоторые обсуждения здесь: bytes.com/topic/ до-диез/ответы/   -  person Arithmomaniac    schedule 24.06.2016


Ответы (6)


Ваш первый подход уже выглядит оптимальным решением. Имейте в виду, что вы в основном не привязаны к ЦП, но ограничены скоростью чтения HD, которая при 500 МБ / 4 с = 125 МБ / с уже довольно быстро. Единственный способ стать быстрее — использовать RAID или SSD, а не лучший алгоритм.

person SirViver    schedule 23.05.2011
comment
Я также понял, что могу оценить количество строк, получив размер файла и разделив его на средний размер первых строк. - person Jader Dias; 23.05.2011
comment
@JaderDias: Верно, но тогда у вас есть только оценка, а не фактическое количество. И в зависимости от того, как структурирован файл, ваша оценка может оказаться далекой. Вы не указали, какова цель подсчета строк или как обычно выглядят файлы, поэтому нельзя дать более специализированный совет. - person SirViver; 23.05.2011
comment
Для моего файла CSV оценка достаточно точна - person Jader Dias; 23.05.2011

File.ReadLines появился в .NET 4.0.

var count = File.ReadLines(file).Count();

работает за 4 секунды, столько же, сколько и первый фрагмент кода

person Jader Dias    schedule 23.05.2011
comment
Это потому, что он в основном делает то же самое, что и ваш первый фрагмент;) - person SirViver; 23.05.2011
comment
никогда не используйте Count(), используйте Length (File.ReadAllLines(@yourfile).Length;) // снова проверьте это решение, но используя Length - person cnd; 23.05.2011
comment
@nCdy: Это действительно плохое предложение (в данном случае)! Обратите внимание на разницу: он использует File.ReadLines(), который на самом деле возвращает IEnumerable<string>, и просто делает yield return того, что в основном делает его первый фрагмент. File.ReadAllLines() будет считывать все строки в память, что будет ужасно с точки зрения производительности. Тем не менее, конечно, если у вас есть уже есть массив, вы должны использовать Length вместо Count() ;) - person SirViver; 23.05.2011
comment
@SirViver согласился. Ему не нужно загружать все линии, но если он не будет их использовать. - person cnd; 23.05.2011
comment
@nCdy, как сказал SirViver Exception of type 'System.OutOfMemoryException' was thrown. - person Jader Dias; 23.05.2011
comment
Count и Length возвращают только целые числа? - person Tim Barrass; 14.02.2014

Вы просто ищете инструмент для эффективного подсчета строк в файле? Если да, попробуйте MS LogParser< /а>

Что-то вроде ниже даст вам количество строк:

LogParser "SELECT count(*) FROM file" -i:TEXTLINE
person manojlds    schedule 23.05.2011

Если вы действительно хотите быстро, рассмотрите код C.

Если это утилита командной строки, она будет работать быстрее, поскольку ей не придется инициализировать CLR или .NET. И он не будет перераспределять новую строку для каждой строки, считанной из файла, что, вероятно, экономит время на пропускной способности.

У меня нет файлов с 1g строками, поэтому сравнить не могу. вы можете попробовать, хотя:

/*
 * LineCount.c
 *
 * count lines...
 *
 * compile with: 
 *
 *  c:\vc10\bin\cl.exe /O2 -Ic:\vc10\Include -I\winsdk\Include 
 *          LineCount.c -link /debug /SUBSYSTEM:CONSOLE /LIBPATH:c:\vc10\Lib
 *          /LIBPATH:\winsdk\Lib /out:LineCount.exe
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


void Usage(char *appname)
{
    printf("\nLineCount.exe\n");
    printf("  count lines in a text file...\n\n");
    printf("usage:\n");
    printf("  %s <filename>\n\n", appname);
}



int linecnt(char *file)
{
    int sz = 2048;
    char *buf = (char *) malloc(sz);
    FILE *fp = NULL;
    int n= 0;
    errno_t rc = fopen_s(&fp, file, "r");

    if (rc) {
        fprintf(stderr, "%s: fopen(%s) failed: ecode(%d)\n",
                __FILE__, file, rc);
        return -1;
    }

    while (fgets(buf, sz, fp)){
        int r = strlen(buf);
        if (buf[r-1] == '\n')
            n++;
        // could re-alloc here to handle larger lines
    }
    fclose(fp);
    return n;
}

int main(int argc, char **argv)
{
    if (argc==2) {
        int n = linecnt (argv[1]);
        printf("Lines: %d\n", n);
    }
    else {
        Usage(argv[0]);
        exit(1);
    }
}
person Cheeso    schedule 23.05.2011
comment
наверное быстрее, но держу пари, разница меньше 10% - person Jader Dias; 23.05.2011
comment
Попробуйте и посмотрите. Было бы интересно узнать. - person Cheeso; 23.05.2011
comment
10 секунд =( работает на VS2010 при отладке, как и все остальные тесты - person Jader Dias; 23.05.2011
comment
Очень неожиданно. Подозреваю, что что-то еще не так. - person Cheeso; 23.05.2011
comment
@Jader: Ого подождите минутку, вы выполняете тесты производительности в режиме отладки? Никогда и никогда не делайте этого. Вы можете получить совершенно вводящие в заблуждение результаты. Отладчик намеренно деоптимизирует вашу программу, чтобы улучшить процесс отладки. В этом случае, вероятно, это не проблема, так как вы привязаны к диску, а не к процессору, но тем не менее, измерение производительности в отладчике является ужасной практикой программирования. - person Eric Lippert; 23.05.2011
comment
@ Эрик Я ожидал, что кто-то скажет что-то подобное, но я провел все свои тесты в режиме Release, и ничего не изменилось, если округлить до секунд (4 секунды в режиме отладки по-прежнему 4 секунды в режиме выпуска) - person Jader Dias; 24.05.2011
comment
@Jader: Как я уже сказал, это потому, что вам повезло, и вы выбрали проблему с производительностью, которая связана со скоростью дискового оборудования. Когда вы пытаетесь оптимизировать что-то, что связано со скоростью реального кода, это совсем другая история. - person Eric Lippert; 24.05.2011

Я думаю, что ваш ответ выглядит хорошо. Единственное, что я бы добавил, это поиграться с размером буфера. Я чувствую, что это может изменить производительность в зависимости от размера вашего буфера.

Пожалуйста, обратитесь к размеру буфера по адресу - Оптимальный размер буфера файла для чтения?

person istudy0    schedule 23.05.2011
comment
Я пробовал разные значения, и все, что выше 256, будет иметь одинаковую производительность, а более низкие значения, такие как 4, медленнее. - person Jader Dias; 23.05.2011

Вы пробовали флекс?

%{
long num_lines = 0;
%}
%option 8bit outfile="scanner.c"
%option nounput nomain noyywrap
%option warn

%%
.+ { }
\n { ++num_lines; }
%%
int main(int argc, char **argv);

int main (argc,argv)
int argc;
char **argv;
{
yylex();
printf( "# of lines = %d\n", num_lines );
return 0;
}

Просто скомпилируйте с помощью:

flex -Cf scanner.l 
gcc -O -o lineCount.exe scanner.c

Он принимает ввод на стандартный ввод и выводит количество строк.

person Spencer Rathbun    schedule 23.05.2011