Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Приложите текст речи, пожалуйста — из неё получится хороший конспект.
Правильно ли я понимаю, что по стандарту компилятор должен вернуть мусор, т.к. предполагается, что p_f и p_i указывают на разные области памяти, а программист использовать «безопасные» способы, например, union?Нет, не совсем так. Компилятор ничего не обязан сверх того, что предписано стандартом. Неопределенное поведение потому и опасно, что оно не определено. На вашей машине и вашем компиляторе все может сработать «как надо».
%float_ptr = alloca float
%int_ptr = alloca i32
; ...
store float 3.14, float* %float_ptr, !tbaa !1
store i32 42, i32* %int_ptr, !tbaa !0
; ...
%float_value = load float* %float_ptr, !tbaa !1
; ...
!0 = metadata !{ metadata !"int32" }
!1 = metadata !{ metadata !"float" }
Оптимизатор чего? В том же шланге на уровне фронтенда? Да и в любом другом компиляторе.В данном случае речь про проход SimpleTBAA.
Сама по себе логика оптимизацией основанная на семантики языка не имеет смысла, вернее имеет мало смысла. Оптимизация на то и потимизация, чтобы выйти за семантику языка. Код должен делать что от него требуется, но не должен следовать семантики написанного кода.Вы говорите про наблюдаемое поведение, я говорю про соответстве стандарту.
Поэтому какой-то алиасинг на уровне вменяемого компилятора в данном случае не имеет смысла, ибо компилятор итак знает есть там алиасинг или нет. Нахрен это впилили в стандарт С++, когда как С++ без сильного компилятора не имеет смысла к существованию — мне так неведомо.Компилятор далеко не всегда знает, есть или нет. Может оказаться так, что единственное что есть на руках у компилятора, это прототип функции которую надо вызывать, или прототип функции откуда она получила два значения. В этом смысле strict aliasing работает как последняя способ для компилятора.
Стрикталиасинг не имеет никакого отношения к оптимизациям на том уровне оптимизаторов, которые в эту оптимизацию вообще могут. Там где нет алиасинга — компилятор об этом знает, а там где он есть — это нарушает поведение на которое надеялся тот, кто его писал. Кому нужен профит, если он ломает код, при этом пусть и ломает в рамках стандарта?Вы сами себе противоречите. Сначала вы говорите про выход за семантику языка, потом что код перестает соответствовать тому, что задумал программист. Зачастую люди сами не знают, что они тут написали, а вы хотите чтобы компилятор это понимал. Именно так и происходит, что «код должен делать что от него требуется, но не должен следовать семантики написанного кода».
В конечном итоге и банальная логика, и банальное устройство компиляторов говорит за то, что стрикт алиасинг не имеет смысла и не должен использоваться. Собственно все сменяемые компиляторы этого придерживаются и ни разу я его не видел.Устройство компиляторов далеко не банально, как и логика, за ними стоящая. Давайте все же не будем обсуждать личные качества разработчиков стандарта/компиляторов а сконцентрируемся на описании того что есть. Я не ставлю себе целью защитить strict aliasing, я стараюсь всего лишь донести информацию.
Я выложил доклад ради обсуждения
Чтобы хоть в одном месте можно было бы нормально поговорить на русском языке по интересующим темам и обсудить сильные и слабые стороны.
Очередное враньё ради плюсиков. Можно ссылку на ответ, где описывается «более компактные», либо «почему необходимы лайфтаймы»?
struct bar
{
int Summation(const float* input, int n)
{
sum = 0;
for (int i = 0; i < n; ++i)
{
sum += input[i];
}
};
float sum;
}
struct bar
{
int Summation(const float* input, int n)
{
*sum = 0;
for (int i = 0; i < n; ++i)
{
*sum += input[i];
}
};
bar()
{
sum = new float;
};
~bar()
{
delete sum;
};
private:
float* sum;
}
struct foo
{
int get_sum(const float* input, int size)
{
sum = 0; // try uncomment this
for (int i = 0; i < size; ++i)
{
sum += input[i];
}
return sum;
};
float sum;
};
foo* get_foo();
float* get_floats();
int main(int argc, char** argv) {
float* floats = get_floats();
foo* foo = get_foo();
return foo->get_sum(floats, 10);
}
main: # @main
push esi
sub esp, 8
call get_floats()
mov esi, eax
call get_foo()
mov dword ptr [eax], 0
xorps xmm0, xmm0 ;sum = 0;
addss xmm0, dword ptr [esi] ;sum += input[0];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 4] ;sum += input[1];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 8] ;sum += input[2];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 12] ;sum += input[3];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 16] ;sum += input[4];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 20] ;sum += input[5];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 24] ;sum += input[6];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 28] ;sum += input[7];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 32] ;sum += input[8];
movss dword ptr [eax], xmm0 ;ненужный store sum
addss xmm0, dword ptr [esi + 36] ;sum += input[9];
movss dword ptr [eax], xmm0
cvttss2si eax, xmm0
add esp, 8
pop esi
ret
main: # @main
push esi
sub esp, 8
call get_floats()
mov esi, eax
call get_foo()
mov dword ptr [eax], 0
xorps xmm0, xmm0 ;sum = 0;
addss xmm0, dword ptr [esi] ;sum += input[i];
addss xmm0, dword ptr [esi + 4]
addss xmm0, dword ptr [esi + 8]
addss xmm0, dword ptr [esi + 12]
addss xmm0, dword ptr [esi + 16]
addss xmm0, dword ptr [esi + 20]
addss xmm0, dword ptr [esi + 24]
addss xmm0, dword ptr [esi + 28]
addss xmm0, dword ptr [esi + 32]
addss xmm0, dword ptr [esi + 36]
movss dword ptr [eax], xmm0 ;сохраняем sum
cvttss2si eax, xmm0
add esp, 8
pop esi
ret
struct foo
{
int get_sum(const float* input, int size)
{
float _sum = 0;
for (int i = 0; i < size; ++i)
{
_sum += input[i];
}
sum = _sum;
return sum;
};
float sum;
};
struct foo
{
int get_sum(const float* __restrict input, int size)
{
sum = 0;
for (int i = 0; i < size; ++i)
{
sum += input[i];
}
return sum;
};
float sum;
};
main: # @main
push esi
sub esp, 8
call get_floats()
mov esi, eax
call get_foo()
xorps xmm0, xmm0
addss xmm0, dword ptr [esi]
addss xmm0, dword ptr [esi + 4]
addss xmm0, dword ptr [esi + 8]
addss xmm0, dword ptr [esi + 12]
addss xmm0, dword ptr [esi + 16]
addss xmm0, dword ptr [esi + 20]
addss xmm0, dword ptr [esi + 24]
addss xmm0, dword ptr [esi + 28]
addss xmm0, dword ptr [esi + 32]
addss xmm0, dword ptr [esi + 36]
movss dword ptr [eax], xmm0
cvttss2si eax, xmm0
add esp, 8
pop esi
ret
В данном примере, с get_floats() и get_foo() у компилятора нет ни малейшого шанса что либо проверить, поэтому предпологается худший вариант. Что будет происходить в реальном случае я без понятия.Да, вы верно поняли. Нелокальная переменная индукции без представления о времени жизни объекта не может быть оптимизирована.
Может компилятор сможет отследить все вызовы и все таки догадается, что там нету aliasing? А что если там полиморфизм замешан, и таблица указателей на виртуальные функции, что тогда?Современные компиляторы очень умные.
The experiments yielded that for several programs the statically retrieved data is enormously worse. An example proves that feedback-directed compilers сould greatly enhance program performance for the expected subset of pointers. Also the data can provide an optimistic up to optimal estimate of the points-to sets, which can be used if the statically obtained data is too coarse to be useful.
Троллейбус из буханки или alias analysis в LLVM