Подтвердить что ты не робот

Как большой может быть malloc в C?

У меня есть malloc в C, который равен 26901 ^ 2 * sizeof (double)

Это заставило меня подумать, какая самая большая ценность может быть здесь?

Кроме того, у меня возникли бы проблемы с определением макроса для доступа к этому двумерному массиву?

 #define DN(i,j) ((int)i * ny + (int)j)

Потому что это, кажется, не работает для меня - или я, по крайней мере, не уверен, что это так. Я не могу понять, как сделать totalview погружением на макрос, чтобы рассказать мне, что на самом деле смотрит A [DN (indx, jndx)].

4b9b3361

Ответ 1

Наблюдения

Предполагая, что типичный распределитель, например, один glibc, есть некоторые наблюдения:

  • Независимо от того, действительно ли используется память, регион должен быть зарезервирован смежно в виртуальной памяти.
  • Наибольшие свободные смежные области зависят от использования памяти в существующих областях памяти и доступности этих регионов malloc.
  • Практика отображения зависит от архитектуры и ОС. Кроме того, эти системные вызовы для получения областей памяти подвержены влиянию этих методов (например, malloc для вызова страниц mmap).

Эксперимент

Здесь простая программа для размещения максимально возможного блока (скомпилируйте с помощью gcc largest_malloc_size.c -Wall -O2:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static void *malloc_wrap(size_t size)
{
    void *p = malloc(size);
    if (p) {
        printf("Allocated %zu bytes from %p to %p\n", size, p, p + size);
    }
    else {
        printf("Failed to allocated %zu bytes\n", size);
    }
    return p;
}

int main()
{
    size_t step = 0x1000000;
    size_t size = step;
    size_t best = 0;
    while (step > 0)
    {
        void *p = malloc_wrap(size);
        if (p) {
            free(p);
            best = size;
        }
        else {
            step /= 0x10;
        }
        size += step;
    }
    void *p = malloc_wrap(best);
    if (p) {
        pause();
        return 0;
    }
    else {
        return 1;
    }
}

Запуск вышеуказанной программы (./a.out) на моей машине Linux stanley 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux получает этот результат:

<snip>
Allocated 2919235584 bytes from 0x9763008 to 0xb7763008
Allocated 2936012800 bytes from 0x8763008 to 0xb7763008
Failed to allocated 2952790016 bytes
Failed to allocated 2953838592 bytes
Failed to allocated 2953904128 bytes
Failed to allocated 2953908224 bytes
Allocated 2936012800 bytes from 0x85ff008 to 0xb75ff008

Это распределение точно 2800MiB. Наблюдение соответствующего отображения из /proc/[number]/maps:

<snip>
0804a000-0804b000 rw-p 00001000 08:07 3413394    /home/matt/anacrolix/public/stackoverflow/a.out
085ff000-b7600000 rw-p 00000000 00:00 0          [heap]
b7600000-b7621000 rw-p 00000000 00:00 0 
b7621000-b7700000 ---p 00000000 00:00 0 
b7764000-b7765000 rw-p 00000000 00:00 0 
b7765000-b78b8000 r-xp 00000000 08:08 916041     /lib/tls/i686/cmov/libc-2.11.1.so
<snip>
bfc07000-bfc1c000 rw-p 00000000 00:00 0          [stack]

Заключение

Похоже, что куча была расширена в области между данными программы и кодом и сопоставлениями разделяемых библиотек, которые сидят на фоне памяти пользователя/ядра (очевидно, 3G/1G в этой системе).

Этот результат предполагает, что максимальное выделяемое пространство с использованием malloc примерно равно:

  • Область пользовательского пространства (пример 3GB в примере)
  • Меньше смещения к началу кучи (программный код и данные)
  • Меньше места, зарезервированного для стека основного потока
  • Меньше пространства, занимаемого всеми отображаемыми в общих библиотеках
  • Наконец, самая большая смежная область, которая может быть найдена базовым системным вызовом в пределах области, доступной для кучи (которая может быть фрагментирована другими сопоставлениями)

Примечания

Что касается реализаций glibc и Linux, то следующие основные фрагменты кода представляют большой интерес:

malloc

   Normally, malloc() allocates memory from the heap, and adjusts the size
   of the heap as required, using sbrk(2).  When allocating blocks of mem‐
   ory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation
   allocates the memory as a  private  anonymous  mapping  using  mmap(2).
   MMAP_THRESHOLD  is  128  kB  by  default,  but is adjustable using mal‐
   lopt(3).

mmap

   MAP_ANONYMOUS
          The mapping is not backed by any file; its contents are initial‐
          ized to zero.

Послесловие

Этот тест был выполнен на ядре x86. Я бы ожидал подобных результатов от ядра x86_64, хотя и с гораздо большими областями памяти. Другие операционные системы могут отличаться при размещении сопоставлений и обработке больших malloc s, поэтому результаты могут значительно отличаться.

Ответ 2

Это зависит от вашей реализации malloc!

Согласно Wikipedia, "Начиная с версии v2.3, библиотека GNU C (glibc) использует модифицированный ptmalloc2, который сам основан на dlmalloc v2.7.0". dlmalloc относится к реализации Doug Lea malloc. В этой реализации важно отметить, что большие mallocs выполняются через функциональные возможности файлов с памятью операционной системы, поэтому эти блоки могут быть довольно большими, даже без многих проблем поиска смежного блока.

Ответ 3

Ответ на вопрос malloc (зависит от ОС, который вы не укажете), поэтому об этом определите:

#define DN(i,j) ((int)i * ny + (int)j)

не совсем безопасен, поскольку кто-то может сделать DN(a+b,c), который расширяется до

((int)a+b * ny + (int)c)

что, вероятно, не то, что вы хотели. Поэтому поставьте там много круглых скобок:

#define DN(i,j) ((int)(i) * ny + (int)(j))

чтобы увидеть, что указывает DN(indx,jndx), просто printf("%d\n",DN(indx,jndx));

Ответ 4

Параметр размера в вызове malloc имеет тип size_t, который зависит от реализации. Подробнее см. этот вопрос.

Ответ 5

Это заставило меня подумать, какая самая большая ценность может быть здесь?

26'901 ^ 2 = 723'663'801. Если ваш double равен 8 байтам, то он меньше 8 ГБ. Я не вижу проблем, выделяя большую часть памяти, и мои приложения обычно распределяют (на 64-битных системах) гораздо больше. (Наибольшее потребление памяти, которое я когда-либо видел, было 420 ГБ (в Solaris 10 numa с 640 ГБ ОЗУ) с наибольшим непрерывным блоком ~ 24 ГБ.)

Наибольшее значение трудно идентифицировать, так как оно зависит от платформы: аналогично 32-битным системам это зависит от разделения пространства пользователя/пространства ядра. Как сейчас обстоят дела, я думаю, что сначала нужно дойти до пределов фактической физической памяти - до достижения предела того, что может выделить libc. (И ядру все равно, он просто расширяет виртуальную память часто, даже не учитывая, есть ли достаточное количество ОЗУ для ее подключения.)

Ответ 6

Самый большой блок памяти, который вы можете задать malloc() for, является самым большим значением size_t - это SIZE_MAX от <limits.h>. Наибольшая сумма, которую вы можете запросить, явно зависит от операционной системы и конфигурации отдельной машины.

Ваш макрос небезопасен. Он выполняет вычисление индекса с переменной int, которая требуется только для диапазона до 32767. Любое значение выше этого может привести к переполнению подписей, что приводит к поведению undefined. Вероятно, вам лучше всего выполнить вычисление как size_t, так как этот тип должен иметь возможность удерживать любой допустимый индекс массива:

#define DN(i, j) ((size_t)(i) * ny + (size_t)(j))

(Обратите внимание, что если вы укажете отрицательные значения для i или j, вы получите индекс далеко за пределами границ).