(Это не ответ, а важное замечание для всех, кто работает с большими наборами данных в Linux)
Это не то, как вы используете очень большие - порядка терабайт и выше - наборы данных в Linux.
Когда вы используете malloc()
или mmap()
(библиотека GNU C будет использовать mmap()
внутри для больших распределений в любом случае) для выделения частной памяти, ядро ограничивает размер размером (теоретически) доступной оперативной памяти и SWAP, умноженного на коэффициент переопределения.
Проще говоря, мы знаем, что наборы данных размером больше оперативной памяти, возможно, придется выгрузить, поэтому размер текущего свопа будет влиять на то, насколько большие выделения разрешены.
Чтобы обойти это, мы создаем файл, который будет использоваться как «своп» для данных, и сопоставляем его с помощью флага MAP_NORESERVE
. Это говорит ядру, что мы не хотим использовать стандартную подкачку для этого сопоставления. (Это также означает, что если по какой-либо причине ядро не может получить новую страницу поддержки, приложение получит сигнал SIGSEGV
и умрет.)
Большинство файловых систем в Linux поддерживают разреженные файлы. Это означает, что у вас может быть файл размером в терабайт, который занимает всего несколько килобайт фактического дискового пространства, если большая часть его содержимого не записана (и, следовательно, является нулями). (Создать разреженные файлы легко; вы просто пропускаете длинные серии нулей. Пробить дырку сложнее, поскольку запись нулей требует обычного дискового пространства, вместо этого необходимо использовать другие методы.)
Вот пример программы mapfile.c, которую можно использовать для исследования:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
const char *filename;
size_t page, size;
int fd, result;
unsigned char *data;
char dummy;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s MAPFILE BYTES\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
page = sysconf(_SC_PAGESIZE);
if (page < 1) {
fprintf(stderr, "Unknown page size.\n");
return EXIT_FAILURE;
}
filename = argv[1];
if (!filename || !*filename) {
fprintf(stderr, "No map file name specified.\n");
return EXIT_FAILURE;
}
if (sscanf(argv[2], " %zu %c", &size, &dummy) != 1 || size < 3) {
fprintf(stderr, "%s: Invalid size in bytes.\n", argv[2]);
return EXIT_FAILURE;
}
if (size % page) {
/* Round up to next multiple of page */
size += page - (size % page);
fprintf(stderr, "Adjusted to %zu pages (%zu bytes)\n", size / page, size);
}
do {
fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
fprintf(stderr, "Cannot create %s: %s.\n", filename, strerror(errno));
return EXIT_FAILURE;
}
do {
result = ftruncate(fd, (off_t)size);
} while (result == -1 && errno == EINTR);
if (result == -1) {
fprintf(stderr, "Cannot resize %s: %s.\n", filename, strerror(errno));
unlink(filename);
close(fd);
return EXIT_FAILURE;
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, fd, 0);
if ((void *)data == MAP_FAILED) {
fprintf(stderr, "Mapping failed: %s.\n", strerror(errno));
unlink(filename);
close(fd);
return EXIT_FAILURE;
}
fprintf(stderr, "Created file '%s' to back a %zu-byte mapping at %p successfully.\n", filename, size, (void *)data);
fflush(stdout);
fflush(stderr);
data[0] = 1U;
data[1] = 255U;
data[size-2] = 254U;
data[size-1] = 127U;
fprintf(stderr, "Mapping accessed successfully.\n");
munmap(data, size);
unlink(filename);
close(fd);
fprintf(stderr, "All done.\n");
return EXIT_SUCCESS;
}
Скомпилируйте его, например,
gcc -Wall -O2 mapfile.c -o mapfile
и запустите его без аргументов, чтобы увидеть использование.
Программа просто устанавливает сопоставление (скорректированное до кратного размера текущей страницы) и обращается к первым двум и последним двум байтам сопоставления.
На моей машине с SMP-ядром 4.2.0-42-generic # 49 ~ 14.04.1-Ubuntu на x86-64 в файловой системе ext4 я не могу отобразить полный петабайт. Максимальный размер составляет около 17 592 186 040 320 байт (2 44 -4096) - 16 ТиБ - 4 КиБ -, что составляет 4 294 967 296 страниц размером 4096 байт (2 32 страницы по 2 12 байтов каждый). Похоже, что ограничение наложено файловой системой ext4, поскольку сбой происходит в вызове ftruncate()
(до того, как будет выполнено сопоставление).
(На tmpfs я могу получить около 140 187 732 541 440 байтов или 127,5 ТиБ, но это просто уловка, потому что tmpfs поддерживается ОЗУ и свопингом, а не фактическим устройством хранения. Так что это не вариант для реальной работы с большими данными. напомнить xfs будет работать для действительно больших файлов, но я слишком ленив, чтобы форматировать раздел или даже искать спецификации; я не думаю, что кто-то на самом деле прочитает этот пост, хотя информация здесь была для меня очень полезна за последнее десятилетие или около того.)
Вот как этот пример выглядит на моей машине (с использованием оболочки Bash):
$ ./mapfile datafile $[(1<<44)-4096]
Created file 'datafile' to back a 17592186040320-byte mapping at 0x6f3d3e717000 successfully.
Mapping accessed successfully.
All done.
.
person
Nominal Animal
schedule
25.07.2016
volatile
? Скорее всего, это чушь и не поддается никаким оптимизациям. - person too honest for this site   schedule 25.07.2016volatile
, чтобы избежать оптимизации, при которой нет доступа к памяти, и будет работать в регистрах. Чтобы показать, что мы действительно работаем с памятью. - person Alex   schedule 25.07.2016(int)
приведение не требуется в... %d \n", (int)(ptr[petabyte - 1LL]));
, это неявно. - person Sourav Ghosh   schedule 25.07.2016petabyte
комментарий неверен. Это не 10 ^ 15. Это 2 ^ 50. (Если правильно посчитал биты). - person Zan Lynx   schedule 25.07.2016malloc
не вернет нулевой указатель. - person too honest for this site   schedule 25.07.2016-Wconversion
. - person too honest for this site   schedule 25.07.2016free
, вы заметите, что он вылетает. Записывая вptr[-1]
, вы уничтожили хранилища данных malloc (в этой реализации) перед выделенным блоком памяти (размером 0 байт), и, таким образом, вызовfree((void*)ptr)
завершился аварийно. - person Daniel Fischer   schedule 27.07.2016