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

Есть ли накладные расходы для использования массивов переменной длины?

Есть ли накладные расходы на использование массивов переменной длины? Может ли размер массива быть передан через аргумент командной строки во время выполнения? Почему это введено, по сравнению с автоматическим и динамическим распределением массива?

4b9b3361

Ответ 1

У VLA есть некоторые накладные расходы (по сравнению с "обычным" массивом с именем типа компиляции).

Во-первых, он имеет длину времени выполнения, и все же язык предоставляет вам средства для получения фактического размера массива во время выполнения (используя sizeof). Это немедленно означает, что фактический размер массива должен быть где-то сохранен. Это приводит к некоторым незначительным издержкам памяти для каждого массива. Однако, поскольку VLA могут быть объявлены только как автоматические объекты, эта накладная память не является чем-то, что кто-либо мог бы заметить. Это похоже на объявление дополнительной локальной переменной интегрального типа.

Во-вторых, VLA обычно выделяется в стеке, но из-за его переменного размера в общем случае его точное местоположение в памяти неизвестно во время компиляции. По этой причине базовая реализация обычно должна реализовывать ее как указатель на блок памяти. Это вводит некоторые дополнительные накладные расходы памяти (для указателя), что опять-таки совершенно незначительно по причинам, описанным выше. Это также приводит к незначительным накладным расходам, поскольку мы должны прочитать значение указателя, чтобы найти фактический массив. Это те же самые накладные расходы, которые вы получаете при доступе к массивам malloc -ed (и не получаете с массивами с указанным размером времени компиляции).

Поскольку размер VLA - это целочисленное значение времени выполнения, его можно, конечно, передать в качестве аргумента командной строки. VLA не заботится о том, откуда его размер.

VLA были представлены как массивы времени выполнения с низкой стоимостью распределения/освобождения. Они подходят между "обычными" именованными массивами размера времени компиляции (которые имеют практически нулевую стоимость распределения-освобождения, но фиксированный размер) и malloc -ed массивы (которые имеют размер времени выполнения, но относительно высокую стоимость распределения-освобождения).

VLA подчиняются [почти] тем же правилам жизни, зависящим от объема, как автоматические (то есть локальные) объекты, а это означает, что в общем случае они не могут заменить массивы malloc -ed. Они применимы к ситуациям, когда вам нужен массив с быстрым временем выполнения с типичным временем автономной работы.

Ответ 2

Накладные расходы во время выполнения с массивами переменной длины, но вам придется работать достаточно сложно, чтобы их измерить. Обратите внимание, что sizeof(vla) не является константой времени компиляции, если vla - массив переменной длины.

Размер массива может быть передан функции во время выполнения. Если вы решите взять размер из аргумента командной строки и преобразовать его в целое число и передать его функции во время выполнения, пусть будет так - он будет работать.

Массивы переменной длины используются, потому что переменные автоматически распределяются по правильному размеру и автоматически освобождаются при выходе из функции. Это позволяет избежать чрезмерного выделения пространства (выделяя достаточное пространство для максимально возможного размера, когда вы в основном работаете с минимальными размерами), и избегает проблем с очисткой памяти.

Кроме того, с многомерными массивами AFAIK ведет себя скорее как Fortran - вы можете динамически настраивать все измерения, а не застревать с фиксированными размерами для всех, кроме ведущего измерения массива.


Конкретные доказательства некоторых служебных расходов во время выполнения для VLA - по крайней мере, с GCC 4.4.2 на SPARC (Solaris 10).

Рассмотрим два файла ниже:

vla.c - использование массива переменной длины

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int vla[n][m];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            vla[i][j] = 0;
        }
        vla[i][i] = 1;
    }
    return(sizeof(vla));
}

fla.c - использование массива с фиксированной длиной

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int fla[32][32];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            fla[i][j] = 0;
        }
        fla[i][i] = 1;
    }
    return(sizeof(fla));
}

Размеры компиляции и объектного файла

В целях сравнения имена локального массива различаются (vla vs fla), а размеры в массиве различаются, когда они объявлены - в противном случае файлы одинаковы.

Я скомпилировал, используя:

$ gcc -O2 -c -std=c99 fla.c vla.c

Размер объектного файла несколько отличается - как измеряется как "ls", так и "size":

$ ls -l fla.o vla.o
-rw-r--r--   1 jleffler rd          1036 Jan  9 12:13 fla.o
-rw-r--r--   1 jleffler rd          1176 Jan  9 12:13 vla.o
$ size fla.o vla.o
fla.o: 530 + 0 + 0 = 530
vla.o: 670 + 0 + 0 = 670

Я не проводил подробное тестирование, чтобы узнать, сколько накладных расходов исправлено и сколько переменных, но есть накладные расходы при использовании VLA.

Ответ 3

Мне просто интересно, есть ли накладные расходы на использование массивов переменной длины?

Неа

Может ли размер массива быть передан через аргумент командной строки во время выполнения?

Да.

Почему это введено, по сравнению с автоматическим и динамическим распределением массива?

Автоматическое выделение разрешает только фиксированный размер, известный во время компиляции.

Динамическое выделение (malloc) будет хранить массив в куче, который имеет большое пространство памяти, но медленнее для доступа.

VLA работает, помещая массив в стек. Это делает выделение и доступ чрезвычайно быстрым, , но, как правило, стек небольшой (от нескольких килобайт), а когда VLA переполняет стек, он неотличим от бесконечной рекурсии.

Ответ 4

Для VLA должно быть очень мало накладных расходов (самое большее это должно привести к добавлению указателя стека). Динамическое распределение требует ручного управления памятью и медленнее, чем распределение VLA на основе стека, а для "автоматического" объявления массива требуется выражение времени компиляции для размера массива. Однако имейте в виду, что если происходит переполнение стека, это приведет к поведению undefined, поэтому сохраняйте VLA относительно небольшим.

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