Чтение файла быстрее в C - PullRequest
2 голосов
/ 31 января 2011

Хм, интересно, есть ли способ прочитать файл быстрее, чем использовать fscanf ()

Например, предположим, что у меня есть этот текст

4

55 k

52 o

24 l

523 i

Сначала я хочу прочитать первыйчисло, которое дает нам количество следующих строк.

Пусть это число будет называться N.

После N я хочу прочитать N строк, которые имеют целое число и символ.С fscanf это будет так

fscanf(fin,"%d %c",&a,&c);

Ответы [ 6 ]

3 голосов
/ 31 января 2011

Вы почти не обрабатываете, поэтому, вероятно, узким местом является пропускная способность файловой системы.Однако сначала вы должны измерить, если это действительно так.Если вы не хотите использовать профилировщик, вы можете просто измерить время работы вашего приложения.Размер входного файла, деленный на время выполнения, можно использовать для проверки, достигли ли вы предела пропускной способности файловой системы.

Тогда, если вы далеки от вышеупомянутого предела, вам, вероятно, нужно оптимизировать способ чтенияфайл.Может быть лучше прочитать его большими кусками, используя fread(), а затем обработать буфер, сохраненный в памяти, с помощью sscanf().

Вы также можете самостоятельно проанализировать буфер, который будет быстрее, чем *scanf().

[править]

Специально для Дракоши:

$ time ./main1
Good entries: 10000000

real    0m3.732s
user    0m3.531s
sys 0m0.109s
$ time ./main2
Good entries: 10000000

real    0m0.605s
user    0m0.496s
sys 0m0.094s

Таким образом, оптимизированная версия составляет ~ 127 МБ / с, что может быть узким местом моей файловой системы иливозможно ОС кеширует файл в оперативной памяти.Исходная версия ~ 20 МБ / с.

Протестировано с файлом 80 МБ:

10000000

1234 a

1234 a
...

main1.c

#include <stdio.h>

int ok = 0;
void processEntry(int a, char c) {
    if (a == 1234 && c == 'a') {
        ++ok;
    }
}

int main(int argc, char **argv) {
    FILE *f = fopen("data.txt", "r");
    int total = 0;
    int a;
    char c;
    int i = 0;

    fscanf(f, "%d", &total);
    for (i = 0; i < total; ++i) {
        if (2 != fscanf(f, "%d %c", &a, &c)) {
            fclose(f);
            return 1;
        }
        processEntry(a, c);
    }
    fclose(f);
    printf("Good entries: %d\n", ok);
    return (ok == total) ? 0 : 1;
}

main2.c

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

int ok = 0;
void processEntry(int a, char c) {
    if (a == 1234 && c == 'a') {
        ++ok;
    }
}

int main(int argc, char **argv) {
    FILE *f = fopen("data.txt", "r");
    int total = 0;
    int a;
    char c;
    int i = 0;
    char *numberPtr = NULL;
    char buf[2048];
    size_t toProcess = sizeof(buf);
    int state = 0;
    int fileLength, lengthLeft;

    fseek(f, 0, SEEK_END);
    fileLength = ftell(f);
    fseek(f, 0, SEEK_SET);

    fscanf(f, "%d", &total);  // read the first line

    lengthLeft = fileLength - ftell(f);

    // read other lines using FSM
    do {
        if (lengthLeft < sizeof(buf)) {
            fread(buf, lengthLeft, 1, f);
            toProcess = lengthLeft;
        } else {
            fread(buf, sizeof(buf), 1, f);
            toProcess = sizeof(buf);
        }
        lengthLeft -= toProcess;
        for (i = 0; i < toProcess; ++i) {
            switch (state) {
                case 0:
                    if (isdigit(buf[i])) {
                        state = 1;
                        a = buf[i] - '0';
                    }
                    break;
                case 1:
                    if (isdigit(buf[i])) {
                        a = a * 10 + buf[i] - '0';
                    } else {
                        state = 2;
                    }
                    break;
                case 2:
                    if (isalpha(buf[i])) {
                        state = 0;
                        c = buf[i];
                        processEntry(a, c);
                    }
                    break;
            }
        }
    } while (toProcess == sizeof(buf));

    fclose(f);
    printf("Good entries: %d\n", ok);
    return (ok == total) ? 0 : 1;
}
1 голос
/ 31 января 2011

Вряд ли вы сможете значительно ускорить фактическое чтение данных. Большая часть времени здесь будет потрачена на передачу данных с диска в память, что неизбежно.

Вы можете немного ускориться, заменив вызов fscanf на fgets, а затем вручную проанализировав строку (с strtol), чтобы обойти синтаксический анализ строки формата, который должен делать fscanf, но не ожидайте огромных сбережений.

В конце концов, обычно не стоит сильно оптимизировать операции ввода-вывода, потому что на них обычно будет влиять время, необходимое для передачи фактических данных на / с аппаратного / периферийного оборудования.

0 голосов
/ 12 ноября 2011

Оформить заказ read и fread.Во время тренировок по программированию вы можете игнорировать все предупреждения о проблемах с дисковым вводом-выводом, поскольку файлы могут находиться в памяти или в каналах других процессов, генерирующих тесты «на лету».

Поместите свои тесты в /dev/shm (новое решение для tmpfs) или создайте тестовый генератор и передайте его по конвейеру.

Я обнаружил, что на соревнованиях по программированию синтаксический анализ чисел до atoi может значительно повысить производительность по сравнению с scanf / fscanf (atoi может отсутствовать, так что будьте готовы реализовать это вручную - это просто).

0 голосов
/ 31 января 2011

Не так много надежды на чтение файла быстрее, поскольку это системный вызов.Но есть много способов разобрать его быстрее, чем scanf со специализированным кодом.

0 голосов
/ 31 января 2011

fgets () или fgetc () работают быстрее, так как им не нужно перетаскивать в программу весь балет списка параметров форматирования / переменных в fscanf () Однако любая из этих двух функций предоставит вам ручное преобразование символов в целое число. Тем не менее, программа в целом будет намного быстрее.

0 голосов
/ 31 января 2011

Как обычно, начните с профилирования, чтобы убедиться, что эта часть действительно является узким местом. На самом деле, кэш FileSystem должен выполнять небольшие операции чтения, которые вы выполняете, не очень дорого, однако чтение больших частей файла в память и последующая работа с памятью могут быть (немного) быстрее. В случае (который я считаю крайне маловероятным) вам нужно сохранять каждый цикл ЦП, вы можете написать свой собственный вариант fscanf, так как вы знаете формат строки и вам нужно поддерживать только один вариант. Но это улучшение также принесло бы небольшой выигрыш, особенно на современных процессорах.

Вход выглядит как в различных конкурсах программирования. В этом случае - оптимизировать алгоритм, а не чтение.

...