Ошибка выделения памяти, когда требуется более 16 ГБ непрерывной памяти - PullRequest
1 голос
/ 09 июля 2020

На моей рабочей станции 128 ГБ памяти. Я не могу выделить массив, занимающий (непрерывную) память более ~ 16 ГБ. Но я могу выделить несколько массивов, каждый из которых занимает около 15 ГБ.

Пример кода:

#include <stdlib.h>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int MM = 1000000;
int NN = 2200; // 2000 is okay, used ~16GB memory; 2200 produces Segmentation fault

double* testMem1d;
testMem1d  = (double*) malloc(MM*NN*sizeof(double));

double* testMem1d1; // NN=2000, allocate another array (or two) at the same time is okay
testMem1d1 = (double*) malloc(MM*NN*sizeof(double));

cout << "testMem1d allocated" << endl;
cin.get(); // here is okay, only malloc but not accessing the array element

cout << "testMem1d[MM*NN-1]=" << testMem1d[MM*NN-1]<< endl;
cout << "testMem1d1[MM*NN-1]=" << testMem1d1[MM*NN-1]<< endl;

// keep running and check the physical memory footprint
for (int tt=0;tt<1000;tt++)
{
    for (int ii=0; ii<MM*NN; ii++)
    {
        testMem1d[ii]=ii;
        testMem1d1[ii]=ii;
    }
    cout << "MM=" << MM << ", NN=" << NN << ", testMem1d[MM*NN-1]=" << testMem1d[MM*NN-1]<< endl;
}
}

Пожалуйста, не обращайте внимания. Я использую mallo c () в c ++, если это не является существенной проблемой . (Это так?) Мне нужно / хочу использовать mallo c () по другим причинам.

Некоторые наблюдения: (1) Выделение нескольких массивов, каждый из которых меньше 15 ГБ, нормально (2) Только do mallo c () в порядке. «Ошибка сегментации» при доступе к элементам массива.

Я подумал, что могут быть некоторые системные настройки, ограничивающие выделение памяти. С ulimit -a вроде все нормально. Поскольку программа может получить доступ к 64-битному виртуальному адресному пространству, я не смог найти никакой причины, которая ограничивает только непрерывное выделение памяти.

ОС: Ubunt 16.04. Я пробовал g ++ и i cc с mcmodel = large. Это кажется несущественным.

uname -a
Linux 4.4.0-143-generic #169-Ubuntu SMP Thu Feb 7 07:56:38 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

ulimit -a 
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 515031
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 515031
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

Редактирует:

(1) mall c () фактически возвращает NULL [в mcleod_ideafix]

(2) [в zwol]

free -m
              total        used        free      shared  buff/cache   available
Mem:         128809       18950      107840        1129        2018      107910
Swap:           974         939          35

1 Ответ

6 голосов
/ 09 июля 2020

Умножение MM*NN*sizeof(double) остается ассоциативным, поэтому происходит как (MM * NN) * sizeof(double). На платформе с 32-битным int, имеющим уравнение MM * NN, равно 2200000000, которое не может быть представлено в 32-битном int и переполняется (и происходит неопределенное поведение) и оборачивается вокруг, что приводит к -2094967296. Затем это значение повышается до общего типа с sizeof(double) до size_t. Это преобразование типа со знаком в тип без знака, где значение со знаком не может быть представлено в типе без знака, поэтому преобразование определяется реализацией. В дополнении до двух с 64-битным расширением знака size_t должно получиться 18446744071614584320. Затем это значение умножается на sizeof(double), которое, как я полагаю, равно 8, оно переполняется несколько раз (что безопасно, size_t без знака) и приводит к 18446744056949813248 байтам. У вашей машины не так много памяти, поэтому malloc возвращает NULL.

Вот почему лучше поставить sizeof в качестве первого операнда в вызове malloc:

malloc(sizeof(double) * MM * NN);

в этом случае операнды будут повышены до size_t перед умножением.

Тем не менее этого будет недостаточно, потому что в testMem1d[MM*NN-1] и в ii<MM*NN все равно происходит переполнение. Поэтому вам следует изменить тип MM и NN на тип с достаточным количеством бит для хранения результата.

size_t MM = 1000000;
size_t NN = 2200;

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

Обратите внимание, что операторы cout << "testMem1d[MM*NN-1]=" << testMem1d[MM*NN-1]<< endl; cout << "testMem1d1[MM*NN-1]=" << testMem1d1[MM*NN-1]<< endl; читают неинициализированную память.

Предпочитайте использовать new в C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...