Re[3]: Нелогичность static_cast
От: Кодт Россия  
Дата: 06.10.04 13:07
Оценка: 10 (1)
Здравствуйте, Antikrot, Вы писали:

A>Сначала оговорюсь, что все нижесказанное верно, если вообще проц использует сегменты.


К>>Но представим себе, что b стоит на краю сегмента. Т.е. надо вычесть 1000 из адреса (dataseg,0). Что при этом получится? (dataseg,0xFFFFFE00)

A>Да.
K>> или переход на смежный сегмент (otherseg,0)?
A>Соответственно, нет.

К>>Даст ли процессор по пальцам за integer overflow в адресном регистре,

A>нет, не даст. потому что вычитание (с соответствующим overflow) будет проведено не в адресном регистре:
A>00411A10  mov         eax,dword ptr [pb] 
A>00411A13  sub         eax,100h 
A>00411A18  mov         dword ptr [ebp-0E8h],eax

Прекрасно, а кто мешает компилятору в дебаг-версии подклеить сюда же код проверки
jnc @okay
call overflow_in_address_arithmetics


K>>или за загрузку селектора сегмента, на который у программы нет прав?

A>Вот что-то типа того. Короче, access violation.
A>А вероятность такое схватить весьма небольшая... ведь объект-то на стеке, который растет от старших адресов к младшим.

Это допущение. Как устроен стек на конкретной платформе — большой-пребольшой вопрос.

Ещё одна проблема — это возможность получить нулевой указатель.
static_cast правильно приводит нуль-указатели туда-сюда
struct A {...};
struct B {...};
struct C : A, B {...};

int main()
{
  C c;
  C* pc1 = &c;
  B* pb1 = pc1; // неявный static_cast
  C* pd1 = static_cast<C*>(pb1); // d значит downcast
  assert( pd1 == pc1 );

  C* pc0 = NULL;
  B* pb0 = pc0;
  assert( pb0 == NULL );
  C* pd0 = static_cast<C*>(pb0);
  assert( pd0 == NULL );

  static B b;
  B* pb2 = &b;
  C* pd2 = static_cast<B*>(pb2);
  assert( pd2 != NULL ); // увы, таких гарантий нет.
  B* pu2 = pd2; // если pd2 == 0, то и pu2 == 0
  assert( pb2 == pu2 ); // смотрим выше...
}
Перекуём баги на фичи!
Re[3]: Нелогичность static_cast
От: Шахтер Интернет  
Дата: 07.10.04 16:33
Оценка: 10 (1)
Здравствуйте, folk, Вы писали:

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


К>>Но представим себе, что b стоит на краю сегмента. Т.е. надо вычесть 1000 из адреса (dataseg,0). Что при этом получится? (dataseg,0xFFFFFE00) или переход на смежный сегмент (otherseg,0)?

К>>Даст ли процессор по пальцам за integer overflow в адресном регистре, или за загрузку селектора сегмента, на который у программы нет прав?
К>>Вот такая страшилка на ночь.

F>Я заглянул в C99, полагая что уж там-то можно указывать куда попало. Оказалось, в С приращение укзателя дальше чем на one past last element of array приводит к возможному переполнению и ub. Короче, я уверовал, что это не просто страшилки.


Есть одна реальная возможность -- если в проце реализована арифметика с насыщением, к примеру, или взведен флаг, по которому генерируется исключение при переполнении в арифметических операциях.
Темне менее, на наиболее распространённых типах процессоров этой проблемы на самом деле нет.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Нелогичность static_cast
От: Кодт Россия  
Дата: 06.10.04 10:09
Оценка: 1 (1)
Здравствуйте, folk, Вы писали:

F>1. Почему так жестоко? Неужто нельзя было просто ограничить операции, допустимые над результатом static_cast<D*>(B*) ?

F>2. Может я все-таки ошибаюсь и там нет ub?

Там UB, но "мягкое".
Работа static_cast сводится к смещению (при множественном наследовании, например).
В большинстве случаев это "не страшно", если не пытаться разыменовывать полученные заведомо инвалидные указатели.

Однако, получение инвалидного указателя — само по себе UB.
#pragma pack(1)
struct A { char x[0x100]; };
struct B { int y; };
struct C : A, B {};


B b;

main()
{
  B* pb = &b;
  C* pc = static_cast<C*>(pb);
  char* ps = reinterpret_cast<char*>(pb) - 0x100;
  // значения pc и ps совпадают
};

Но представим себе, что b стоит на краю сегмента. Т.е. надо вычесть 1000 из адреса (dataseg,0). Что при этом получится? (dataseg,0xFFFFFE00) или переход на смежный сегмент (otherseg,0)?
Даст ли процессор по пальцам за integer overflow в адресном регистре, или за загрузку селектора сегмента, на который у программы нет прав?
Вот такая страшилка на ночь.
Перекуём баги на фичи!
Re[2]: Нелогичность static_cast
От: folk Россия  
Дата: 07.10.04 07:30
Оценка: :)
Здравствуйте, Кодт, Вы писали:

К>Но представим себе, что b стоит на краю сегмента. Т.е. надо вычесть 1000 из адреса (dataseg,0). Что при этом получится? (dataseg,0xFFFFFE00) или переход на смежный сегмент (otherseg,0)?

К>Даст ли процессор по пальцам за integer overflow в адресном регистре, или за загрузку селектора сегмента, на который у программы нет прав?
К>Вот такая страшилка на ночь.

Я заглянул в C99, полагая что уж там-то можно указывать куда попало. Оказалось, в С приращение укзателя дальше чем на one past last element of array приводит к возможному переполнению и ub. Короче, я уверовал, что это не просто страшилки.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Нелогичность static_cast
От: folk Россия  
Дата: 06.10.04 02:00
Оценка:
struct B {};
struct D : B {};

int main()
{
    B b;
    B* pb1 = reinterpret_cast<B*>( reinterpret_cast<D*>( &b ) ); // pb1 == &b
    B* pb2 = static_cast     <B*>( static_cast     <D*>( &b ) ); // значение pb2 неопределено согласно 5.2.9/1 :crash: 
}

1. Почему так жестоко? Неужто нельзя было просто ограничить операции, допустимые над результатом static_cast<D*>(B*) ?
2. Может я все-таки ошибаюсь и там нет ub?
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re: Нелогичность static_cast
От: lst Россия  
Дата: 06.10.04 04:09
Оценка:
Здравствуйте, folk, Вы писали:

F>
F>struct B {};
F>struct D : B {};

F>int main()
F>{
F>    B b;
F>    B* pb1 = reinterpret_cast<B*>( reinterpret_cast<D*>( &b ) ); // pb1 == &b
F>    B* pb2 = static_cast     <B*>( static_cast     <D*>( &b ) ); // значение pb2 неопределено согласно 5.2.9/1 :crash: 
F>}
F>

F>1. Почему так жестоко? Неужто нельзя было просто ограничить операции, допустимые над результатом static_cast<D*>(B*) ?

F>2. Может я все-таки ошибаюсь и там нет ub?
Есть согласно 5.2.9/8, т.к. в нашем случае не удовлетворяется условие:

[skipped]
If the rvalue of type "pointer to cv1 B" points to the B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D.
[skipped]

Re[2]: Нелогичность static_cast
От: folk Россия  
Дата: 06.10.04 05:16
Оценка:
Здравствуйте, lst, Вы писали:

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


F>>
F>>struct B {};
F>>struct D : B {};

F>>int main()
F>>{
F>>    B b;
F>>    B* pb1 = reinterpret_cast<B*>( reinterpret_cast<D*>( &b ) ); // pb1 == &b
F>>    B* pb2 = static_cast     <B*>( static_cast     <D*>( &b ) ); // значение pb2 неопределено согласно 5.2.9/1 :crash: 
F>>}
F>>

F>>1. Почему так жестоко? Неужто нельзя было просто ограничить операции, допустимые над результатом static_cast<D*>(B*) ?
lst>
F>>2. Может я все-таки ошибаюсь и там нет ub?
lst>Есть согласно 5.2.9/8, т.к. в нашем случае не удовлетворяется условие:

Да, 5.2.9/1 было опиской, конечно же 5.2.9/8

lst>

lst>[skipped]
lst>If the rvalue of type "pointer to cv1 B" points to the B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D.
lst>[skipped]

На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[2]: Нелогичность static_cast
От: Antikrot  
Дата: 06.10.04 12:44
Оценка:
Здравствуйте, Кодт, Вы писали:

Сначала оговорюсь, что все нижесказанное верно, если вообще проц использует сегменты.

К>Но представим себе, что b стоит на краю сегмента. Т.е. надо вычесть 1000 из адреса (dataseg,0). Что при этом получится? (dataseg,0xFFFFFE00)

Да.
K> или переход на смежный сегмент (otherseg,0)?
Соответственно, нет.

К>Даст ли процессор по пальцам за integer overflow в адресном регистре,

нет, не даст. потому что вычитание (с соответствующим overflow) будет проведено не в адресном регистре:
00411A10 mov eax,dword ptr [pb]
00411A13 sub eax,100h
00411A18 mov dword ptr [ebp-0E8h],eax

K>или за загрузку селектора сегмента, на который у программы нет прав?

Вот что-то типа того. Короче, access violation.
А вероятность такое схватить весьма небольшая... ведь объект-то на стеке, который растет от старших адресов к младшим.

К>Вот такая страшилка на ночь.
Re[3]: Нелогичность static_cast
От: achp  
Дата: 06.10.04 16:33
Оценка:
Здравствуйте, Antikrot, Вы писали:

A>нет, не даст. потому что вычитание (с соответствующим overflow) будет проведено не в адресном регистре:


А откуда вы это знаете?

A>00411A10 mov eax,dword ptr [pb]

A>00411A13 sub eax,100h
A>00411A18 mov dword ptr [ebp-0E8h],eax

Отдаленно напоминает ассемблер, но на моей любимой платформе "Супер-пупер платформа" таких команд нет.
Я кончил, джентльмены, мне остается только поблагодарить вас за внимание.
Re[2]: Нелогичность static_cast
От: folk Россия  
Дата: 06.10.04 23:21
Оценка:
Здравствуйте, Кодт, Вы писали:

F>>1. Почему так жестоко? Неужто нельзя было просто ограничить операции, допустимые над результатом static_cast<D*>(B*) ?

F>>2. Может я все-таки ошибаюсь и там нет ub?

К>Там UB, но "мягкое".

К>Работа static_cast сводится к смещению (при множественном наследовании, например).

Вобщем об этом и речь. Была бы удобной возможность откастить указатель в одном месте, хранить его в таком виде, и затем в другом месте корректно прикастить его обратно, с помощью того же static_cast или даже использованием указателя на член, который содержит корректное смещение this.

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


Жаль нет гарантий.

К>Однако, получение инвалидного указателя — само по себе UB.

К>
К>#pragma pack(1)
К>struct A { char x[0x100]; };
К>struct B { int y; };
К>struct C : A, B {};


К>B b;

К>main()
К>{
К>  B* pb = &b;
К>  C* pc = static_cast<C*>(pb);
К>  char* ps = reinterpret_cast<char*>(pb) - 0x100;
К>  // значения pc и ps совпадают
К>};
К>

К>Но представим себе, что b стоит на краю сегмента. Т.е. надо вычесть 1000 из адреса (dataseg,0). Что при этом получится? (dataseg,0xFFFFFE00) или переход на смежный сегмент (otherseg,0)?
К>Даст ли процессор по пальцам за integer overflow в адресном регистре, или за загрузку селектора сегмента, на который у программы нет прав?

Согласен с Antikrot, что нашему указателю нечего делать в адресном регистре, до тех пор пока над ним не производятся операции, связанные с его разыменованием.
Имхо, у такого указателя столько же "прав" на загрузку селектора сегмента, как и у этого:
int main()
{
  SomeClass* p; // неинициализированный указатель, как насчет загрузки селектора сегмента?
}


К>Вот такая страшилка на ночь.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[4]: Нелогичность static_cast
От: folk Россия  
Дата: 06.10.04 23:31
Оценка:
Здравствуйте, Кодт, Вы писали:

К>>>Даст ли процессор по пальцам за integer overflow в адресном регистре,

A>>нет, не даст. потому что вычитание (с соответствующим overflow) будет проведено не в адресном регистре:
К>
A>>00411A10  mov         eax,dword ptr [pb] 
A>>00411A13  sub         eax,100h 
A>>00411A18  mov         dword ptr [ebp-0E8h],eax
К>

К>Прекрасно, а кто мешает компилятору в дебаг-версии подклеить сюда же код проверки
К>
К>jnc @okay
К>call overflow_in_address_arithmetics
К>


Да, никто не мешает.

[]

К>Ещё одна проблема — это возможность получить нулевой указатель.

К>static_cast правильно приводит нуль-указатели туда-сюда
К>
К>struct A {...};
К>struct B {...};
К>struct C : A, B {...};

К>int main()
К>{
К>  C c;
К>  C* pc1 = &c;
К>  B* pb1 = pc1; // неявный static_cast
К>  C* pd1 = static_cast<C*>(pb1); // d значит downcast
К>  assert( pd1 == pc1 );

К>  C* pc0 = NULL;
К>  B* pb0 = pc0;
К>  assert( pb0 == NULL );
К>  C* pd0 = static_cast<C*>(pb0);
К>  assert( pd0 == NULL );

К>  static B b;
К>  B* pb2 = &b;
К>  C* pd2 = static_cast<B*>(pb2);
К>  assert( pd2 != NULL ); // увы, таких гарантий нет.
К>  B* pu2 = pd2; // если pd2 == 0, то и pu2 == 0
К>  assert( pb2 == pu2 ); // смотрим выше...
К>}
К>


Упс, об этом я не подумал. Действительно, VC при static_cast проверяет указатель на ноль, и либо смещает его, либо оставляет нулем.

Видимо я "по старинке" провожу аналогию между операциями над значениями указателей и беззнаковой арифметикой. И поэтому специальные проверки на переполнение или сравнение указателя с нулем при static_cast представляются мне нелогичными.
А ведь сабж этого топика — нелогичность, буду стараться чтобы это показалось мне логичным
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[3]: Нелогичность static_cast
От: Павел Кузнецов  
Дата: 06.10.04 23:33
Оценка:
folk:

> Имхо, у такого указателя столько же "прав" на загрузку селектора сегмента, как и у этого:
> int main()
> {
>   SomeClass* p; // неинициализированный указатель, как насчет загрузки селектора сегмента?
> }
>


Не совсем. Значение данного указателя нигде не используется. Если начнет использоваться (например, копироваться) до инициализации — будут те же самые последствия по тем же самым причинам.
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: Нелогичность static_cast
От: folk Россия  
Дата: 07.10.04 00:00
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>folk:


>> Имхо, у такого указателя столько же "прав" на загрузку селектора сегмента, как и у этого:
>> int main()
>> {
>>   SomeClass* p; // неинициализированный указатель, как насчет загрузки селектора сегмента?
>> }
>>


ПК>Не совсем. Значение данного указателя нигде не используется. Если начнет использоваться (например, копироваться) до инициализации — будут те же самые последствия по тем же самым причинам.


Мне совершенно ясно, что по стандарту этому указателю можно только присвоить значение, а результат всех остальных операций неопределен.
Я хотел показать, что все же бывают указатели, указывающие "куда попало" и само по себе их существование не приводит к загрузке селектора сегмента. И что само по себе приращение указателся до "невалидного" состояния без последующего разыменования также вряд ли приведет к загрузке. Имхо.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[5]: Нелогичность static_cast
От: Павел Кузнецов  
Дата: 07.10.04 00:54
Оценка:
folk:

> Я хотел показать, что все же бывают указатели, указывающие "куда попало" и само по себе их существование не приводит к загрузке селектора сегмента.


Существование — нет. Использование его значения — имеет право. Скажем, static_cast таким использованием значения является.

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


Не обязательно разыменование. Можно и копирование... В присутствии оптимизации — вполне может. Всё зависит от сложности кода и логики оптимизатора...
Posted via RSDN NNTP Server 1.9 gamma
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[6]: Нелогичность static_cast
От: Кодт Россия  
Дата: 07.10.04 08:11
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

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


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


Тем более, что иногда компилятор использует адресную арифметику процессора в корыстных целях. Инструкция LEA, например.
Перекуём баги на фичи!
Re[3]: Нелогичность static_cast
От: Кодт Россия  
Дата: 07.10.04 08:15
Оценка:
Здравствуйте, folk, Вы писали:

F>Вобщем об этом и речь. Была бы удобной возможность откастить указатель в одном месте, хранить его в таком виде, и затем в другом месте корректно прикастить его обратно, с помощью того же static_cast или даже использованием указателя на член, который содержит корректное смещение this.


Какие проблемы?
Берёшь валидный объект, получаешь валидный указатель на него или на любой базовый подобъект и хранишь на здоровье. Затем выполняешь правильный даункаст — снова получаешь валидный указатель на исходный объект.

А всякие трюки с промежуточными невалидными значениями — нафига они нужны?
Перекуём баги на фичи!
Re[4]: Нелогичность static_cast
От: Antikrot  
Дата: 07.10.04 09:21
Оценка:
Здравствуйте, achp, Вы писали:

A>>нет, не даст. потому что вычитание (с соответствующим overflow) будет проведено не в адресном регистре:

A>А откуда вы это знаете?
Ну давайте тогда решим, какие регистры адресные...
типа ebp-esp? ну тогда подумай, зачем компилятору там чего-то считать? надо же будет сохранять-восстанавливать их предыдущие значения... не, конечно он может додуматься, что ничего восстанавливать не надо — не потребуется, но я таких не видел ни разу.

A>>00411A18 mov dword ptr [ebp-0E8h],eax

A>Отдаленно напоминает ассемблер, но на моей любимой платформе "Супер-пупер платформа" таких команд нет.
Предположим, на моей тоже . И предположим также, что для меня единственный адресный регистр — gp (global pointer) / Шутка /

Сорри за флейм...
Re[4]: Нелогичность static_cast
От: folk Россия  
Дата: 07.10.04 11:11
Оценка:
Здравствуйте, Кодт, Вы писали:

F>>Вобщем об этом и речь. Была бы удобной возможность откастить указатель в одном месте, хранить его в таком виде, и затем в другом месте корректно прикастить его обратно, с помощью того же static_cast или даже использованием указателя на член, который содержит корректное смещение this.


К>Какие проблемы?

К>Берёшь валидный объект, получаешь валидный указатель на него или на любой базовый подобъект и хранишь на здоровье. Затем выполняешь правильный даункаст — снова получаешь валидный указатель на исходный объект.

Ну разумеется ты прав.

К>А всякие трюки с промежуточными невалидными значениями — нафига они нужны?


Ок, попытаюсь объяснить откуда ересь.
На днях здесь вспыла вечноживая тема "как сделать closure". Подумался очередной вариант — с помощью извращенного кросс-каста — приводить указатель на любой класс к посторонней "общей" базе и соответствующим образом подстраивать указатель на функцию-член. Типа этого:
class Closure
{
 public:
    typedef int Func(int);
    
    template<class T> Closure(T*, Func T::*);
    
    int operator()(int i) const { return ( object_->*method_ )( i ); }
 
 private:
    // Наследование от A и B нужно, чтобы VC использовал для CommonBase
    // multiple inheritance member pointer representation без всяких прагм.
    // Для других компиляторов этого может не понадобиться, но хуже от этого не будет.
    class A {};
    class B {};
    class CommonBase : A, B {};
    
    CommonBase* object_;
    Func CommonBase::* method_;
};

template<class T>
Closure::Closure(T* object, Func T::* method)
{
    class CommonDeriver : public T, public CommonBase {};
    
    Func CommonDeriver::* pmf = method;
    this->method_ = static_cast<Func CommonBase::*>( pmf );
    
    CommonDeriver* po = static_cast<CommonDeriver*>( object ); // ub, обидно однако
    this->object_ = po;
}
используем:
#include<iostream>

struct Test
{
    int i;
    int add(int j) { return i + j; }
};

int main()
{
    Test t = { 5 };
    Closure c( &t, &Test::add );
    std::cout << c( 2 ) << '\n';
    return 0;
}

Где ub понятно, других ub я здесь не вижу. Не то чтобы оно мне было нужно, просто стало немного обидно что так нельзя. Вот и все дела.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[6]: Нелогичность static_cast
От: Шахтер Интернет  
Дата: 07.10.04 16:33
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>folk:


>> Я хотел показать, что все же бывают указатели, указывающие "куда попало" и само по себе их существование не приводит к загрузке селектора сегмента.


ПК>Существование — нет. Использование его значения — имеет право. Скажем, static_cast таким использованием значения является.


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


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


Вообще-то, оптимизатор должен оптимизировать код. Может вы подскажите, что это за чудо процессор такой, у которого загрузка сегментного регистра с проверкой селектора сегмента выполняется быстрее, чем простая загрузка регистра общего назначения? Очень хочу поглядеть на это чудо инженерной мысли.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: Нелогичность static_cast
От: Шахтер Интернет  
Дата: 07.10.04 16:33
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Павел Кузнецов, Вы писали:


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


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


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


Не въехал, что значит "тем более".
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
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...
Пока на собственное сообщение не было ответов, его можно удалить.