Вызванная память имеет значение NULL

Каковы возможные случаи, когда следующий код может выполнить условие if в следующем фрагменте кода? Что касается меня, я не могу объяснить причину выполнения оператора if.

#include <stdio.h>
#include <stdlib.h>
void main(void){
int Nod = 1024 * 8; //Nod contains the number of nodes
double *MM; //MM is a square matrix it can contain very large number of data 10^10
MM = calloc(8 * Nod * 8 * Nod, sizeof(double));
if (MM == NULL)exit(0);
//then MM will then be passed to some other functions say
eigenvalue(MM);}

Я работаю с кодом FEM, в котором эта проверка находится в середине очень большой программы. Интересный факт: когда я запускаю код, он показывает аномальное поведение. Иногда программа останавливается именно здесь. Иногда просто нормально работает. Стоит упомянуть одну вещь: когда программа запускается с грубой сеткой, т. Е. Когда Nod имеет меньшее количество узлов для расчета, программа просто работает нормально. Но когда используется мелкая сетка, программа, к сожалению, вылетает. Эта программа запускается на мини-рабочей станции с оперативной памятью 128 ГБ. Программа занимает 1 ГБ (или около того) оперативной памяти.


person Ahmed Afif Khan    schedule 13.03.2017    source источник
comment
Эта конкретная программа всегда возвращается с нулевым статусом, независимо от того, какая ветвь была выбрана. Основная функция (и только основная функция) имеет неявный return 0;, когда выполнение достигает закрывающей скобки и завершается.   -  person StoryTeller - Unslander Monica    schedule 13.03.2017
comment
8 * Nod * 8 * Nod равно 2³², поэтому целочисленное переполнение. Используйте более крупный шрифт, чем int.   -  person mch    schedule 13.03.2017
comment
прочитайте справочную страницу для calloc и возможных возвращаемых значений.   -  person Sourav Ghosh    schedule 13.03.2017
comment
Если вы работаете с разреженной матрицей, вы можете просто хранить заполненные ячейки на карте или неупорядоченной карте. Нет необходимости выделять огромные объемы памяти.   -  person doron    schedule 13.03.2017
comment
Крупный шрифт для кого? Nod? Nod обозначает только то, сколько узлов нужно вычислить, и это вполне достижимо для типа данных int.   -  person Ahmed Afif Khan    schedule 13.03.2017


Ответы (2)


Со страницы руководства:

   The malloc() and calloc() functions return a pointer to the allocated  memory  that
   is  suitably  aligned  for  any kind of variable.  On error, these functions return
   NULL.  NULL may also be returned by a successful call to malloc() with  a  size  of
   zero, or by a successful call to calloc() with nmemb or size equal to zero.e here

Теперь в вашем случае это не роса для выделения памяти нулевого размера, поэтому единственная другая причина возврата NULL - это неспособность выделить память. Во фрагменте, который вы показываете, вы выделяете 4294967296 элементов (1024 * 1024 * 64 * 64) размером с двойной (8 байтов), что составляет 32 ГБ оперативной памяти. Теперь ваша система определенно имеет такое количество оперативной памяти, но в любой момент времени она может не иметь всего этого в последовательном выделяемом блоке, поэтому по этой причине calloc может дать сбой.

Еще одна вещь, на которую следует обратить внимание, — это чрезмерное выделение памяти,

/proc/sys/vm/overcommit_memory
or vis sysctl  vm.overcommit_memory

по умолчанию overcommit_memory имеет значение 0, но, возможно, наиболее безопасной комбинацией будет установка значения 2. См. man-страницу proc или документацию ядра/vm/overcommit-accounting для получения более подробной информации об этом.

vm.overcommit_ratio
vm.overcommit_kbytes
vm.nr_overcommit_hugepages

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

Даже при этом я сделал все возможное, чтобы не допустить чрезмерного выделения памяти на 32-битной Linux-машине, но мне все же удалось заставить огромный 32-гигабитный callot не возвращать null (что я считаю странным само по себе, поскольку PAE меньше 32-битная машина может адресовать в общей сложности только 4 ГБ виртуальной памяти, и даже если бы у нее был PAE, она позволяла бы адресовать только 4 ГБ за раз).

person louigi600    schedule 13.03.2017
comment
Просто отметим, что прямо сейчас я тестирую программу на своем ПК, у которого всего 4 ГБ ОЗУ. Когда я разместил вопрос, программа увидела, что в указанной строке произошел сбой. Но что интересно программа работает прямо сейчас...! и с очень плотной сеткой...! Я просто хочу подсказать... что вызывает такое аномальное поведение. - person Ahmed Afif Khan; 13.03.2017
comment
По сути, я написал почти те же предложения, что и второй пункт, сделанный Тоби Спейтом ... всего несколько слов и цифр, чтобы показать, как я пришел к 32Gb. Мне странно, что на машине с оперативной памятью 4Gb вы можете выделить объект 32Gb ... даже если размер long должен был быть один байт (а это определенно не так), он все равно выделял бы 4Gb ... на e 4Gb машина с лайв ос мне трудно поверить что можно выделить 4Gb. Может быть, это витальная память: сколько у вас свопа на машине 4Gb? - person louigi600; 13.03.2017
comment
@AhmedAfifKhan Такое поведение никоим образом не является аномальным — на самом деле вы пытаетесь выделить огромный объем памяти, который может быть или не быть доступен в определенное время — и поскольку вы даже не проверяете возвращаемое значение напрямую после выделения ваша программа дает сбой. - person tofro; 13.03.2017
comment
Пейджинг = 1280 МБ. Он бежит...! Ну, я также озадачен, почему он работает на моей машине...! Я только что упомянул здесь только одну переменную просто для иллюстрации. Объявлено несколько ОГРОМНЫХ массивов размером double...! - person Ahmed Afif Khan; 13.03.2017
comment
Просто из любопытства: как вы работаете над анализом больших данных для ИИ? Если вы отключите подкачку на машине 4Gb, она все еще будет работать? - person louigi600; 13.03.2017
comment
*** НЕТ. Это программа FEM с огромными разреженными матрицами. У меня нет времени на оптимизацию кода. *** Я не думаю, что ему вообще нужна память подкачки. Он использует только около 900 МБ оперативной памяти. *** Сегодня работает... но завтра может не работать... вот тут я и зависаю. Тот же код, та же машина. Но разные результаты в разное время. - person Ahmed Afif Khan; 13.03.2017
comment
но ваш фрагмент выделяет намного больше, чем это ... и я понятия не имею, где calloc, если это делается :( Я не хотел expec MM = calloc(8 * Nod * 8 * Nod, sizeof(double)); чтобы всегда fain на машине 4Gb. - person louigi600; 13.03.2017
comment
Это частично объясняет это stackoverflow.com/questions/19750796/ ... но даже с vm.overcommit_memory = 2 я все еще могу перегрузить гораздо больше, чем SWAP + RAM (overcommit%/100) - person louigi600; 13.03.2017
comment
@ Ахмед Афиф Хан Спасибо за признание моего ответа, это помогает моей репутации. Вы хотите, чтобы я включил в ответ некоторые сообщения, которыми обменивались, потому что есть кое-что интересное, касающееся vm.overcommit_memory? - person louigi600; 15.03.2017
comment
Конечно, почему нет! Я хотел бы дать рекомендацию, т.е. мой вопрос больше концептуальный, чем технический. Мне было интересно узнать, правильно ли я понимаю эту проблему или нет. Пожалуйста, запросите дополнительную информацию, если вам нужно. - person Ahmed Afif Khan; 15.03.2017

Две очевидные проблемы:

  1. Вычисление 8 * Nod * 8 * Nod будет иметь тип int, который может быть недостаточно большим (на вашей платформе) для хранения результата. Вместо этого вы, вероятно, хотите size_t Nod. И вы можете проверить переполнение (возможно, с помощью функций для конкретной платформы, таких как __builtin_mul_overflow() GCC), если значения не являются постоянными.
  2. Вы используете результат calloc(), не проверяя, что это не NULL. Если распределитель не сможет найти достаточно большой непрерывный блок, произойдет сбой, и вы должны проверить это, прежде чем продолжить.

Никогда не игнорировать значение, возвращаемое библиотечными функциями, которые используют его для сообщения об ошибках.

person Toby Speight    schedule 13.03.2017
comment
если бы это было просто переполнение типа данных, не было бы сбоя каждый раз? - person louigi600; 13.03.2017
comment
@ louigi600: это зависит. Переполнение со знаком не определено, поэтому вы ни на что не можете положиться. В лучшем (то есть в худшем) случае вы получите значение, которое при преобразовании в size_t будет достаточно большим для данных. Если вам повезет, вы получите немедленную ошибку или неудовлетворительное значение, что приведет к сбою вашей программы и поощрит некоторую отладку. - person Toby Speight; 13.03.2017
comment
@ Тони С. хорошо, но даже в этом случае, как он выделяет огромный блок 32 ГБ в системе 4 ГБ с недостаточной виртуальной памятью для этого? Я проверил на 32-битной машине (с выходом (1) в случае сбоя malloc), и он выходит с 0 ... как, черт возьми, это происходит? Я озадачен! - person louigi600; 13.03.2017
comment
На 32-битной машине без PAE адресное пространство виртуальной памяти не может быть больше 4 Гб... что выделяет гипотетические 32 Гб... что-то еще идет не так! - person louigi600; 13.03.2017
comment
@luigi, это во многом зависит от вашей платформы. Например, большинство установок Linux будут избыточно выделены, поэтому выделение будет выполнено успешно, но возникнет ошибка, если/когда вы попытаетесь получить доступ ко всему этому. Возможно, стоит установить соответствующие настраиваемые параметры, если это имеет значение (я забыл, как это сделать; я думаю, что есть sysctl для установки глобальных значений, но может быть и настройка для каждого процесса). - person Toby Speight; 13.03.2017
comment
Я попробовал echo 2 › /proc/sys/vm/overcommit_memory, но он все равно завершается с 0 . Это все еще безумие, как это может произойти в 32-битной системе без PAE! - person louigi600; 13.03.2017