Ваша идея сама по себе неплохая. Если мы предположим, что файл с разделителем новой строки (то есть: вы можете отрезать между строками без значка), вы можете найти лимиты блоков с чем-то подобным (вырванные из другой программы, поэтому, пожалуйста, сначала проверьте)
// 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);
).
Вы были совершенно не уверены в том, какие данные вам нужны для работы, поэтому вышеуказанное может и не представлять для вас ни малейшего интереса.
Вы хотите, чтобы разные темы читали разные части файла одновременно? – yano
Вы хотите прочитать записи «\ n» с разделителями? Что должно произойти, если линии пересекают границы страниц? – wildplasser
@yano Yesss, как вы сказали, есть ли способ сделать это? – superrman777