Зачем программе на C ++ выделять больше памяти для локальных переменных, чем в худшем случае? - PullRequest
9 голосов
/ 17 августа 2011

Вдохновлен этим вопросом .

Видимо в следующем коде:

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    if( GetTickCount() > 1 ) {
        char buffer[500 * 1024];
        SecureZeroMemory( buffer, sizeof( buffer ) );
    } else {
        char buffer[700 * 1024];
        SecureZeroMemory( buffer, sizeof( buffer ) );
    }
    return 0;
}

скомпилировано с размером стека по умолчанию (1 мегабайт) с помощью Visual C ++ 10 с оптимизациейв (/ O2) переполнение стека происходит из-за того, что программа пытается выделить 1200 килобайт в стеке.

Приведенный выше код, конечно, немного преувеличен, чтобы показать проблему - использует много стека довольно тупым способом.Тем не менее, в реальных сценариях размер стека может быть меньше (например, 256 килобайт), и может быть больше веток с более мелкими объектами, которые приводят к общему размеру выделения, достаточному для переполнения стека.

Это не имеет смысла.В худшем случае это будет 700 килобайт - это будет кодовая дорожка, которая строит набор локальных переменных с наибольшим общим размером.Обнаружение этого пути во время компиляции не должно быть проблемой.

Таким образом, компилятор создает программу, которая пытается выделить даже больше памяти, чем в худшем случае.Согласно этот ответ LLVM делает то же самое.

Это может быть недостатком в компиляторе или может быть какая-то реальная причина сделать это таким образом.Я имею в виду, может быть, я просто не понимаю что-то в дизайне компиляторов, которое объясняет, почему такое размещение необходимо.

Почему компилятор хотел бы, чтобы программа выделяла больше памяти, чем требуется коду в худшем случае?

Ответы [ 4 ]

2 голосов
/ 17 августа 2011

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

Кстати, в Windows стек резервируется полностью, когда поток начинает выполнение, но фиксируется по мере необходимости, поэтому вына самом деле не тратят много «реальной» памяти, даже если вы зарезервировали большой стек.

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

2 голосов
/ 17 августа 2011

Это может быть связано с использованием SecureZeroMemory. Попробуйте заменить его на обычную ZeroMemory и посмотрите, что произойдет - страница MSDN, по сути, указывает на то, что у SZM есть некоторая дополнительная семантика помимо того, что подразумевает его подпись, и они могут быть причиной ошибки.

1 голос
/ 17 августа 2011

Следующий код при компиляции с использованием GCC 4.5.1 для ideone размещает два массива по одному адресу:

#include <iostream>

int main()
{
  int x;
  std::cin >> x;

  if (x % 2 == 0)
  {
    char buffer[500 * 1024]; 
    std::cout << static_cast<void*>(buffer) << std::endl;
  }

  if (x % 3 == 0)
  {
    char buffer[700 * 1024]; 
    std::cout << static_cast<void*>(buffer) << std::endl;
  }
}

input: 6

output:
0xbf8e9b1c
0xbf8e9b1c

Ответ, вероятно, "использовать другой компилятор", если вы хотите эту оптимизацию.

0 голосов
/ 17 августа 2011

OS Pageing и байтовое выравнивание могут быть фактором.Также домашнее хозяйство может использовать дополнительный стек вместе с пространством, необходимым для вызова других функций в этой функции.

...