_alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 03.08.03 17:22
Оценка:
Всем доброго времени суток.
Вот, натолкнулся на проблему.
Есть некий кусок кода:

class CReceiver
{
public:
    void OnData(void *p)
    {
    }
};

template <class T>
void fff(T member)
{
    void *p = _alloca(65546);
    member.OnData(p);
}


/// main

CReceiver a;
for (int i = 0; i < 20000; i++)
   fff(a);


В дебаг версии все работает, в релизе выдается ошибка переполнения стрека. Вскрытие показало, что добрый компилер VC6sp5 на это формирует примерно следующее:

    mov    esi, 20000                ; 00004e20H
$L91248:

; 210  :     CReceiver a;
; 211  :     for (int i = 0; i < 20000; i++)
; 212  :         fff(a);

    mov    eax, 65548                ; 0001000cH
    call    __alloca_probe
    dec    esi
    jne    SHORT $L91248


Очевидно, что стек не очищается после выполнения тела inline функции fff. Интересно, почему этот гад не восстанавливает esp после вызова fff

Какие варианты кроме использования malloc или отключения оптимизации? Это на самом деле критичный к производительности участок кода, где выделение при помощи malloc может очень плохо сказаться на производительности. Вынесение аллокации памяти и работу с ней в отдельную функцию тоже нежелательно, тем более что там придется извращаться с параметрами темплайта — заводить указатель на функцию или что еще похуже

Спасибо за советы!
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: _alloca и inline функции
От: SWAN Украина  
Дата: 03.08.03 18:05
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Очевидно, что стек не очищается после выполнения тела inline функции fff. Интересно, почему этот гад не восстанавливает esp после вызова fff


AS>Какие варианты кроме использования malloc или отключения оптимизации? Это на самом деле критичный к производительности участок кода, где выделение при помощи malloc может очень плохо сказаться на производительности. Вынесение аллокации памяти и работу с ней в отдельную функцию тоже нежелательно, тем более что там придется извращаться с параметрами темплайта — заводить указатель на функцию или что еще похуже



Очевидно что в Debug fff () был просто напросто не inline

насколько мне известно стек после _alloca чистится после выхода их функции/метода т.е. в твоем случае после подстановки ff () как inline получилось что в цикле стек просто скушался т.е.

CReceiver a;
for (int i = 0; i < 20000; i++)
{
  void *p = _alloca(65546);
  a.OnData(p);
}


сам подумай что из такого выйдет ;-
Re[2]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 03.08.03 18:19
Оценка:
Эээ, конечно, спасибо, но я как то сам понял, что происходит. Вопрос был не _что_ происходит, а _как_ это лучше всего поправить. Почитайте внимательнее
Судя по документации, стек читстится после выхода из функции. Выход из функции (синтаксически) есть? Есть. В документации сказано, что _alloca нельзя использовать или что после него не восстанавливается стек при выходе из inline функции?

_alloca allocates size bytes from the program stack. The allocated space is automatically freed when the calling function exits.


Нет, не сказано. То, что компилер ее экспандит как inline — это его проблемы. А он магическим образом превращает их в мои

SWA>насколько мне известно стек после _alloca чистится после выхода их функции/метода т.е. в твоем случае после подстановки ff () как inline получилось что в цикле стек просто скушался т.е.


SWA>
SWA>CReceiver a;
SWA>for (int i = 0; i < 20000; i++)
SWA>{
SWA>  void *p = _alloca(65546);
SWA>  a.OnData(p);
SWA>}
SWA>


SWA>сам подумай что из такого выйдет ;-
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[3]: _alloca и inline функции
От: SWAN Украина  
Дата: 03.08.03 19:26
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Судя по документации, стек читстится после выхода из функции. Выход из функции (синтаксически) есть? Есть. В документации сказано, что _alloca нельзя использовать или что после него не восстанавливается стек при выходе из inline функции?


В документации сказанно что при выходе именно из функции а не из шаблона тем более что вскрытие показало что тело ff как таковое в результате отсутсвует...

AS>Нет, не сказано. То, что компилер ее экспандит как inline — это его проблемы. А он магическим образом превращает их в мои


Если честно то я не вижу проблем со стороны компилятора — по логике он все сделал правильно или же ему надо заводить список функции при использовании которых не применять inline ?!

Как это поправить — вариантом множество плюсы или минусы которых естественно зависят от конкретной реализации и я не думаю что можно будет при этом оставить именно эту схему разве что каким либо образом отменить inline

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

Можно конечно попробовать дать более конкретные советы но я не думаю что по данному коду можно дать действительно оптимальный совет тем более что он критичен по скорости да и давать банальные советы человеку с таким Experience на RSDN наверно будет смешно

P.S.Sorry за ошибочную интерпретацию вопроса но фраза "Очевидно, что стек не очищается после выполнения тела inline функции fff" заставила подумать меня именно так как я подумал
Re[4]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 03.08.03 20:03
Оценка:
Ну, я могу написать инлайн функцию, а не темплайт функцию с ровно таким же глюком. К тому же компилер все равно при инстанцировании генерит экземпляр функции. И повторюсь — синтаксически функция и возврат из нее есть, так что правила документации честно соблюдены.

SWA>Если честно то я не вижу проблем со стороны компилятора — по логике он все сделал правильно или же ему надо заводить список функции при использовании которых не применять inline ?!


Всего то что надо — увеличить esp на размер, который забран вызовами _alloca после выполнения кода функции. Проблем для компилера тут особо наверное нет.

SWA>Как это поправить — вариантом множество плюсы или минусы которых естественно зависят от конкретной реализации и я не думаю что можно будет при этом оставить именно эту схему разве что каким либо образом отменить inline


SWA>Если участок критичный по скорости то я не вижу причин не вынести его в отдельную специальным образом оптимизированную функцию.


Но как? Это темплайт. Значит, придется по меньшей мере делать не инлайн функию, заводить указатель на нужную функцию объекта, передавать в не инлайн функцию указатель на экземпляр объекта и эту функцию, а так же параметры. В общем, некрасиво, да и у того, кто потом будет смотреть такой код, появятся соменения во вменяемости автора. Но опять — компилер может и не инлайн функцию подставить inline при оптимизации. Чего он только не может, чтобы нагадить

SWA>Можно конечно попробовать дать более конкретные советы но я не думаю что по данному коду можно дать действительно оптимальный совет тем более что он критичен по скорости да и давать банальные советы человеку с таким Experience на RSDN наверно будет смешно


Ну почему смешно. Иногда истина очень близко — так, что можно просто ее не увидеть.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: _alloca и inline функции
От: Alexey Shirshov Россия http://wise-orm.com
Дата: 04.08.03 04:43
Оценка: 10 (1)
Hello, Andrew!
You wrote on Sun, 03 Aug 2003 17:22:30 GMT:

[]

AS> Какие варианты кроме использования malloc или отключения оптимизации?

AS> Это на самом деле критичный к производительности участок кода, где
AS> выделение при помощи malloc может очень плохо сказаться на
AS> производительности. Вынесение аллокации памяти и работу с ней в
AS> отдельную функцию тоже нежелательно, тем более что там придется
AS> извращаться с параметрами темплайта — заводить указатель на функцию или
AS> что еще похуже

Попробуй скомпилять с #pragma optimize( "", off ).
Еще можно с элипсисами извернуться (для меня всегда работало).
В MSDN написано:

You cannot force the compiler to inline a function when conditions other
than cost/benefit analysis prevent it. You cannot inline a function if:
— ...
— The function has a variable argument list.
— ...


With best regards, Alex Shirshov.
Posted via RSDN NNTP Server 1.7 beta
Re: _alloca и inline функции
От: alexandrov_alex США  
Дата: 04.08.03 06:28
Оценка: 1 (1)
В добавление к вышесказанному:
Если в alloca у тебя стоит константа, почему бы тогда не объявить это дело как локальную переменную? Весь смысл alloca, наверное, все-таки в динамическом выделении на стеке НЕИЗВЕСТНОГО количества памяти...
Posted via RSDN NNTP Server 1.7 beta
It's kind of fun to do the impossible (Walt Disney)
Re: _alloca и inline функции
От: kam Россия  
Дата: 04.08.03 06:36
Оценка:
Здравствуйте, Andrew S, Вы писали:


AS>template <class T>
AS>void fff(T member)
AS>{
AS>    void *p = _alloca(65546);
AS>    member.OnData(p);
AS>}



AS>Какие варианты...



А просто сделать вместо
void *p = _alloca(65546);

char p[65546];


Не то-же самое получиться?

--
BR, Kam
Re[2]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.08.03 06:38
Оценка:
Ну, я ж не буду приводить _реальный_ участок кода, верно? Там на самом деле все более сложно — а тут просто жмых, чтобы проще разобраться в сути проблемы.

_>В добавление к вышесказанному:

_> Если в alloca у тебя стоит константа, почему бы тогда не объявить это дело как локальную переменную? Весь смысл alloca, наверное, все-таки в динамическом выделении на стеке НЕИЗВЕСТНОГО количества памяти...
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[2]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.08.03 06:38
Оценка:
Не то же. Приведенный пример именно пример, а не реальный участок кода.

kam>Не то-же самое получиться?
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[2]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.08.03 06:41
Оценка:
Ага, это я уже пробовал. Прямо ASProtect вспоминается. А еще я пробовал __try и __except — тоже помогает. А вот с переменным количеством аргументов как то не догадался. Интересно, попробую. Спасибо!

И все таки интересно узнать мнение — это таки можно считать багом документации или багом компилера?

AS>Попробуй скомпилять с #pragma optimize( "", off ).

AS>Еще можно с элипсисами извернуться (для меня всегда работало).
AS>В MSDN написано:
AS>

You cannot force the compiler to inline a function when conditions other
AS>than cost/benefit analysis prevent it. You cannot inline a function if:
AS> — ...
AS> — The function has a variable argument list.
AS> — ...


AS>With best regards, Alex Shirshov.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: _alloca и inline функции
От: Alex Fedotov США  
Дата: 04.08.03 06:44
Оценка: 10 (1)
Здравствуйте, Andrew S, Вы писали:

AS>Какие варианты кроме использования malloc или отключения оптимизации?


1. Сменить компилятор. Хотя не уверен, что поможет.

2. Использовать фиксированный буфер достаточно большого размера и вызывать malloc только тогда, когда требуемый размер больше размера фиксированного буфера (что должно быть достаточно редко).
-- Alex Fedotov
Re[3]: _alloca и inline функции
От: SWAN Украина  
Дата: 04.08.03 06:55
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>И все таки интересно узнать мнение — это таки можно считать багом документации или багом компилера?


Думаю ни то ни другое
по первому — скушать стек размер которого по default 1Mb (если не ошибаюсь) довольно таки сложно — это надо постараться посему считаю данную ситуацию частным случаем но упомянуть конечно о нем не помешало бы и в доке — возможно такой KB таки появится

по второму — функция _alloca с точки зрения компилятора наверняка такая же как и любая другая CRT функция а посему невижу причин ему именно дле нее делать какие то дополнительные телодвижения (типа делать возврат ESP) тем более что она основанная на других принципах — чистка стека при выходе
Re: _alloca и inline функции
От: MaximE Великобритания  
Дата: 04.08.03 07:23
Оценка: 5 (1)
Здравствуйте, Andrew S, Вы писали:

AS>Какие варианты кроме использования malloc или отключения оптимизации? Это на самом деле критичный к производительности участок кода, где выделение при помощи malloc может очень плохо сказаться на производительности. Вынесение аллокации памяти и работу с ней в отдельную функцию тоже нежелательно, тем более что там придется извращаться с параметрами темплайта — заводить указатель на функцию или что еще похуже


Выправляй стэк сам:

int main()
{
    void* stack;
    __asm mov [stack],esp;
    void* p = _alloca(0x100);

    // ...

    __asm mov esp,[stack];

    return 0;
}
Re[2]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.08.03 07:33
Оценка:
Да, я тоже примерно к этому и пришел. Просто написать темплайт.
Например, вида:


template <class T = void *, size_t fixed_size = 16384>
class CAlloc
{
    T   *m_pBuffer;
    char m_szBuffer[fixed_size];
public:
    CAlloc(size_t size): m_pBuffer((size <= fixed_size) ? (T*)m_szBuffer : (T*)malloc(size))
    {
    }
    ~CAlloc()
    {
        if ((m_pBuffer) && (m_pBuffer != (T *)m_szBuffer))
           free(m_pBuffer);
    }
    operator T*() const
    {
        return m_pBuffer;
    }
};



AS>>Какие варианты кроме использования malloc или отключения оптимизации?


AF>1. Сменить компилятор. Хотя не уверен, что поможет.


AF>2. Использовать фиксированный буфер достаточно большого размера и вызывать malloc только тогда, когда требуемый размер больше размера фиксированного буфера (что должно быть достаточно редко).
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[2]: _alloca и inline функции
От: Alexey Shirshov Россия http://wise-orm.com
Дата: 04.08.03 07:34
Оценка:
Hello, Alex!
You wrote on Mon, 04 Aug 2003 06:44:24 GMT:

AS>> Какие варианты кроме использования malloc или отключения оптимизации?


AF> 1. Сменить компилятор. Хотя не уверен, что поможет.



Именно это и делает CTempBuffer из седьмой ATL.

With best regards, Alex Shirshov.
Posted via RSDN NNTP Server 1.7 beta
Re[2]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.08.03 07:37
Оценка:
Спасибо за совет!
Тогда придется заводить отдельно дебужные и релизные версии функций
Тоже весело. Опять же — непортабельно на другой компилер — а ну ка тот сам вздумает справится с такой ситуацией.
Меня в последнее время как то не тянет на эксперименты с таким кодом в коммерческих продуктах

ME>Выправляй стэк сам:


ME>
ME>int main()
ME>{
ME>    void* stack;
ME>    __asm mov [stack],esp;
ME>    void* p = _alloca(0x100);

ME>    // ...

ME>    __asm mov esp,[stack];

ME>    return 0;
ME>}
ME>
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[3]: _alloca и inline функции
От: Alex Fedotov США  
Дата: 04.08.03 07:37
Оценка: :)
Здравствуйте, Alexey Shirshov, Вы писали:

AS>>> Какие варианты кроме использования malloc или отключения оптимизации?


AF>> 1. Сменить компилятор. Хотя не уверен, что поможет.


AS>Именно это и делает CTempBuffer из седьмой ATL.


Меняет компилятор

(Sorry, could not resist)
-- Alex Fedotov
Re[3]: _alloca и inline функции
От: MaximE Великобритания  
Дата: 04.08.03 07:52
Оценка: 13 (2)
Здравствуйте, Andrew S, Вы писали:

AS>Спасибо за совет!

AS>Тогда придется заводить отдельно дебужные и релизные версии функций

Незачем заводить разные версии.

Вот что делает пролог функции:
push ebp  
mov ebp,esp


Вот эпилог:
mov esp,ebp 
pop ebp  
ret


Т.е. трюк основан на том, что мы вручную восстанавливаем esp в нужном нам месте, а не в эпилоге функции.
Re[4]: _alloca и inline функции
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.08.03 07:55
Оценка:
Она основана на том, что компилер использует ebp для кадра стека локальных переменных. В соответствии с этим, он легко восстанавливает значение esp при выходе. А мог бы, кстати, использовать только esp, как делают многие компилеры — тогда ebp освободился бы для общих нужд.

А вообще — хорошо, если была бы функция, выделяющая стековую память только в пределах операторных скобок:

void f()
{
   void *p;
   DWORD dwSize = xxxxx;
   for (int i = 0; i < 20000; i++)
   {
     //  выделяем память
       p = __alloca(dwSize)
     //  используем память
     
   } //  память освободилась
}



Это позволило бы писать циклы с динамическим выделением стековой памяти. Все, что надо компилеру — просто восстанавливать esp.

SWA>по второму — функция _alloca с точки зрения компилятора наверняка такая же как и любая другая CRT функция а посему невижу причин ему именно дле нее делать какие то дополнительные телодвижения (типа делать возврат ESP) тем более что она основанная на других принципах — чистка стека при выходе
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.