Комментарии 15
А еще можно упомянуть коварную функцию alloca, которую хоть и не часто используют явно, но неявно она живее всех живых:
for (size_t i = 0; i < n; ++i)
if (wcscmp(strings[i], A2W(pszSrc[i])) == 0)
{
...
}
+2
мы делаем так (стек в MySQL традиционно маленький):
во-первых, в gcc можно компилировать с -Wframe-larger-than. Честно говоря в Makefile-ы я этого еще не прописал, все собираюсь. Но вручную иногда проверяю. Где-то в 8-16K всегда укладываемся.
а главное — если надо выделить больше, чем несколько килобайт, то используем alloca(), и (!) проверяем, на переполнение стека. Для этого при создании треда адрес его стека запоминается в thread-local переменной.
во-первых, в gcc можно компилировать с -Wframe-larger-than. Честно говоря в Makefile-ы я этого еще не прописал, все собираюсь. Но вручную иногда проверяю. Где-то в 8-16K всегда укладываемся.
а главное — если надо выделить больше, чем несколько килобайт, то используем alloca(), и (!) проверяем, на переполнение стека. Для этого при создании треда адрес его стека запоминается в thread-local переменной.
0
Все верно, но есть два момента.
Во-первых, нужно еще обстоятельно проверить, что с -Wframe-larger-than gcc вычисляет расход стека верно. Например, в Visual C++ есть /analyze:stacksize, который в общем работает, но во многих случаях вычисляет расход неверно. Может быть, в gcc это отлажено лучше и аналогичных проблем нет.
Во-вторых, не всегда же большой расход от того, что объявили массив большого размера на стеке явно. Мог быть большой массив в качестве поля класса, а временный объект этого класса мог быть создан автоматически при вызове функции.
В общем, действительно нужно по возможности пользоваться статическим анализом компилятора и проверять на тестовых пакетах, хватает ли стека уменьшенного размера.
Во-первых, нужно еще обстоятельно проверить, что с -Wframe-larger-than gcc вычисляет расход стека верно. Например, в Visual C++ есть /analyze:stacksize, который в общем работает, но во многих случаях вычисляет расход неверно. Может быть, в gcc это отлажено лучше и аналогичных проблем нет.
Во-вторых, не всегда же большой расход от того, что объявили массив большого размера на стеке явно. Мог быть большой массив в качестве поля класса, а временный объект этого класса мог быть создан автоматически при вызове функции.
В общем, действительно нужно по возможности пользоваться статическим анализом компилятора и проверять на тестовых пакетах, хватает ли стека уменьшенного размера.
+3
Очень интересное наблюдение, я думал, что вопрос оптимизации использования памяти в непересекающихся ветвях хорошо отлажен, ан нет.
А не получится ли аналогичная ситуация здесь?
[code]
inline
void f()
{
char buff[1000*1000];
…
}
void ff()
{
if( rand() )
f();
else
f();
}
[/code]
Конечно, если изменить код так, чтоб компилятор его не прооптимизировал. Но идею именно в inline. Конечно, компилятор не должен хотеть подставить функцию, которая отожрет много памяти на стеке, но он может не правильно подсчитать эту память (или еще что-нибудь).
Еще вы забыли упоменять о таких замечательных вещах (которыми меня учили у вас), как перегрузка new, что может позволить выделить большой кусок в куче, а потом уже его использовать «как стек» и удалить в конце работы целиком.
А не получится ли аналогичная ситуация здесь?
[code]
inline
void f()
{
char buff[1000*1000];
…
}
void ff()
{
if( rand() )
f();
else
f();
}
[/code]
Конечно, если изменить код так, чтоб компилятор его не прооптимизировал. Но идею именно в inline. Конечно, компилятор не должен хотеть подставить функцию, которая отожрет много памяти на стеке, но он может не правильно подсчитать эту память (или еще что-нибудь).
Еще вы забыли упоменять о таких замечательных вещах (которыми меня учили у вас), как перегрузка new, что может позволить выделить большой кусок в куче, а потом уже его использовать «как стек» и удалить в конце работы целиком.
0
я думал, что вопрос оптимизации использования памяти в непересекающихся ветвях хорошо отлажен, ан нет.
В том и дело, что это считается оптимизацией в смысле «сделаем как-нибудь, а потом оптимизируем», в то время как стек является дефицитным ресурсом.
А не получится ли аналогичная ситуация здесь?
Будет зависеть от компилятора, это надо проверять. На Visual C++ 10 так сразу не сломалось.
как перегрузка new, что может позволить выделить большой кусок в куче, а потом уже его использовать «как стек» и удалить в конце работы целиком.
Расскажите, пожалуйста, как заставить компилятор использовать выделенный в куче блок памяти для локальных и временных переменных?
0
Заставить не как, все равно придется явно или не явно использовать new. Но его можно перегрузить и сделать куда более легким, чем по умолчанию.
0
Да, это тоже путь. Например, можно использовать блочный распределитель памяти.
Правда есть одно «но». Куча в Visual C++ runtime потокобезопасная (и это одна из причин, почему она такая относительно небыстрая), в случае своего распределителя памяти о потокобезопасности придется хотя бы просто подумать.
Правда есть одно «но». Куча в Visual C++ runtime потокобезопасная (и это одна из причин, почему она такая относительно небыстрая), в случае своего распределителя памяти о потокобезопасности придется хотя бы просто подумать.
+1
ну автоматические переменные «не потокобезопасные» (если вообще этот термин к ним применять разумно). Так что если блочный распределитель установлен только для текущего потока, то это будет безопасно. Главное, не забыть включить обычные, если надо выделять «нормальные» объекты, а не временные.
0
Почему автоматические переменные не потокобезопасные? У каждого потока свой стек.
+1
Ну если к ним полезет другой поток, то ничего хорошего не будет.
способ аллокации я предлагаю менять только для одного потока.
способ аллокации я предлагаю менять только для одного потока.
0
если к ним полезет другой поток, то ничего хорошего не будет.
Верно, но выделение памяти под автоматическую переменную все равно потокобезопасно — оно происходит до того, как начинается время жизни переменной. Если другой поток пытается обратиться к переменной раньше, вы уже ходите по очень тонкому льду и это не связано с потокобезопасностью.
способ аллокации я предлагаю менять только для одного потока.
Как только вы пытаетесь сделать то же самое в еще одном потоке (а это вполне нормальное стремление), потокобезопасность становится актуальной.
В любом случае смысл затеи с выделенным распределителем в том, что вы экономите на освобождении памяти и фрагментации памяти. Для того, чтобы иметь возможность разрушить распределитель целиком, вам нужна уверенность, что к моменту разрушения распределителя нигде вне его не осталось указателей внутрь этого распределителя. Это не так просто, как может показаться на первый взгляд. В общем, идея работоспособная, но отнюдь не пуленепробиваемая и требующая большой дисциплины.
0
Я имел в виду что способ аллокации меняется только для текущего потока, блочный аллокатор выделяется только для текущего потока и не доступен другим потокам. Тогда получается та же самая ситуация: если только один поток пользуется, то все хорошо, а если другой полез пользоваться переменными этого потока, то уже надо обеспечить синхронизацию, так же, как и в случае автоматических переменных.
0
Очень интересное наблюдение, я думал, что вопрос оптимизации использования памяти в непересекающихся ветвях хорошо отлажен, ан нет.
А не получится ли аналогичная ситуация здесь?
Конечно, если изменить код так, чтоб компилятор его не прооптимизировал. Но идею именно в inline. Конечно, компилятор не должен хотеть подставить функцию, которая отожрет много памяти на стеке, но он может не правильно подсчитать эту память (или еще что-нибудь).
Еще вы забыли упоменять о таких замечательных вещах (которыми меня учили у вас), как перегрузка new, что может позволить выделить большой кусок в куче, а потом уже его использовать «как стек» и удалить в конце работы целиком.
А не получится ли аналогичная ситуация здесь?
inline
void f()
{
char buff[1000*1000];
…
}
void ff()
{
if( rand() )
f();
else
f();
}
Конечно, если изменить код так, чтоб компилятор его не прооптимизировал. Но идею именно в inline. Конечно, компилятор не должен хотеть подставить функцию, которая отожрет много памяти на стеке, но он может не правильно подсчитать эту память (или еще что-нибудь).
Еще вы забыли упоменять о таких замечательных вещах (которыми меня учили у вас), как перегрузка new, что может позволить выделить большой кусок в куче, а потом уже его использовать «как стек» и удалить в конце работы целиком.
0
>Visual C++ 10 справляется со вторым примером, но зато не справляется в этом случае
область видимости у обоих временных обьектов одна.
область видимости у обоих временных обьектов одна.
+1
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Стековые переменные — быстрые и иногда мертвые