Как узнать в рантайме была ли заинлайнена функция?
От: denisko http://sdeniskos.blogspot.com/
Дата: 24.07.14 12:55
Оценка: :)
Есть у нас в проекте ручной разворачиватель классов через лямбды, используется он в туевой куче месте в важных с точки зрения производительности местах. Это хозяйство (проект) собирается под разные платформы, соответственно смотреть километровые простыни -Winline под гсс нет никакого желания. Сейчас есть режим работы приложения, когда оно прогоняет на тестовых данных все сомнительные функции и если "оптимизированная версия" медленнее неоптимизированной, то верещит. Прогонять тоже занимает какое то время, поэтому хочется придумать волбешную пилюлю, чтобы съел ее и сразу в момент вызова функции (внешне разворачивалка выглядит как цепочка рекурсивных функций) мог узнать заинлайнилась она или нет? Есть идеи?
<Подпись удалена модератором>
Re: Как узнать в рантайме была ли заинлайнена функция?
От: saf_e  
Дата: 24.07.14 15:35
Оценка: 4 (1) -1
Здравствуйте, denisko, Вы писали:

D>Есть у нас в проекте ручной разворачиватель классов через лямбды, используется он в туевой куче месте в важных с точки зрения производительности местах. Это хозяйство (проект) собирается под разные платформы, соответственно смотреть километровые простыни -Winline под гсс нет никакого желания. Сейчас есть режим работы приложения, когда оно прогоняет на тестовых данных все сомнительные функции и если "оптимизированная версия" медленнее неоптимизированной, то верещит. Прогонять тоже занимает какое то время, поэтому хочется придумать волбешную пилюлю, чтобы съел ее и сразу в момент вызова функции (внешне разворачивалка выглядит как цепочка рекурсивных функций) мог узнать заинлайнилась она или нет? Есть идеи?


В принципе, инлайн от неинлайна, (не глядя в ассемблер) отличается наличием собственного фрейма.

Теоретически можно попробовать что-то вроде:

bool is_inline_f(char a, const char *a_addr)
{
  return &a == a_addr; // т.е. если мы инлайн -> адрес переменной должен быть такой же как и у вызываемой ф-ции 
}

void test_f()
{
  char test;
  is_inline_f(test, &test);
}


Сам не тестил, лень
Re[2]: Как узнать в рантайме была ли заинлайнена функция?
От: Кодт Россия  
Дата: 24.07.14 15:53
Оценка: 4 (1) +1
Здравствуйте, saf_e, Вы писали:

__>Теоретически можно попробовать что-то вроде:


Как только ты берёшь адрес у переменной, ты приземляешь её в стек.
По стандарту, адреса разных переменных (у вызывающей функции и внутри вызываемой), чьи времена жизни перекрываются, должны различаться. Даже если эти переменные нигде больше не используются.
(Мне так кааэтся).
Перекуём баги на фичи!
Re: Как узнать в рантайме была ли заинлайнена функция?
От: Alexander G Украина  
Дата: 24.07.14 16:33
Оценка: 4 (1)
Здравствуйте, denisko, Вы писали:

D>Есть у нас в проекте ручной разворачиватель классов через лямбды, используется он в туевой куче месте в важных с точки зрения производительности местах. Это хозяйство (проект) собирается под разные платформы, соответственно смотреть километровые простыни -Winline под гсс нет никакого желания. Сейчас есть режим работы приложения, когда оно прогоняет на тестовых данных все сомнительные функции и если "оптимизированная версия" медленнее неоптимизированной, то верещит. Прогонять тоже занимает какое то время, поэтому хочется придумать волбешную пилюлю, чтобы съел ее и сразу в момент вызова функции (внешне разворачивалка выглядит как цепочка рекурсивных функций) мог узнать заинлайнилась она или нет? Есть идеи?


Если собирать с отладочными симовлами, то среди символов будут не-заинлайненые функции, а заинлайненых быть не должно.

Можно проверять значение адреса возврата на стеке или значение EBP для стек фрейма, и сравнивать её внутри и снаружи.

В студии есть меджик _ReturnAddress, про который явно сказано

Optimizations such as inlining may affect the return address. For example, if the sample program below is compiled with /Ob1, inline_func will be inlined into the calling function, main. Therefore, the calls to _ReturnAddress from inline_func and main will each produce the same value.


Ну и на других компиляторах можно такое сделать через asm вставки, если интринсика нет.

Мне кажется, лучше, если заработает через символы, т.к. это неинтрузивный путь; asm-вставки могут аффектить инлайнинг.
Русский военный корабль идёт ко дну!
Re[2]: Как узнать в рантайме была ли заинлайнена функция?
От: c-smile Канада http://terrainformatica.com
Дата: 24.07.14 16:40
Оценка: 1 (1) +5
Здравствуйте, saf_e, Вы писали:

_>Сам не тестил, лень


Как в квантовой механике — сам факт наблюдения меняет результат.
В твоем случае твой код как раз и приводит к тому что функция может перестать быть inline.
Мой gut feeling (в русской транскрипции "ж***й чувствую") говорит что только анализом логов компайлера или чего-то там это можно узнать надежно...
Re: Как узнать в рантайме была ли заинлайнена функция?
От: Кодт Россия  
Дата: 24.07.14 16:51
Оценка: 4 (1)
Здравствуйте, denisko, Вы писали:

D>Есть у нас в проекте ручной разворачиватель классов через лямбды, используется он в туевой куче месте в важных с точки зрения производительности местах. Это хозяйство (проект) собирается под разные платформы, соответственно смотреть километровые простыни -Winline под гсс нет никакого желания. Сейчас есть режим работы приложения, когда оно прогоняет на тестовых данных все сомнительные функции и если "оптимизированная версия" медленнее неоптимизированной, то верещит. Прогонять тоже занимает какое то время, поэтому хочется придумать волбешную пилюлю, чтобы съел ее и сразу в момент вызова функции (внешне разворачивалка выглядит как цепочка рекурсивных функций) мог узнать заинлайнилась она или нет? Есть идеи?


Под конкретную платформу можно что-то такое замутить:
intptr_t __declspec(naked) framepointer() // переведите на синтаксис gcc сами по вкусу
{
  __asm mov eax, ebp; // скопировали указатель кадра
}

inline void foo(bla, bla, bla, intptr_t& fp) // добавили параметр для проверки
{
  fp = framepointer();
}

void bar()
{
  intptr_t my_fp = framepointer(), their_fp;
  .....
  foo(bla,bla,bla, their_fp);
  if(my_fp == their_fp) puts("inline"); else puts("call");
  .....
}

Правда, как показывает опыт, оптимизатор первым делом избавляется от ebp-кадров. А фотографировать esp — почти бессмысленно.
Перекуём баги на фичи!
Re: Как узнать в рантайме была ли заинлайнена функция?
От: Erop Россия  
Дата: 24.07.14 19:43
Оценка:
Здравствуйте, denisko, Вы писали:

D>Есть у нас в проекте ручной разворачиватель классов через лямбды, используется он в туевой куче месте в важных с точки зрения производительности местах. Это хозяйство (проект) собирается под разные платформы, соответственно смотреть километровые простыни -Winline под гсс нет никакого желания. Сейчас есть режим работы приложения, когда оно прогоняет на тестовых данных все сомнительные функции и если "оптимизированная версия" медленнее неоптимизированной, то верещит. Прогонять тоже занимает какое то время, поэтому хочется придумать волбешную пилюлю, чтобы съел ее и сразу в момент вызова функции (внешне разворачивалка выглядит как цепочка рекурсивных функций) мог узнать заинлайнилась она или нет? Есть идеи?


Во-первых, может быть не только подстановка, но и иное использование в месте вызова знания об определениии функции.
Во-вторых, в одних местах может подставится, а в других -- нет.

Так что вообще не совсем понятно, что конкретно ты хочешь.
IMHO, если ты просто хочешь почитать список варнингов о том, что что-то где-то не подставилось, то проще всего разобрать скриптом лог компиляции...
Только смысл этого всё равно не совсем понятен...

А что такое "ручной разворачиватель классов через лямбды"? Это какой-то кодогенератор? Может его переделать так, что бы сомнений в том, что результат эффективный, не возникало?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Как узнать в рантайме была ли заинлайнена функция?
От: denisko http://sdeniskos.blogspot.com/
Дата: 25.07.14 03:57
Оценка:
Здравствуйте, Erop, Вы писали:

E>Во-первых, может быть не только подстановка, но и иное использование в месте вызова знания об определениии функции.

E>Во-вторых, в одних местах может подставится, а в других -- нет.

E>Так что вообще не совсем понятно, что конкретно ты хочешь.

E>IMHO, если ты просто хочешь почитать список варнингов о том, что что-то где-то не подставилось, то проще всего разобрать скриптом лог компиляции...
E>Только смысл этого всё равно не совсем понятен...
Егор не мешай двигаться от русского форума к оплоту демократии.


E>А что такое "ручной разворачиватель классов через лямбды"? Это какой-то кодогенератор? Может его переделать так, что бы сомнений в том, что результат эффективный, не возникало?..

Типа такого http://rsdn.ru/forum/alg/5579882.1
Автор:
Дата: 29.04.14
(отсюда, понятно, что вопрос о подстановке где то еще лишен смысла?) с достаточным количеством блекджека и необходимым количеством шлюх, чтобы можно было использовать нескольким людям в проекте. Можно переделать в этом проблемы нет, но тогда отлаживать будет неудобно -- бряку в макрос не забьешь. А так а) удобно б) быстро, если все развернулось правильно с) компактно и читабельно.
<Подпись удалена модератором>
Re[3]: Как узнать в рантайме была ли заинлайнена функция?
От: saf_e  
Дата: 25.07.14 08:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, saf_e, Вы писали:


__>>Теоретически можно попробовать что-то вроде:


К>Как только ты берёшь адрес у переменной, ты приземляешь её в стек.

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

Вот этот тест в VS2013 работает. В дебаге — 0, в релизе — 1. Возможно это только особенность компилятора, которую потом уберут...

inline bool test(char a, char *addr_a)
{
    return addr_a == &a;
}

int _tmain(int argc, _TCHAR* argv[])
{
    char test_v;
    bool v = test(test_v, &test_v);

    std::cout << v << std::endl;
}
Re[3]: Как узнать в рантайме была ли заинлайнена функция?
От: Erop Россия  
Дата: 25.07.14 08:26
Оценка:
Здравствуйте, denisko, Вы писали:

D>Егор не мешай двигаться от русского форума к оплоту демократии.


Если будет понятно, в чём твой вопрос, будет проще дать тебе хороший совет...

E>>А что такое "ручной разворачиватель классов через лямбды"? Это какой-то кодогенератор? Может его переделать так, что бы сомнений в том, что результат эффективный, не возникало?..

D>Типа такого http://rsdn.ru/forum/alg/5579882.1
Автор:
Дата: 29.04.14
(отсюда, понятно, что вопрос о подстановке где то еще лишен смысла?) с достаточным количеством блекджека и необходимым количеством шлюх, чтобы можно было использовать нескольким людям в проекте. Можно переделать в этом проблемы нет, но тогда отлаживать будет неудобно -- бряку в макрос не забьешь. А так а) удобно б) быстро, если все развернулось правильно с) компактно и читабельно.


Речь идёт о "разворачивателе циклов", то есть?
А как ты борешься с тем, что простую функцию в цикле, ну, там удвоение, например, оптимизатор может сделать через SSE, а твой разворачиватель вряд ли? Или твоему компилятору это не мешает?

В любом случае, как это выглядит? Ну, типа пишешь вызов вроде
call_for_range<5, 184>( тут лямбда );
или как-то ещё?

Например, один из путей, завернуть всё в макрос, в котором определять CRTP-наследника класса-механизма этой раворотки, в котором определять тело цикла, которое помечать как force_inline и парсить лог компиялции на тему того, что такой метод не подставился, подойдёт?

То есть будет то-то вроде
STATIC_RANGE_FOR_BEGIN( 5, 184, тут список захватываемых переменных ) {
    тут тело цикла;
}STATIC_RANGE_FOR_END;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Как узнать в рантайме была ли заинлайнена функция?
От: rm822 Россия  
Дата: 25.07.14 09:23
Оценка: 5 (1)
А надо именно узнать или можно тупо зафорсить инлайны?
на SO говорят вот такая штука работает

#define ALWAYS_INLINE __attribute__((always_inline))
[](volatile int &i)ALWAYS_INLINE{i++;}
Re: Как узнать в рантайме была ли заинлайнена функция?
От: rm822 Россия  
Дата: 25.07.14 13:30
Оценка:
Забавная задачка.
Налабал вот такое на MSVC 2013.



#include "stdafx.h"

uintptr_t __declspec(naked) getEIP()
{
    __asm {
        pop   eax
        push  eax
        ret
    }
}

void  __declspec(noinline) CheckInlining(uintptr_t funcAddress, uintptr_t callerAddress)
{
    if (callerAddress < callerAddress || callerAddress > funcAddress + 32) //считаем если мы не заинлайнились, то callerAddress будет где-то в диапазоне [&foo, &foo + 32]
        printf("inlined\n");
    else
        printf("not inlined\n");
}

void __forceinline foo()
{
    CheckInlining((uintptr_t)&foo, getEIP());
    printf(__FUNCTION__"\n");
}

void __declspec(noinline) foo2()
{
    CheckInlining((uintptr_t)&foo2, getEIP());
    printf(__FUNCTION__"\n");
}


template <typename T>
uintptr_t GetLambdaAddr(T*)
{
    auto addr = &T::operator();
    //вообще сие хачок, но мы то знаем что указатель на мембер лямбды будет тупо адресом
    return *(uintptr_t*)(&addr);
}

auto lam = []()  {
    CheckInlining(GetLambdaAddr(&lam), getEIP());
    printf("in lambda\n"); 
};

auto lam2 = []() {
    _asm nop; // inline assembly force not inlining this function
    CheckInlining(GetLambdaAddr(&lam2), getEIP());
    printf("in lambda2\n");
};


int _tmain(int , _TCHAR* [])
{
    foo(); printf("\n");
    foo2(); printf("\n");
    lam(); printf("\n");
    lam2(); printf("\n");

    return 0;
}


Результат



inlined
foo

not inlined
foo2

inlined
in lambda

not inlined
in lambda2

Press any key to continue . . .
Re: Как узнать в рантайме была ли заинлайнена функция?
От: antropolog  
Дата: 25.07.14 22:10
Оценка: +2
Здравствуйте, denisko, Вы писали:

D>Есть идеи?


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