거대한 메모리 맵을 열어야 해요. 파일은 1TB입니다.
그러나 나는 errno: 를 받았습니다 ENOMEM 12 Cannot allocate memory
. 무엇이 나를 방해하는지 이해하지 못합니다. 요청된 값 RLIMIT_AS
의 결과 : 18446744073709551615. 충분 해. 내 시스템도 64비트이므로 가상 메모리가 너무 작은 것은 아닙니다. ulimit -v
예ulimited
np.lib.format.open_memmap
물리적으로 가능하도록 데이터를 Python으로 만들었습니다 . C언어로 읽어보려고 합니다. 예 , Python을 읽는 데에는 문제가 없습니다 numpy.load('terabytearray.npy', mmap_mode='r')
.
이것은 최소한의 예입니다.
다음과 같이 numpy 배열을 만듭니다.
import numpy as np
shape = (75000, 5000000)
filename = 'datafile.obj'
if __name__ == '__main__':
arr = np.lib.format.open_memmap(filename, mode='w+', dtype=np.float32, shape=shape)
다음과 같이 읽어보세요:
#include <stdbool.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#include <errno.h>
typedef enum {
CNPY_LE, /* little endian (least significant byte to most significant byte) */
CNPY_BE, /* big endian (most significant byte to least significant byte) */
CNPY_NE, /* no / neutral endianness (each element is a single byte) */
/* Host endianness is not supported because it is an incredibly bad idea to
use it for storage. */
} cnpy_byte_order;
typedef enum {
CNPY_B = 0, /* We want to use the values as index to the following arrays. */
CNPY_I1,
CNPY_I2,
CNPY_I4,
CNPY_I8,
CNPY_U1,
CNPY_U2,
CNPY_U4,
CNPY_U8,
CNPY_F4,
CNPY_F8,
CNPY_C8,
CNPY_C16,
} cnpy_dtype;
typedef enum {
CNPY_C_ORDER, /* C order (row major) */
CNPY_FORTRAN_ORDER, /* Fortran order (column major) */
} cnpy_flat_order;
typedef enum {
CNPY_SUCCESS, /* success */
CNPY_ERROR_FILE, /* some error regarding handling of a file */
CNPY_ERROR_MMAP, /* some error regarding mmaping a file */
CNPY_ERROR_FORMAT, /* file format error while reading some file */
} cnpy_status;
#define CNPY_MAX_DIM 4
typedef struct {
cnpy_byte_order byte_order;
cnpy_dtype dtype;
cnpy_flat_order order;
size_t n_dim;
size_t dims[CNPY_MAX_DIM];
char *raw_data;
size_t data_begin;
size_t raw_data_size;
} cnpy_array;
cnpy_status cnpy_open(const char * const fn, bool writable, cnpy_array *arr) {
assert(arr != NULL);
cnpy_array tmp_arr;
/* open, mmap, and close the file */
int fd = open(fn, writable? O_RDWR : O_RDONLY);
if (fd == -1) {
return CNPY_ERROR_FILE;
}
size_t raw_data_size = (size_t) lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
printf("%lu\n", raw_data_size);
if (raw_data_size == 0) {
close(fd); /* no point in checking for errors */
return CNPY_ERROR_FORMAT;
}
if (raw_data_size == SIZE_MAX) {
/* This is just because the author is too lazy to check for overflow on every pos+1 calculation. */
close(fd);
return CNPY_ERROR_FORMAT;
}
void *raw_data = mmap(
NULL,
raw_data_size,
PROT_READ | PROT_WRITE,
writable? MAP_SHARED : MAP_PRIVATE,
fd,
0
);
if (raw_data == MAP_FAILED) {
close(fd);
return CNPY_ERROR_MMAP;
}
if (close(fd) != 0) {
munmap(raw_data, raw_data_size);
return CNPY_ERROR_FILE;
}
/* parse the file */
// cnpy_status status = cnpy_parse(raw_data, raw_data_size, &tmp_arr); // library call ignore
// if (status != CNPY_SUCCESS) {
// munmap(raw_data, raw_data_size);
// return status;
// }
// *arr = tmp_arr;
return CNPY_SUCCESS;
}
int main(){
cnpy_array arr = {};
cnpy_status status = cnpy_open("datafile.obj", false, &arr);
printf("status %i\n",(int) status);
if(status != CNPY_SUCCESS){
printf("failure\n");
printf("errno %i\n", errno);
}
struct rlimit lim;
printf("getrlimit RLIMIT_AS %s\n", (getrlimit(RLIMIT_AS, &lim) == 0 ? "success" : "failure") );
printf("lim.rlim_cur %lu\n", lim.rlim_cur );
printf("lim.rlim_max %lu\n", lim.rlim_max );
printf("RLIM_INFINITY; %lu\n", RLIM_INFINITY );
return 0;
}
컴파일용
gcc -std=c11 -o mmap_testing main.c
나는 그것을 사용하고 있다~quf/cnpy라이브러리에는 numpy 항목과 함께 작동하도록 관련 부분을 포함했습니다.
답변1
void *raw_data = mmap(
NULL,
raw_data_size,
PROT_READ | PROT_WRITE,
writable? MAP_SHARED : MAP_PRIVATE,
fd,
0
);
따라서 가 및 에 대한 매핑을 writable == false
요청합니다 . 이는 매핑된 메모리에 쓸 수 있지만 수정된 파일에는 쓸 수 없음을 의미합니다. 그럼 이렇게 쓰기를 하면 복사본이 만들어지는데(쓰기시 복사, 페이지별로), 어디에 저장되나요? 디스크에 파일을 다시 쓸 수 없으므로 실제 메모리나 스왑에만 상주할 수 있습니다. 따라서 이 호출은 실제로 1TB의 실제 메모리를 할당하거나 예약하도록 요청하고 있습니다. 아마도 당신은 그렇지 않을 것입니다.PROT_READ | PROT_WRITE
MAP_PRIVATE
의견에서 논의한 바와 같이과다 사용이것이 작동하도록 허용합니다. 이 경우 시스템은 1TB를 예약하는 척하지만 실제로는 그렇게 하지 않습니다. 메모리를 너무 많이 쓰면 결국 프로세스가 종료되어 복구할 수 없게 됩니다(그리고 관련되지 않은 다른 프로세스도 종료될 수 있습니다).
하지만 이 경우에는 실제로 해당 메모리에 쓸 필요가 전혀 없는 것처럼 들리 writable == false
므로 PROT_READ
단독으로 사용하세요. 원하는 매개변수는 와 유사합니다 PROT_READ | (writable ? PROT_WRITE : 0)
.