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

StackOverflow https://stackoverflow.com/questions/2034712

  •  19-09-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

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

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

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

Другие советы

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

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

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

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


Конкретные доказательства некоторых накладных расходов во время выполнения 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», так и по «размеру»:

$ 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 существуют накладные расходы.

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

Неа

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

Да.

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top