Re[7]: Нелогичность static_cast
От: Павел Кузнецов  
Дата: 07.10.04 18:33
Оценка:
Шахтер:

> ПК>Не обязательно разыменование. Можно и копирование... В присутствии оптимизации — вполне может. Всё зависит от сложности кода и логики оптимизатора...


> Вообще-то, оптимизатор должен оптимизировать код.


При этом оптимизация может быть как по размеру, так и по скорости. И для обоих случаев можно вполне представить примеры, когда ранняя загрузка сегментного регистра может быть выгоднее, чем поздняя.

> Может вы подскажите, что это за чудо процессор такой, у которого загрузка сегментного регистра с проверкой селектора сегмента выполняется быстрее, чем простая загрузка регистра общего назначения?


Давай представим, что загрузка сегментного регистра в 10000 раз медленее, чем загрузка регистра общего назначения — так даже нагляднее получится. И представим такой гипотетический пример:
int f(A* a, B* b, int size)
{
   int result = 0;
   for (int i = 0; i < size; ++i)
   {
     if (a->f(i))
       result += a->g(i, b);
     else
       result += b->h(i);
   }
}

Можно легко заметить, что непосредственно в данной функции b разыменовывается только в одной из веток. Но если в результате какой-нибудь profile-guided оптимизации выяснится, что вынесение загрузки b в сегментный регистр за пределы цикла уменьшает среднее время исполнения этой функции в 10 раз, то я вполне пойму логику оптимизатора, загружающего сегментный регистр заранее, даже если иногда при вызове этой функции ветка с разыменованием b вообще не будет выполняться.
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[8]: Нелогичность static_cast
От: Шахтер Интернет  
Дата: 08.10.04 15:56
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Шахтер:


>> ПК>Не обязательно разыменование. Можно и копирование... В присутствии оптимизации — вполне может. Всё зависит от сложности кода и логики оптимизатора...


>> Вообще-то, оптимизатор должен оптимизировать код.


ПК>При этом оптимизация может быть как по размеру, так и по скорости. И для обоих случаев можно вполне представить примеры, когда ранняя загрузка сегментного регистра может быть выгоднее, чем поздняя.


>> Может вы подскажите, что это за чудо процессор такой, у которого загрузка сегментного регистра с проверкой селектора сегмента выполняется быстрее, чем простая загрузка регистра общего назначения?


ПК>Давай представим, что загрузка сегментного регистра в 10000 раз медленее, чем загрузка регистра общего назначения — так даже нагляднее получится. И представим такой гипотетический пример:

ПК>
ПК>int f(A* a, B* b, int size)
ПК>{
ПК>   int result = 0;
ПК>   for (int i = 0; i < size; ++i)
ПК>   {
ПК>     if (a->f(i))
ПК>       result += a->g(i, b);
ПК>     else
ПК>       result += b->h(i);
ПК>   }
ПК>}
ПК>

ПК>Можно легко заметить, что непосредственно в данной функции b разыменовывается только в одной из веток. Но если в результате какой-нибудь profile-guided оптимизации выяснится, что вынесение загрузки b в сегментный регистр за пределы цикла уменьшает среднее время исполнения этой функции в 10 раз, то я вполне пойму логику оптимизатора, загружающего сегментный регистр заранее, даже если иногда при вызове этой функции ветка с разыменованием b вообще не будет выполняться.

Боюсь, что пример не корректен. Если я передам в b нулевой укащатель(а это вполне законно), то при ранней загрузке сегментного регистра получу исключение. Так что данная оптимизация некорректна.

А вообще, с тяжелыми сегментными регистрами так не работают. Их обычно загружают вручную и не меняют без нужды.

Вообщем, я пока не вижу большой пользы от запрета копирования неинициализированных фундаментальных типов, включая указатели. Тем более, что наиболее распространённые платформы это допускают.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[9]: Нелогичность static_cast
От: Павел Кузнецов  
Дата: 08.10.04 17:44
Оценка:
Шахтер:

> Боюсь, что пример не корректен. Если я передам в b нулевой укащатель(а это вполне законно), то при ранней загрузке сегментного регистра получу исключение. Так что данная оптимизация некорректна.


Оптимизатор вполне может включить проверку на 0

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


Для платформ, на которых это допустимо, неопределенное поведение всегда можно определить. А вот что делать с теми платформами, у которых подобные операции недопустимы?
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[10]: Нелогичность static_cast
От: Шахтер Интернет  
Дата: 08.10.04 18:06
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Шахтер:


>> Боюсь, что пример не корректен. Если я передам в b нулевой укащатель(а это вполне законно), то при ранней загрузке сегментного регистра получу исключение. Так что данная оптимизация некорректна.


ПК>Оптимизатор вполне может включить проверку на 0


Т.е. фактически, данный вид оптимизации будет базироваться на запрете копирования неинициализированных указателей. А теперь вынесем этот код из функции в блок

{
...
{
A* a=что-то, B* b, int size=что-то;

int result = 0;
for (int i = 0; i < size; ++i)
{
if (a->f(i))
result += a->g(i, b);
else
result += b->h(i);
}
}
...
}

Этот фрагмент не содержит UB, если else не выполняется. Т.е. его оптимизировать таким способом уже нельзя. Хождение по краю какое-то.

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


ПК>Для платформ, на которых это допустимо, неопределенное поведение всегда можно определить. А вот что делать с теми платформами, у которых подобные операции недопустимы?


А между прочим, есть выход. Уровни конформантности. Т.е. компилятор должен явно декларировать некоторые свойства платформы. Скажем, путем определения макросимволов. В тот же __cplusplus можно зафигачить набор флагов. И выбирать код, соответствующий платформе.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[11]: Нелогичность static_cast
От: Павел Кузнецов  
Дата: 08.10.04 18:15
Оценка:
Шахтер:

> Т.е. фактически, данный вид оптимизации будет базироваться на запрете копирования неинициализированных указателей. А теперь вынесем этот код из функции в блок

>
> {
> ...
> {
>  A* a=что-то, B* b, int size=что-то;
>
>    int result = 0;
>    for (int i = 0; i < size; ++i)
>    {
>      if (a->f(i))
>        result += a->g(i, b);
>      else
>        result += b->h(i);
>    }
> }
> ...
> }
>

> Этот фрагмент не содержит UB, если else не выполняется

Почему не содержит? Копирование невалидного указателя все равно происходит...

> ПК>Для платформ, на которых это допустимо, неопределенное поведение всегда можно определить. А вот что делать с теми платформами, у которых подобные операции недопустимы?


> А между прочим, есть выход. Уровни конформантности. Т.е. компилятор должен явно декларировать некоторые свойства платформы. Скажем, путем определения макросимволов. В тот же __cplusplus можно зафигачить набор флагов. И выбирать код, соответствующий платформе.


На практике это приведет к провоцированию появления множества диалектов языка, что вряд ли можно назвать хорошим делом.
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[12]: Нелогичность static_cast
От: Шахтер Интернет  
Дата: 09.10.04 20:43
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Шахтер:


>> Т.е. фактически, данный вид оптимизации будет базироваться на запрете копирования неинициализированных указателей. А теперь вынесем этот код из функции в блок

>>
>> {
>> ...
>> {
>>  A* a=что-то, B* b, int size=что-то;
>>
>>    int result = 0;
>>    for (int i = 0; i < size; ++i)
>>    {
>>      if (a->f(i))
>>        result += a->g(i, b);
>>      else
>>        result += b->h(i);
>>    }
>> }
>> ...
>> }
>>

>> Этот фрагмент не содержит UB, если else не выполняется

ПК>Почему не содержит? Копирование невалидного указателя все равно происходит...


Вот так тогда.

{
...
{
 A* a=что-то, B* b, int size=что-то;

 int result = 0;
 
 for (int i = 0; i < size; ++i)
   {
    if (a->f(i))
       result += a->g(i);
    else
       result += b->h(i);
   }
}
...
}


Кстати, я что-то протормозил с проверкой на нуль. Это сделать конечно можно, но придётся код цикла сдубрировать, поскольку он будет завязан на предположение о предзагрузке, а в случае нулевого указателя это предположение неверно.

>> ПК>Для платформ, на которых это допустимо, неопределенное поведение всегда можно определить. А вот что делать с теми платформами, у которых подобные операции недопустимы?


>> А между прочим, есть выход. Уровни конформантности. Т.е. компилятор должен явно декларировать некоторые свойства платформы. Скажем, путем определения макросимволов. В тот же __cplusplus можно зафигачить набор флагов. И выбирать код, соответствующий платформе.


ПК>На практике это приведет к провоцированию появления множества диалектов языка, что вряд ли можно назвать хорошим делом.


ЭЭЭ. Да эти диалекты уже существуют по факту. Поздно пить боржоми. Совсем поздно. Даже гемодиализ не спасет.

Как раз наоборот, было бы полезно этот дикий сейчас процесс ввести в нормальное русло -- т.е. оформить де юре, то что уже и так существует де факто. Это даст возможность упростить перенос кода между платформами, потому что специфические куски кода будут явно выделены.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
ПРОЦЕСС ПОШЕЛ
От: Шахтер Интернет  
Дата: 11.10.04 22:12
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

Фрагмент из yasli::vector

template <class T, class Allocator>
class vector 
{
    struct ebo : public Allocator
    {
        T *beg_;
        ebo() {}
        ebo(const Allocator& a) : Allocator(a) {}
    } ebo_;
    T *end_;
    T *eos_; 

private:
    void init_empty() 
    {
#if YASLI_UNDEFINED_POINTERS_COPYABLE == 1
        end_ = ebo_.beg_;
        eos_ = ebo_.beg_;
#else
        ebo_.beg_ = 0;
        end_ = 0;
        eos_ = 0;
#endif
        assert(empty());
    }


Когда увидел, долго ржал.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: ПРОЦЕСС ПОШЕЛ
От: folk Россия  
Дата: 12.10.04 03:42
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Фрагмент из yasli::vector


Ш>
Ш>template <class T, class Allocator>
Ш>class vector 
Ш>{
Ш>    struct ebo : public Allocator
Ш>    {
Ш>        T *beg_;
Ш>        ebo() {}
Ш>        ebo(const Allocator& a) : Allocator(a) {}
Ш>    } ebo_;
Ш>    T *end_;
Ш>    T *eos_; 

Ш>private:
Ш>    void init_empty() 
Ш>    {
Ш>#if YASLI_UNDEFINED_POINTERS_COPYABLE == 1
Ш>        end_ = ebo_.beg_;
Ш>        eos_ = ebo_.beg_;
Ш>#else
Ш>        ebo_.beg_ = 0;
Ш>        end_ = 0;
Ш>        eos_ = 0;
Ш>#endif
Ш>        assert(empty());
Ш>    }

Ш>


Ш>Когда увидел, долго ржал.


Имо, в данном случае это экономия на спичках, от которой мало пользы. И вообще, когда несколько месяцев назад смотрел yasli::vector, сложилось впечатление, что ясландеры иногда уделяют слишком много внимания всякой мелочной фигне, вместо того чтобы потратить время на что-нибудь более полезное.

Например запомнилась специализация функции destroy для примитивных типов, которая ничего не делает. Вменяемый оптимизатор и сам сможет выкинуть цикл for( ; p != end; ++p ) p->~T(); если T имеет тривиальный деструктор. А значит нефиг усложнять код ненужными подробностями.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[2]: ПРОЦЕСС ПОШЕЛ
От: Шахтер Интернет  
Дата: 12.10.04 16:39
Оценка:
Здравствуйте, folk, Вы писали:

F>Имо, в данном случае это экономия на спичках, от которой мало пользы.


Не факт, что тут вообще будет экономия. В варианте

#if YASLI_UNDEFINED_POINTERS_COPYABLE == 1
        end_ = ebo_.beg_;
        eos_ = ebo_.beg_;


мы имеем одну операцию чтения из памяти и две записи.

В этом варианте

        ebo_.beg_ = 0;
        end_ = 0;
        eos_ = 0;


три операции записи. Скорее всего, тоже на тоже и выйдет. А, кроме того, если посмотреть другие методы, например empty() или деструктор, то там теперь приходится делать две операции чтения из памяти вместо одной, т.е. идёт проигрышь. Суммарный эффект, скорее всего, будет отрицательный.

F>И вообще, когда несколько месяцев назад смотрел yasli::vector, сложилось впечатление, что ясландеры иногда уделяют слишком много внимания всякой мелочной фигне, вместо того чтобы потратить время на что-нибудь более полезное.


Да, есть такое ощущение. Похоже, у Александреску остроумия больше, чем здравого смысла.

F>Например запомнилась специализация функции destroy для примитивных типов, которая ничего не делает. Вменяемый оптимизатор и сам сможет выкинуть цикл for( ; p != end; ++p ) p->~T(); если T имеет тривиальный деструктор. А значит нефиг усложнять код ненужными подробностями.


Согласен.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.