Pull to refresh
0
0
Send message

Это сомнительное утверждение. На этапе компиляции нет никаких гарантий, что используется именно стандартная библиотека, поэтому malloc как функция, задекларированная средствами языка, не несет никакой информации о семантике. То, что компилятор делает некоторые (пусть даже верные в 99 процентах случаев) предположения о семантике с целью последующей оптимизации, — это допустимо, но должно быть четко отражено в документации на компилятор, а по-хорошему должно управляться опциями (кстати, интересно, будет ли, скажем, gcc это оптимизировать с -nostdlib?), и должна быть возможность получать предупреждения о подобных оптимизациях.
Это пример сильно отличается от приведенного примера с int_size(), где причиной оптимизации является неопределенное поведение, четко описанное в семантике языка

Хм… А чем формально вышеописанный код отличается от

file1.cpp
X* malloc_x() {
    return (X*)malloc(sizeof(struct X));
}


file2.cpp:
extern X* malloc_x();

X *make_x() {
  X *p = malloc_x();
  p->a = 1;
  p->b = 2;
  return p;
}


В измененном примере мы вызываем функцию, которая возвращает указатель на объект типа X, и компилятор понятия не имеет, каким образом он получен. Разница только в том, что в исходном примере компилятор видит слово malloc и думет: «ага, мы тут память неинициализированную выделяем», хотя никто ему не давал права судить, что именно malloc() делает семантически.
Насчет FORCE_INLINE имеется некоторое лукавство.
1. Проблема с кэшом инструкций может возникнуть не от самого по себе инлайна, а когда один и тот же код инлайнится многократно. Ибо если функция не инлайнится, то ее код все равно попадает в кэш инструкций :)
2. Обычно FORCE_INLINE применяют в тех случаях, когда это буквально пара строчек, которые определять #define'ом вроде как уже некошерно, а возможности сделать intrinsic для компилятора нет. И в этих случаях FORCE_INLINE вполне оправдан. А если кто-то принудительно инлайнит функции на несколько десятков строчек кода, то он сам себе злобный Буратино.

Если уж тема инлайна и кэша инструкций была затронута, то можно было бы обратить внимание и на смежную проблему — когда кэш заполняется за счет кусков кода, которые потенциально не исполняются вообще или исполняются на slow path. В некоторых случаях компилятор может сам оптимизировать такие случаи, хороший пример — инициализация статических переменных, которую компилятор выносит за пределы тела функции. В общем же случае компилятор ничего не знает, насколько вероятно выполнение/невыполнение некоторого условия. С точки зрения процессора, пока код еще не выполнялся, действует static branch prediction, у которого, например, на Intel'е крайне простые правила — условный переход назад считается вероятным событием, условный переход вперед — маловероятным. В соответствии с этим префетчер будет подтягивать инсрукции в кэш. Поэтому во многих случаях является оправданным использование интринсиков типа GCC'шного __builtin_expect — те самые макросы likely и unlikely, которые многие так не любят. Эти макросы позволяют компилятору наиболее оптимально организовать код с точки зрения кэша инструкций и работы механизма предсказания переходов.
2

Information

Rating
Does not participate
Registered
Activity