Re[11]: Откуда эта лютая любовь к знаковым целым?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 07.05.20 17:52
Оценка: 1 (1)
Здравствуйте, Reset, Вы писали:

R>Не очень понимаю, про какие проверки вы все говорите: godbolt (смотреть на серенькое и желтенькое, т.е. содержимое цикла).


Вы не на то смотрите, посмотрите на прошлый пример. В беззнаковом варианте вычитание и сразу je, в знаковом вычитание, test для проверки результата и за ним jle.

У нас есть цикл: for(TYPE i = 0; i < (end — begin); ++i ) { baz(i); } // (в каком примере где foo, bar, baz — мне пофиг, разберётесь по описанию)

Сначала компилятор его разбирает в:

i = 0;
cycle_body_start: if (!(i < (end - begin))) goto cycle_break;
baz(i);
cycle_continue: ++i; goto cycle_body_start;
cycle_break:


а затем — типовая оптимизация сейчас — превращает в:

i = 0;
if (!(i < (end - begin))) goto cycle_break;
cycle_body:
baz(i);
cycle_continue: ++i;
if (i < (end - begin)) goto cycle_body;
cycle_break:


таким образом, условие выхода в этом варианте может быть реализовано дважды, с противоположным финальным знаком (в условии невхода — инвертировано, а продолжения — прямое).
Почему такая оптимизация — с ходу не помню — но, как вариант, из-за branch prediction.

Теперь начинаем оптимизировать. Компилятор знает, что в цикл он вошёл при i=0, затем i только увеличивался, и он может выкинуть в условии продолжения варианты i >= (end-begin) — что он успешно и делает; поэтому в цикле независимо от знака будет проверка одного и того же типа — на границу, зная, что мы к ней подходим снизу. А вот вначале — на входной проверке прямо перед cycle_body — он в случае знакового знает, что 0 может оказаться всё равно не меньше, чем end — begin, ему не гарантировали ничего про эту разность, поэтому вставлены test + jle; а при беззнаковых 0 > end — begin не может быть, поэтому он сокращает >= до ==.

В вашем же примере картина замутнена использованием не long (тип, равный ptrdiff_t и size_t по ширине), а int. Поэтому мышление компилятора сбито уже странными эффектами — например, что, если end — begin переполнит даже uint32_t? Отсюда совершенно ненужный, в норме, jle на входной проверке. Более того, там jl в цикле! Вы совсем запутали бедную программу, она если на вашем unsigned достигнет 2**31... нет, выполнение не нарушится за счёт того, что `mov eax, ebx` косвенно беззнаково расширяет 32->64, но jl вместо jne показывает, что ему поплохело. А вот если signed int, то он делает jne!

Не делайте так, это вредно.
The God is real, unless declared integer.
Отредактировано 07.05.2020 17:54 netch80 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.