При использовании и злоупотреблении Alloca
-
24-10-2019 - |
Вопрос
Я работаю над системой обработки событий в мягкой реальности. Я хотел бы свести к минимуму столько вызовов в моем коде, которые имеют не определенное время. Мне нужно построить сообщение, которое состоит из строк, чисел, временных метров и GUID. Вероятно, а std::vector
из boost::variant
S.
Я всегда хотел использовать alloca
в прошлом кодексе аналогичного характера. Однако, когда кто -то изучает литературу по программированию систем, всегда есть огромные предостережения против этого вызова функции. Лично я не могу вспомнить машину класса сервера за последние 15 лет, у которой нет виртуальной памяти, и я точно знаю, что стек Windows выращивает страницу виртуальной памяти в время, поэтому я предполагаю Unices делают тоже. Здесь нет кирпичной стены (больше), стек с такой же вероятностью не хватит места, как и куча, так что дает? Почему люди не ходят по Алоке? Я могу вспомнить о многих вариантах использования ответственного использования Alloca (обрабатывает кого-нибудь строки?).
В любом случае, я решил проверить разницу в производительности (см. Ниже), и между Alloca и Malloc существует 5-кратная разница в скорости (тест охватывает, как я буду использовать Alloca). Итак, все изменилось? Должны ли мы просто поддерживать осторожность и использовать alloca
(завернут в std::allocator
) всякий раз, когда мы можем быть абсолютно уверены в жизни наших объектов?
Я устал жить в страхе!
Редактировать:
Итак, есть пределы, для Windows это ограничение на ссылку. Для Unix это кажется настраиваемым. Похоже, что распределитель памяти, выдвинутые на страницу, в порядке: D Кто-нибудь знает о портативной реализации общего назначения: D?
Код:
#include <stdlib.h>
#include <time.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
using namespace boost::posix_time;
int random_string_size()
{
return ( (rand() % 1023) +1 );
}
int random_vector_size()
{
return ( (rand() % 31) +1);
}
void alloca_test()
{
int vec_sz = random_vector_size();
void ** vec = (void **) alloca(vec_sz * sizeof(void *));
for(int i = 0 ; i < vec_sz ; i++)
{
vec[i] = alloca(random_string_size());
}
}
void malloc_test()
{
int vec_sz = random_vector_size();
void ** vec = (void **) malloc(vec_sz * sizeof(void *));
for(int i = 0 ; i < vec_sz ; i++)
{
vec[i] = malloc(random_string_size());
}
for(int i = 0 ; i < vec_sz ; i++)
{
free(vec[i]);
}
free(vec);
}
int main()
{
srand( time(NULL) );
ptime now;
ptime after;
int test_repeat = 100;
int times = 100000;
time_duration alloc_total;
for(int ii=0; ii < test_repeat; ++ii)
{
now = microsec_clock::local_time();
for(int i =0 ; i < times ; ++i)
{
alloca_test();
}
after = microsec_clock::local_time();
alloc_total += after -now;
}
std::cout << "alloca_time: " << alloc_total/test_repeat << std::endl;
time_duration malloc_total;
for(int ii=0; ii < test_repeat; ++ii)
{
now = microsec_clock::local_time();
for(int i =0 ; i < times ; ++i)
{
malloc_test();
}
after = microsec_clock::local_time();
malloc_total += after-now;
}
std::cout << "malloc_time: " << malloc_total/test_repeat << std::endl;
}
выход:
hassan@hassan-desktop:~/test$ ./a.out
alloca_time: 00:00:00.056302
malloc_time: 00:00:00.260059
hassan@hassan-desktop:~/test$ ./a.out
alloca_time: 00:00:00.056229
malloc_time: 00:00:00.256374
hassan@hassan-desktop:~/test$ ./a.out
alloca_time: 00:00:00.056119
malloc_time: 00:00:00.265731
-Редакция: Результаты на домашней машине, кланге и Google Perftools--
G++ without any optimization flags
alloca_time: 00:00:00.025785
malloc_time: 00:00:00.106345
G++ -O3
alloca_time: 00:00:00.021838
cmalloc_time: 00:00:00.111039
Clang no flags
alloca_time: 00:00:00.025503
malloc_time: 00:00:00.104551
Clang -O3 (alloca become magically faster)
alloca_time: 00:00:00.013028
malloc_time: 00:00:00.101729
g++ -O3 perftools
alloca_time: 00:00:00.021137
malloc_time: 00:00:00.043913
clang++ -O3 perftools (The sweet spot)
alloca_time: 00:00:00.013969
malloc_time: 00:00:00.044468
Решение
Ну, во -первых, даже если много виртуальной памяти не означает, что ваш процесс будет разрешен его заполнить. На *nix существуют ограничения размера стека, тогда как куча намного более прощающая.
Если вы собираетесь выделить только несколько сотен / тысячи байтов, конечно, продолжайте. Все, что кроме этого, будет зависеть от того, какие ограничения (Ulimit) находятся в любой системе, и это всего лишь рецепт катастрофы.
Почему использование Alloca () не считается хорошей практикой?
На моем ящике для разработки на работе (Gentoo) у меня есть предел размера стека по умолчанию 8192 КБ. Это не очень большое, и если Alloca переполняет стек, то поведение не определен.
Другие советы
Я думаю, что вам нужно быть немного осторожным, понимая, что такое Alloca. В отличие от Malloc, которая идет на кучу, поиск по ведрах и связанных спискам различных буферов, Alloca просто принимает ваш регистр стека (ESP на x86) и перемещает его, чтобы создать «отверстие» в стеке вашей потока, где вы можете хранить все, что хотите. Вот почему это безумно, только одна (или мало) инструкции по сбору.
Таким образом, как отмечали другие, это не «виртуальная память», о которой вам нужно беспокоиться, а размер, зарезервированный для стека. Хотя другие ограничиваются «несколькими сотнями байтов», если вы знаете свое приложение и осторожно о нем, мы выделили до 256 КБ без каких -либо проблем (размер стека по умолчанию, по крайней мере для визуальной студии, составляет 1 МБ, и вы всегда можете Увеличьте его, если вам нужно).
Кроме того, вы действительно не можете использовать Alloca в качестве распределителя общего назначения (т.е. обертывание его в другую функцию), потому что любая аллока памяти выделяется для вас, эта память будет исчезла, когда кадр стека для текущей функции будет всплыть (т.е. при выходе функции).
Я также видел, как некоторые люди говорят, что Alloca не совсем кроссплатформенная совместима, но если вы пишете конкретное приложение для конкретной платформы и у вас есть возможность использовать Alloca, иногда это лучший вариант, который у вас есть. Вы понимаете последствия увеличения использования стека.
Во -первых, это потому, что alloca
Память очень сложно контролировать. Он нетипирован, умирает при первой же возможности, что делает его не очень полезным. Кроме того, alloca
Имеет некоторые неудачные побочные эффекты, и эти побочные эффекты заключаются в том, что регулярные переменные стека теперь должны быть динамически индексировать вместо константов, что может повлиять на вашу производительность даже в базовых операциях, доступа к ним, и потребляет пространство для регистрации/стека для хранения динамических смещений. Это означает, что реальная стоимость использования alloca
не записано только в то время, которое требуется для возврата функции. Кроме того, память стека очень ограничена по сравнению с памятью кучи. В Windows ограничение стека по умолчанию составляет 8 МБ, я по умолчанию, тогда как куча может быть почти все адресное пространство пользователя. Более того, в конечном счете, какие данные, которые вы хотите вернуть, должны быть на куче, так что вы можете просто использовать это в качестве рабочего пространства.
Один пункт, который не был сделан AFAI, это то, что стек часто смежный, в то время как куча нет. В целом не правда сказать, что стек так же вероятно, что заканчивается память, как куча.
В C ++ очень часто рассматривать экземпляры объектов как местные жители, что вроде как alloca
Но из структурированной памяти, а не блока n байтов - возможно, вы можете думать об этом как о своем главном вопросе, что является хорошей идеей более широкого использования памяти на основе стека. Я бы раньше сделал это (объявить экземпляр объекта как локальный raii), чем использовать malloc
(или же alloca
) в программе C ++. Всех тех, кто free
Звонки, чтобы сделать исключение-безопасным ...
Обычно это предполагает, что объем объекта ограничен этой функцией и его называемыми функциями. Если это не так, то использование памяти на основе стека, как правило, в любом случае не очень хорошая идея.
Стек Windows не растет - его зарезервированный размер устанавливается во время ссылки, но страницы в пределах этого размера будут совершены только по мере необходимости. Видеть http://msdn.microsoft.com/en-us/library/ms686774%28v=vs.85%29.asp. Анкет Поскольку зарезервированный размер по умолчанию составляет 1 МБ, вы можете легко превзойти это при использовании alloca()
.