2016-09-26 9 views
-3

Я пытаюсь прочитать большой .txt-файл в C. Я сделал версию с fgets(), но производительность ограничена I/O. Поэтому мне нужно, чтобы что-то другое могло улучшить производительность, чем fgets(), и я обнаружил, что mmap() не будет ограничен с помощью ввода-вывода. Поэтому мой вопрос: возможно ли это сделать с помощью mmap() и multi threaded (POSIX Thread)? А вот что мне нужно:читать файл с mmap несколькими потоками в C

Different threads to read(mmap() or something else) different parts of the file simultaneously

Я не могу нашел какой-либо ресурс о ттаре() с многопоточным онлайн, может кто-то пожалуйста, помогите мне с каким-то примером кодом, и объяснить? Я был бы очень благодарен вашей помощи, спасибо

+1

Вы хотите, чтобы разные темы читали разные части файла одновременно? – yano

+1

Вы хотите прочитать записи «\ n» с разделителями? Что должно произойти, если линии пересекают границы страниц? – wildplasser

+0

@yano Yesss, как вы сказали, есть ли способ сделать это? – superrman777

ответ

0

страница Linux руководство для mmap состояний:

ММАП - карта файлов или устройств в память

#include <sys/mman.h> 
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); 

Описание для mmap говорит :

mmap() создает новое сопоставление в спа-центре виртуального адреса ce вызывающего процесса. Исходный адрес для нового сопоставления указан в addr. Аргумент length указывает длину отображения.

И вот пример кода с man-страниц.

#include <sys/mman.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) 
int main(int argc, char *argv[]) 
{ 
    char *addr; 
    int fd; 
    struct stat sb; 
    off_t offset, pa_offset; 
    size_t length; 
    ssize_t s; 
    if (argc < 3 || argc > 4) { 
     fprintf(stderr, "%s file offset [length]\n", argv[0]); 
     exit(EXIT_FAILURE); 
    } 
    fd = open(argv[1], O_RDONLY); 
    if (fd == -1) 
     handle_error("open"); 
    if (fstat(fd, &sb) == -1)   /* To obtain file size */ 
     handle_error("fstat"); 
    offset = atoi(argv[2]); 
    pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1); 
     /* offset for mmap() must be page aligned */ 
    if (offset >= sb.st_size) { 
     fprintf(stderr, "offset is past end of file\n"); 
     exit(EXIT_FAILURE); 
    } 
    if (argc == 4) { 
     length = atoi(argv[3]); 
     if (offset + length > sb.st_size) 
      length = sb.st_size - offset; 
    } else { /* No length arg ==> display to end of file */ 
     length = sb.st_size - offset; 
    } 
    addr = mmap(NULL, length + offset - pa_offset, PROT_READ, 
       MAP_PRIVATE, fd, pa_offset); 
    if (addr == MAP_FAILED) 
     handle_error("mmap"); 
    s = write(STDOUT_FILENO, addr + offset - pa_offset, length); 
    if (s != length) { 
     if (s == -1) 
      handle_error("write"); 
     fprintf(stderr, "partial write"); 
     exit(EXIT_FAILURE); 
    } 
    exit(EXIT_SUCCESS); 
} 

Ничто из этого не является моей работой, все это на страницах руководства Linux.

+0

Спасибо за ваш ответ, но мне нужно, чтобы mmap с несколькими потоками – superrman777

1

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

// just in case 
#define _LARGEFILE_SOURCE 
#define _BSD_SOURCE 
#define _POSIX_C_SOURCE 200112L 

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <errno.h> 
#include <string.h> 

// TODO: should be calculated 
#define FILE_PARTS 100 
// TODO: should not be global 
off_t positions[FILE_PARTS + 1]; 

int slice_file(FILE * fp) 
{ 
    off_t curr_pos = 0; 
    off_t filesize = 0; 
    off_t chunk_size = 0; 
    int fd; 
    int i, res; 
    char c; 

    struct stat sb; 

    // get size of file 
    fd = fileno(fp); 
    if (fd == -1) { 
    fprintf(stderr, "EBADF in prepare_and_backup() for data-file pointer\n"); 
    return 0; 
    } 

    if (fstat(fd, &sb) == -1) { 
    fprintf(stderr, "fstat() failed\n"); 
    return 0; 
    } 
    // check if it is a regular file 
    if ((sb.st_mode & S_IFMT) != S_IFREG) { 
    fprintf(stderr, "Not a regular file\n"); 
    return 0; 
    } 
    // TODO: check if filesize and chunksize >> 1 
    filesize = sb.st_size; 
    chunk_size = filesize/((off_t) FILE_PARTS); 

    positions[0] = 0; 
    curr_pos = 0; 

    for (i = 1; i < FILE_PARTS; i++) { 
    res = fseeko(fp, curr_pos, SEEK_SET); 
    if (res == -1) { 
     fprintf(stderr, "Error in fseeko(): %s\n", 
       strerror(errno)); 
     return 0; 
    } 
    curr_pos += chunk_size; 
    // look for the end of the line to cut at useful places 
    while ((c = fgetc(fp)) != EOF) { 
     curr_pos++; 
     // TODO: add code to honor Apple's special needs 
     if (c == '\n') { 
     c = fgetc(fp); 
     if (c == EOF) { 
      break; 
     } 
     curr_pos++; 
     break; 
     } 
    } 
    positions[i] = curr_pos - 1; 
    } 
    // Position of the end of the file 
    positions[i] = filesize; 
    // Is that even needed? 
    rewind(fp); 
    return 1; 
} 

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

EDIT

Декларация mmap является

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 

Если вы не заботитесь для конкретного адреса вы установите его в NULL.
length - это количество байтов, на которое вы хотите получить инициализацию карты, то есть в этом случае: заполнено содержимым из файлового дескриптора fd.
Начало этой начинки устанавливается offset с одним неудобным предупреждением: оно должно быть кратным размеру страницы (укажите точное количество цифр sysconf(_SC_PAGE_SIZE)). Не большая проблема, просто установите его на страницу перед запуском и начните работу при фактическом запуске, вся необходимая информация существует. Вы можете (и должны!) Игнорировать остальную часть этой страницы.

Или вы берете весь файл и сопоставляете его и используете его, как если бы вы использовали файл на диске: дайте каждому потоку блок этой карты (необходимая информация в positions) и работайте оттуда.

Преимущество первого: у вас есть несколько блоков памяти, которые могут быть легко перемещены ОС, и вы можете или не иметь меньше промахов в кеше с несколькими процессорами. Это необходимо, даже если вы запускаете кластер или любую другую архитектуру, где каждый процессор/группа процессоров имеет свою собственную RAM или, по крайней мере, очень большой кеш.

Преимущество последнего: проще реализовать, но у вас есть один большой кусок карты. Это может повлиять или не повлиять на время выполнения.

Подсказка: мои впечатления от современных быстрых SSD: скорость чтения настолько высока в эти дни, вы можете легко начать с прямого доступа к файлам, а не для сопоставления. Даже с довольно медленным «нормальным» жестким диском вы получаете разумные скорости. Программа, с которой я разорвала этот фрагмент выше, должна была искать более 120 ГБ большого CSV-файла, не имея достаточного количества ОЗУ для полной загрузки, даже не достаточного места на диске для загрузки его в какую-то БД (да, это была пара много лет назад). Это был ключ -> «много, из разных, значений» файла, и, к счастью, уже отсортирован. Таким образом, я сделал небольшой (такой большой, как я мог поместиться на диске) индексный файл для него с помощью метода выше (KEY-> position), хотя и намного больше блоков, чем 100 в моем примере. Ключи в индексном файле также были отсортированы, поэтому вы нашли правильный блок, если ключ, который вы искали, был больше (данные отсортированы в порядке возрастания), чем индексная запись, что означает, что ключ находится в блоке до этого если он существует. Блоки были достаточно маленькими, чтобы некоторые из них в ОЗУ работали как кеш, но полученные не так много, входящие запросы были довольно равномерными.

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

Смешная примечание: ключи были буквенно-цифровыми и алгоритм сортировки отсортировал их «aAbBcC ...», это означает, что вы не можете использовать strcmp напрямую. Сделал меня поцарапать голову на некоторое время, но решение довольно просто: сравните игнорирующее дело (например, strcasecmp, если оно доступно), и если оно не равно , то возвратите этот результат, в противном случае верните обратное к результату нормального strncmp (например, только return -strcmp(a,b);).

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

+0

К сожалению, следующий шаг о mmap - mmap (позиции [i-> FILE_PARTS], chunk_size, PROT_READ, MAP_PRIVATE, fd, 0)? – superrman777