Есть ли накладные расходы на использование массивов переменной длины? - PullRequest
46 голосов
/ 09 января 2010

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

Ответы [ 4 ]

43 голосов
/ 10 января 2010

VLA имеет некоторые издержки (по сравнению с «обычным» именованным массивом размера компиляции).

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

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

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

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

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

28 голосов
/ 09 января 2010

Существуют некоторые накладные расходы во время выполнения с массивами переменной длины, но вам придется приложить немало усилий, чтобы измерить их. Обратите внимание, что 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 против 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 есть издержки.

4 голосов
/ 09 января 2010

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

Неа

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

Да.

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

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

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

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

1 голос
/ 09 января 2010

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

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

...