Re: Умный вопрос по C/C++
От: Андрей Коростелев Голландия http://www.korostelev.net/
Дата: 22.08.07 22:18
Оценка: 72 (7) +1
Здравствуйте, McSeem2, Вы писали:

MS>
MS>void swap_words(uint32_t* arg)
MS>{
MS>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>   [...]
MS>

Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
-- Андрей
Re[5]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:45
Оценка: +2 :))) :)))
Здравствуйте, Cyberax, Вы писали:

C>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


О! Мне удалось добиться предупреждения. Надо писать так:
void swap_words(uint32_t* arg)
{
    uint16_t* sp = (uint16_t*)(*(void**)&arg);
    uint16_t  hi = sp[0];
    uint16_t  lo = sp[1];
    
    sp[1] = hi;
    sp[0] = lo;
}


По-моему, это уже клиническая паранойя. И старческий маразм комитета.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 22.08.07 20:52
Оценка: 54 (6)
#include <stdio.h>

void swap_words(uint32_t* arg)
{
    uint16_t* sp = (uint16_t*)arg;
    uint16_t  hi = sp[0];
    uint16_t  lo = sp[1];
    sp[1] = hi;
    sp[0] = lo;
} 

int main()
{
   uint32_t v1 = 0xFFFF0000u;
   uint32_t v2 = 0xFFFF0000u;

   swap_words(&v2);

   printf("%08X %08X\n", v1, v2);
   return 0;
}



Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
Еще подсказка: GCC 3.4.1 и более поздние выдают следующий результат:
gcc -O1 -Wall a.c
./a
FFFF0000 0000FFFF

gcc -O3 -Wall a.c
./a
FFFF0000 FFFF0000


Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: aliasing и aligning
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 05:42
Оценка: 1 (1) +2
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Вообще, такая проблема наблюдается на процессорах, адресация в которых к данным должна быть выравнена по границе 2, 4 и т.д. (8 не встречал)


Не путайте aliasing и aligning!
Это обе засады в С++. Но тем не менее — очень разные вещи

aligning - выравнивание данных. Например, 4-ёх байтное слово должно располагаться по адресу кратному 4 байтам. Корни растут из железа.

aliasing - совмещение имен. Т.е. когда 2 указателя указывают на один и тот же объект, говорится, что это есть alias'ы объекта. Корни растут исключительно из компилятора и связано это с проведением возможных оптимизаций в коде.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Умный вопрос по C/C++
От: Кодт Россия  
Дата: 23.08.07 10:39
Оценка: :)))
Здравствуйте, McSeem2, Вы писали:

<>

А ещё одна версия — правда, дикая — то, что на целевой платформе 32-битный байт!
Ты уверен, что не для БЭСМ-6 компилируешь?
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[8]: Умный вопрос по C/C++
От: CreatorCray  
Дата: 23.08.07 09:51
Оценка: 27 (2)
Здравствуйте, Smal, Вы писали:

S>Комментарии?


ICC 10 /O3
P4 630

Скорость в попугаях

swap_words1: 54.2
swap_words2: 94.8
swap_words3: 122.6

код (есессна все вызовы заинлайнило)
;;;             swap_words1(&v);
  00036 0f b7 54 24 08   movzx edx, WORD PTR [esp+8]            ;.\test.cpp:44.4
  0003b 0f b7 4c 24 0a   movzx ecx, WORD PTR [esp+10]           ;.\test.cpp:44.4
  00040 66 89 54 24 0a   mov WORD PTR [esp+10], dx              ;.\test.cpp:44.4
  00045 66 89 4c 24 08   mov WORD PTR [esp+8], cx               ;.\test.cpp:44.4


;;;             swap_words2(&v);
  0006c 8b 54 24 08      mov edx, DWORD PTR [esp+8]             ;.\test.cpp:53.16
$LN33:
  00070 8b da            mov ebx, edx                           ;.\test.cpp:53.4
  00072 81 e3 00 00 ff 
        ff               and ebx, -65536                        ;.\test.cpp:53.4
  00078 c1 eb 10         shr ebx, 16                            ;.\test.cpp:53.4
  0007b c1 e2 10         shl edx, 16                            ;.\test.cpp:53.4
  0007e 0b da            or ebx, edx                            ;.\test.cpp:53.4
$LN35:
  00080 89 5c 24 08      mov DWORD PTR [esp+8], ebx             ;.\test.cpp:53.16


;;;             swap_words3(&v);
  000a6 c1 44 24 08 10   rol DWORD PTR [esp+8], 16              ;.\test.cpp:62.4


Впечатлил результат компиляции третьего варианта...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:08
Оценка: 8 (2)
Здравствуйте, ilnar, Вы писали:

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


I>...skiped


I>а что если написать так?


I>
I>void swap_words(uint32_t* arg)
I>{
I>    uint16_t* sp = reinterpret_cast<uint16_t*>(arg);
I>    uint16_t  lo = sp[1];
I>    sp[1] = sp[0];
I>    sp[0] = lo;
I>} 
I>


Тоже самое. Надо так.

void swap_words(uint32_t* arg)
{
    union { uint16_t sp[2]; uint32_t v; } u;
    u.v = *arg;
    uint16_t  hi = u.sp[0];
    uint16_t  lo = u.sp[1];

    u.sp[1] = hi;
    u.sp[0] = lo;
    *arg = u.v;
}
С уважением, Александр
Re[4]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 24.08.07 02:30
Оценка: 4 (2)
Ну и в догонку.

/* main.cpp */ 

#include <stdio.h>

// assume unsigned is 32 bit

unsigned SwapHalf(unsigned val)
 {
  return (val<<16)|(val>>16);
 }
 
void SwapHalf(unsigned *var)
 {
  *var=SwapHalf(*var);
 }
 
/* main() */   

int main()
 {
  unsigned val=0xABBABEDA;
  
  SwapHalf(&val);
  
  printf("%.8X\n",val);
 
  return 0;
 }


?SwapHalf@@YAXPAI@Z PROC                ; SwapHalf, COMDAT

; 14   :   *var=SwapHalf(*var);

    mov    eax, DWORD PTR _var$[esp-4]
    mov    ecx, DWORD PTR [eax]
    rol    ecx, 16                    ; 00000010H
    mov    DWORD PTR [eax], ecx

; 15   :  }

    ret    0
?SwapHalf@@YAXPAI@Z ENDP                ; SwapHalf
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[4]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.08.07 08:18
Оценка: 1 (1) +1
Здравствуйте, Smal, Вы писали:

S>А еще лучше так (и короче, и переносимо).


S>
S>void swap_words(uint32_t* arg)
S>{
S>    uint16_t  hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
S>    uint16_t  lo = (uint16_t) ((*arg & 0x0000FFFFu));

S>    *arg = hi | (lo << 16);
S>} 
S>


А по скорости?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[6]: Умный вопрос по C/C++
От: ncode  
Дата: 23.08.07 14:17
Оценка: 1 (1) +1
Здравствуйте, Alexander Pazdnikov, Вы писали:

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


E>>Выравнивание -- это не новая проблема. Но как раз в приведенном McSeem2 механизме свопинга проблем быть не должно. Поскольку uint32_t* будет выравнен на кратную 4-м границу, uint16_t* на кратную 2-м границу, а char* на любой адрес. Такие заморочки были на SPARC-ах когда-то очень давно. И ничего, все работало.


AP>Мда... Сейчас перевожу проект под ARM с Си на С++, так что чувствую, если включить -O2, то в кусках могут появиться интересные глюки, невоспроизводимые при -g ;-)


Были такие моменты у меня, когда с PC на ARM портировал проект, помню я там reinterpret_cast`ы на memcpy() менял, чтобы все работало.
Re[2]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.08.07 07:47
Оценка: +2
Здравствуйте, Андрей Коростелев, Вы писали:

MS>>
MS>>void swap_words(uint32_t* arg)
MS>>{
MS>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>>   [...]
MS>>

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).

Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C

McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Умный вопрос по C/C++
От: Аноним  
Дата: 04.04.08 12:16
Оценка: -1 :)
Здравствуйте, McSeem2, Вы писали:
...
MS>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
...
MS>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.

Что я могу сказать, за такие стандарты и компиляторы расстреливать надо. Недостатки аппаратуры выдавать за стандарт это не просто глупость. Куда мы катимся?! И это на обычных скалярных процессорах, а что же на векторных будет. Выравнивали бы по 64байта.
И вообще тенденция не здоровая аппаратные проблемы сваливать на программистов, причем в такой извращенной форме. Это ж не вам ассемблер.
А если в конкретной реализации процессора команда умножения не будет работать для умножения на 10. Это тоже попадёт в стандарт? типа a*=10; -- нелегально, опасайтесь такого кода и пишите так: a=a+a+a+a+a+a+a+a+a+a; а еще лучше так: std::algotithms::align_safe_assign(a, std::algorithms::safe_multiply( a, reiterpret_cast<std::typeof(a).type>10UUL ));

Такой C++ нам не нужен!

ps: #define true false // удачной отладки
Re[7]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:49
Оценка: 20 (1)
Здравствуйте, eao197, Вы писали:

E>Угу. Только вот когда придется мегабайты данных преобразовывать из host byte order в network byte order разница в скорости двух простых решений может оказаться слишком существенной. Так что результаты замеров были бы очень интересны.


#include <stdio.h>

typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;

void swap_words1(uint32_t* arg)
{
    uint16_t* sp = (uint16_t*)arg;
    uint16_t  hi = sp[0];
    uint16_t  lo = sp[1];
    sp[1] = hi;
    sp[0] = lo;
} 

void swap_words2(uint32_t* arg)
{
    uint16_t  hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
    uint16_t  lo = (uint16_t) ((*arg & 0x0000FFFFu));

    *arg = hi | (lo << 16);
} 

void swap_words3(uint32_t* arg)
{
    union { uint16_t sp[2]; uint32_t v; } u;
    u.v = *arg;
    uint16_t  hi = u.sp[0];
    uint16_t  lo = u.sp[1];

    u.sp[1] = hi;
    u.sp[0] = lo;
    *arg = u.v;
}

int main()
{
   uint32_t v = 0xFFFF0000u;
   int i = 0;

   for ( i = 0; i != 10000000; ++i )
       swap_words1(&v);

   for ( i = 0; i != 10000000; ++i )
       swap_words2(&v);

   for ( i = 0; i != 10000000; ++i )
       swap_words3(&v);


   printf("%08X\n", v);
   return 0;
}


gcc

Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
54.17 0.26 0.26 10000000 26.00 26.00 swap_words3
25.00 0.38 0.12 10000000 12.00 12.00 swap_words1
10.42 0.43 0.05 10000000 5.00 5.00 swap_words2
10.42 0.48 0.05 main


gcc -O1

Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
41.67 0.05 0.05 10000000 5.00 5.00 swap_words2
25.00 0.08 0.03 10000000 3.00 3.00 swap_words3
16.67 0.10 0.02 10000000 2.00 2.00 swap_words1
16.67 0.12 0.02 main


gcc -O2

Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
46.15 0.12 0.12 main
19.23 0.17 0.05 10000000 5.00 5.00 swap_words2
19.23 0.22 0.05 10000000 5.00 5.00 swap_words3
15.38 0.26 0.04 10000000 4.00 4.00 swap_words1


gcc.EXE (GCC) 3.4.4 (mingw special)
P4 3.2GHz, 2Gb RAM

Комментарии?
С уважением, Александр
Re[8]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 25.08.07 03:27
Оценка: 10 (1)
Здравствуйте, Erop, Вы писали:

Короче. Исследовал я вопрос (вообще, в подобных случаях надо не гадать, а мерить). На моём нотеке (T7200 2Ghz) оба метода практически эквивалентны по скорости.

/* main.cpp */ 

#include <stdio.h>

typedef unsigned long long uint64;

/* CPUClock() */ 

__declspec(naked) uint64 CPUClock()
 {
  __asm {
           rdtsc
           ret
        }
 }
 
unsigned SwapHalf(unsigned val)
 {
  return (val<<16)|(val>>16);
 }
 
/* test1()  */  

void test1()
 {
  static volatile unsigned val=0x11112222;
 
  val=SwapHalf(val);
 }
 
/* test2() */  

void test2()
 {
  static volatile unsigned short val[2]={0x1111,0x2222};
  
  unsigned short temp=val[1];
  
  val[1]=val[0];
  val[0]=temp;
 }
 
/* Run<func>() */ 

template <void (*func)()> 
void Run(const char *name)
 {
  uint64 result=uint64(-1);
 
  for(unsigned n=1000; n ;n--)
    {
     uint64 start=CPUClock();
     
     for(unsigned k=1000; k ;k--) func();
     
     uint64 stop=CPUClock();
     
     stop-=start;
     
     if( stop<result ) result=stop;
    }
    
  printf("%s result = %llu\n",name,result);  
 }
 
/* Run2() */  

void Run2()
 {
  uint64 result=uint64(-1);
 
  for(unsigned n=1000; n ;n--)
    {
     uint64 start=CPUClock();
     
     static volatile unsigned short val[2]={0x1111,0x2222};
     
     __asm {
                mov ecx, 1000
            L:    
                sub ecx,1
                
                mov dx, WORD PTR val[0]
                mov ax, WORD PTR val[2]
                mov WORD PTR val[2], dx
                mov WORD PTR val[0], ax
                
                jne L
           }
     
     uint64 stop=CPUClock();
     
     stop-=start;
     
     if( stop<result ) result=stop;
    }
    
  printf("asm swap result = %llu\n",result);  
 }
 
/* main() */   

int main()
 {
  Run<&test1>("rot16");
  Run<&test1>("rot16");
  Run<&test2>("swap");
  Run<&test2>("swap");
  
  Run2();
  Run2();
 
  return 0;
 }


Вот результирующий ассемблер.

test2:

    call    ?CPUClock@@YA_KXZ            ; CPUClock
    mov    esi, eax
    mov    edi, edx

; 53   :      
; 54   :      for(unsigned k=1000; k ;k--) func();

    mov    ecx, 1000                ; 000003e8H
    npad    15
$LL4@Run@2:
    sub    ecx, 1
    mov    ax, WORD PTR ?val@?1??test2@@YAXXZ@4RCGC+2
    mov    dx, WORD PTR ?val@?1??test2@@YAXXZ@4RCGC
    movzx    eax, ax
    mov    WORD PTR ?val@?1??test2@@YAXXZ@4RCGC+2, dx
    mov    WORD PTR ?val@?1??test2@@YAXXZ@4RCGC, ax
    jne    SHORT $LL4@Run@2

; 55   :      
; 56   :      uint64 stop=CPUClock();

    call    ?CPUClock@@YA_KXZ            ; CPUClock


test1:

    call    ?CPUClock@@YA_KXZ            ; CPUClock
    mov    esi, eax
    mov    edi, edx

; 53   :      
; 54   :      for(unsigned k=1000; k ;k--) func();

    mov    ecx, 1000                ; 000003e8H
$LL4@Run:
    mov    eax, DWORD PTR ?val@?1??test1@@YAXXZ@4IC
    rol    eax, 16                    ; 00000010H
    sub    ecx, 1
    mov    DWORD PTR ?val@?1??test1@@YAXXZ@4IC, eax
    jne    SHORT $LL4@Run

; 55   :      
; 56   :      uint64 stop=CPUClock();

    call    ?CPUClock@@YA_KXZ            ; CPUClock


asm

    call    ?CPUClock@@YA_KXZ            ; CPUClock
    mov    esi, eax
    mov    edi, edx

; 75   :      
; 76   :      static volatile unsigned short val[2]={0x1111,0x2222};
; 77   :      
; 78   :      __asm {
; 79   :                 mov ecx, 1000

    mov    ecx, 1000                ; 000003e8H
$L$1444:

; 80   :             L:    
; 81   :                 sub ecx,1

    sub    ecx, 1

; 82   :                 
; 83   :                 mov dx, WORD PTR val[0]

    mov    dx, WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC

; 84   :                 mov ax, WORD PTR val[2]

    mov    ax, WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC+2

; 85   :                 mov WORD PTR val[2], dx

    mov    WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC+2, dx

; 86   :                 mov WORD PTR val[0], ax

    mov    WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC, ax

; 87   :                 
; 88   :                 jne L

    jne    SHORT $L$1444

; 89   :            }
; 90   :      
; 91   :      uint64 stop=CPUClock();

    call    ?CPUClock@@YA_KXZ            ; CPUClock


Такты

rot16 result = 6132
rot16 result = 6132
swap result = 6264
swap result = 6276
asm swap result = 6036
asm swap result = 6048
Для продолжения нажмите любую клавишу . . .

В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:54
Оценка: 2 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

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


AP>Кто-нибудь!!!

AP>Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!!
AP>Правильно ли я понял?
Можно получить все что угодно, ибо UB.

В реальности я трейсил адреса. arg и sp совпадают.
Правда при трейсах меняется результат, ибо это гейзенбаг.
Что там внутри происходит, я не смотрел. Надо дизассемблировать и смотреть — лень.
Да и какая разница. А т.к. UB, то можно получить и SF .
С уважением, Александр
Re[5]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 25.08.07 01:12
Оценка: 2 (1)
Здравствуйте, McSeem2, Вы писали:

MS>Здравствуйте, Шахтер, Вы писали:


Ш>>Здесь нет запрещения "простого свопинга байт/слов".

Ш>>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода.
Ш>>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки.
Ш>>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.

MS>Вот ты, как мощный Шахтер и объясни мне, как я могу написать собственный аллокатор, 100% соблюдая правило strict aliasing.


Я, кажется понял, что тебя смущает. Ты просто не совсем правильно понимаешь, что именно запрещено делать. Не закапываясь в текст стандарта, просто напишу несколько примеров.

Легально.

int a=...;
unsigned *b=(unsigned *)(void *)&a;

printf("%u\n",*b);


Нелегально.

int a=...;
short *b=(short *)(void *)&a;

printf("%u\n",*b);


Опять легально (сюрприз!).

int a;
char *c=(char *)(void *)&a;

c[0]=...;
c[1]=...;
c[2]=...;
c[3]=...;

printf("%d\n",a);


Манипуляции с указателями вообще под эту статью не попадают. Попадает только доступ.

Bottomline is. Если есть два объекта A *a; B *b; физическое местоположение которых пересекаются (как такое может случится -- отдельный больной вопрос), то значения этих объектов рассматриваются как "связанные" только в четко перечисленных случаях. Во всех остальных случах попытка изменить значение одного объекта меняя значение другого нелегальна. Проще говоря, результаты оптимизации будут непредсказуемые. Как в приведённом изначально примере, компилятор просто счел что вызов функции не имеет видимых побочных эффектов и выбросил его.

MS>Или аллокаторы пишутся тоже из за "патологической глупости помноженной на самоуверенность"?


Нет. Это я вообще не про тебя. Где моя большая кружка пива?
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[8]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 25.08.07 02:00
Оценка: 1 (1)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Шахтер, Вы писали:


Ш>>>>
Ш>>>>    rol    ecx, 16                    ; 00000010H
Ш>>>>


E>>>А это точно быстрее обмена слов?


Ш>>В большинстве современных процессоров самая дорогая операция -- обмен с памятью. Битовые же операции обычно очень дешевые (что не удивительно -- они тривиально реализуютя схемотехнически).


E>С памятью — да. С кэшем -- не факт...


Не забывай, что в первом случае у нас два чтения и две записи, а во втором по одной.

E>А с другой стороны тут на 16 двигать надо. Если это таки в цикле делается...


Barrel-shifter

Хотя раньше -- таки да.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[5]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:40
Оценка: +1
Здравствуйте, Cyberax, Вы писали:

C>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


Кстати, хрен-на-ны.
#include <stdio.h>

void swap_words(uint32_t* arg)
{
    uint16_t* sp = (uint16_t*)(void*)arg;
    uint16_t  hi = sp[0];
    uint16_t  lo = sp[1];
    
    sp[1] = hi;
    sp[0] = lo;
} 

int main()
{
   uint32_t v1 = 0xFFFF0000u;
   uint32_t v2 = 0xFFFF0000u;

   swap_words(&v2);

   printf("%08X %08X\n", v1, v2);
   return 0;
}


$ gcc -O3 -Wall a.c
a.c: In function `main':
a.c:20: warning: unsigned int format, uint32_t arg (arg 2)
a.c:20: warning: unsigned int format, uint32_t arg (arg 3)

$ ./a
FFFF0000 FFFF0000


А должен был бы понять намек...
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: Умный вопрос по C/C++
От: carpenter Голландия  
Дата: 23.08.07 07:37
Оценка: +1
Здравствуйте, Андрей Коростелев, Вы писали:

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



АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).


Блин гавно какоето ... эт я не по поводу ответа , а по вышеописанному
Весь мир — Кремль, а люди в нем — агенты
Re[2]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 07:59
Оценка: -1
Здравствуйте, Андрей Коростелев, Вы писали:

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


MS>>
MS>>void swap_words(uint32_t* arg)
MS>>{
MS>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>>   [...]
MS>>

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).

Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault?!!!
Правильно ли я понял?
Re[3]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:14
Оценка: +1
Здравствуйте, Smal, Вы писали:

....

А еще лучше так (и короче, и переносимо).

void swap_words(uint32_t* arg)
{
    uint16_t  hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
    uint16_t  lo = (uint16_t) ((*arg & 0x0000FFFFu));

    *arg = hi | (lo << 16);
}
С уважением, Александр
Re[4]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 08:19
Оценка: +1
Здравствуйте, Smal, Вы писали:

S>А еще лучше так (и короче, и переносимо).


Согласен, только вот платформа x86 изначально разваращает и в принципе провоцирует использование в С приведение типов, потому как у нее при чтении невыровненной памяти происходит два чтения смежных областей памяти (на 80x86 — 80x286 соответсвенно двух слов) и все пучком, время выполнения увеличивается только.
Re[6]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 08:34
Оценка: -1
Здравствуйте, Smal, Вы писали:

Кто-нибудь!!!
Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!!
Правильно ли я понял?
Re[6]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.08.07 08:34
Оценка: +1
Здравствуйте, Smal, Вы писали:

S>>>А еще лучше так (и короче, и переносимо).


S>>>
S>>>void swap_words(uint32_t* arg)
S>>>{
S>>>    uint16_t  hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
S>>>    uint16_t  lo = (uint16_t) ((*arg & 0x0000FFFFu));

S>>>    *arg = hi | (lo << 16);
S>>>} 
S>>>


E>>А по скорости?

S>Лениво тестировать. Но очень хочется верить, что это не bottle-neck %).
S>Оптимизировать нужно алгоритмы, а не битовые операции. Если окажется, что
S>данная операция сжирает хоть какое-то значимое время, то значит нужно
S>оптимизировать алгоритм, её вызывающий.

Угу. Только вот когда придется мегабайты данных преобразовывать из host byte order в network byte order разница в скорости двух простых решений может оказаться слишком существенной. Так что результаты замеров были бы очень интересны.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: Умный вопрос по C/C++
От: Awaken Украина  
Дата: 23.08.07 23:13
Оценка: +1
пожалуй стоит привести асмовый код полностью
там с /O3 эта функция не вызывается — она инлайнится (тогда зачем компилятор оставил ее код?)

/O1
    .file    "a.c"
    .text
.globl swap_words
    .type    swap_words, @function
swap_words:
    pushl    %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    movzwl    2(%eax), %ecx
    movzwl    (%eax), %edx
    movw    %dx, 2(%eax)
    movw    %cx, (%eax)
    popl    %ebp
    ret
    .size    swap_words, .-swap_words
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "%08X %08X\n"
    .text
.globl main
    .type    main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl    -4(%ecx)
    pushl    %ebp
    movl    %esp, %ebp
    pushl    %ecx
    subl    $36, %esp
    movl    $-65536, -8(%ebp)
    leal    -8(%ebp), %eax
    movl    %eax, (%esp)
    call    swap_words
    movl    -8(%ebp), %eax
    movl    %eax, 8(%esp)
    movl    $-65536, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    $0, %eax
    addl    $36, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size    main, .-main
    .ident    "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
    .section    .note.GNU-stack,"",@progbits


/O3
    .file    "a.c"
    .text
    .p2align 4,,15
.globl swap_words
    .type    swap_words, @function
swap_words:
    pushl    %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    movzwl    2(%eax), %ecx
    movzwl    (%eax), %edx
    movw    %cx, (%eax)
    movw    %dx, 2(%eax)
    popl    %ebp
    ret
    .size    swap_words, .-swap_words
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "%08X %08X\n"
    .text
    .p2align 4,,15
.globl main
    .type    main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl    -4(%ecx)
    pushl    %ebp
    movl    %esp, %ebp
    pushl    %ecx
    subl    $36, %esp
    movzwl    -6(%ebp), %edx
    movzwl    -8(%ebp), %eax
    movl    $-65536, -8(%ebp)
    movw    %dx, -8(%ebp)
    movw    %ax, -6(%ebp)
    movl    $-65536, 8(%esp)
    movl    $-65536, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    addl    $36, %esp
    xorl    %eax, %eax
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size    main, .-main
    .ident    "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
    .section    .note.GNU-stack,"",@progbits
Re[2]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 05:15
Оценка: :)
Здравствуйте, Кодт, Вы писали:

К>Ты уверен, что не для БЭСМ-6 компилируешь?


А вдруг действительно случайно так получилось?



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 10:25
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А, простите, как быть, если у меня два указателя показывают на один и тот же адрес, но один char*, а другой нечто вроде


PD> typedef char (*PCHAR20)[20];


PD>Иными словами, я трактую этот адрес как адрес символа, с одной стороны, и строки символов длины 20 — с другой. Классика, в общем,многомерные массивы...



Выражение PCHAR20[0] имеет тип char& (char lvalue). И именно через него ты получаешь доступ к памяти. Так что в обоих случаях тип, через который ты получаешь доступ к памяти одинаковое. Так что всё законно.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 24.08.07 23:47
Оценка: :)
Здравствуйте, Bell, Вы писали:

B>Здравствуйте, Шахтер, Вы писали:


B>Поправь меня, но ИМХО приведенный вариант с объединением в точности соответствует приведенному тобой первому варианту, который "will work as expected".

B>

Смысл: код нелегален, но текущая версия компилятора так делать позволяет даже при включённом strict-aliasing. Потому что надо поддерживать сущестующий код.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Умный вопрос по C/C++
От: iiice Россия  
Дата: 04.04.08 09:11
Оценка: :)
Так работает:

#include <stdio.h>
#include <stdint.h>


#pragma pack(push, 2)
union un32
{
    uint32_t u32;
    struct
    {
        uint16_t u16_low;
        uint16_t u16_high;
    } u16x2;
};
#pragma pack(pop)

void swap_words(uint32_t* arg)
{
    union un32 *pu = (union un32*)arg;
    uint16_t  hi = pu->u16x2.u16_high;
    uint16_t  lo = pu->u16x2.u16_low;
    pu->u16x2.u16_low  = hi;
    pu->u16x2.u16_high = lo;
}

int main()
{
   uint32_t v1 = 0xFFFF0000u;
   uint32_t v2 = 0xFFFF0000u;

   swap_words(&v2);

   printf("%08X %08X\n", v1, v2);
   return 0;
}
Re[9]: Умный вопрос по C/C++
От: Аноним  
Дата: 07.04.08 10:44
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>>>В системе команд CPU

А>>Система команд CPU. Как раз проблема разработчиков компилятора, а не того кто пишет на C/C++
E>Ну фокус в том, что то, что нельзя сделать на асме, то и на С++ нельзя тем более.
Категорически не согласен, если это не специализированный чип, то на асме можно сделать всё что угодно. И эффективность реализации тут совершенно не важна.

E>Так что у авторов компиляторов нет проблем. Проблемы есть у проектировщиков ПО, которые хотят сделать то, что аппаратура не очень умеет и не особо должна к тому же

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

E>На всякий случай. К char* приводить можно любой указатель и потом по байтам читать память. Это для тех, кто эмуляторы писать хочет очень даже удобно. А для тех, кто хочет то эмуляторы, а то FPU попользоваться ждёт сюрприз в виде того, что даже и не FPU данные иногда в регистры поднимаются, например. И не все состяния процессора и регистров отображаются в биты в памяти при этом...

Вот очень странно если у меня N чисел (сотни и миллионы) с плавающей точкой, то как процессор может их держать у себя в регистрах? Если регистров у него значительно меньше. Мне совершенно не важна внутренняя разрядность и архитектура математического сопроцессора. Но когда он числа сохраняет в память он это делает вполне детерминированно.
Все эти сюрпризы должны решаться и обходиться именно теми кто делает компилятор под эту платформу, а не тем кто пишет кроссплатформенное приложение, а потом выясняется что на этой архитектуре, что бы работало надо знать множество заклинаний и шаманских обрядов, а также досконально знать что именно данный процессор может и как и что компилятор делает не так как все, а по своему.
Re: Умный вопрос по C/C++
От: . Великобритания  
Дата: 22.08.07 21:59
Оценка:
McSeem2 wrote:

> uint16_t* sp = (uint16_t*)arg;

> Undefined Behavior. Вопрос — Где?
Я не считаю себя гуру, но по-моему здесь.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Умный вопрос по C/C++
От: Ужасть бухгалтера  
Дата: 22.08.07 22:31
Оценка:
MS>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?

Мне вот что-то printf не нравится... "X" означает, что ожидается тип int. Если разрядность int отличается от 32, то и возникает Undefined behavior... Правда, является ли это причиной некорректной работы в данном случае, я не знаю...
Re[2]: Умный вопрос по C/C++
От: c-smile Канада http://terrainformatica.com
Дата: 22.08.07 23:49
Оценка:
Здравствуйте, Андрей Коростелев, Вы писали:

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


MS>>
MS>>void swap_words(uint32_t* arg)
MS>>{
MS>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>>   [...]
MS>>

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).

В дополнение: В GCC это управляется с пом. ключа -fstrict-aliasing.
Re: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 02:05
Оценка:
Здравствуйте, McSeem2, Вы писали:

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


Так вот, не могли бы знатоки популярно объяснить, когда точно возникает этот strict aliasing violation?
По идее, всегда при попытке преобразования указателя к другому типу.

Но malloc возващает void*, который по идее надо преобразовать к нужному типу. Что, это становится нелегальным?

Далее, например я делаю свой аллокатор, который, скажем, банально работает в статической памяти.
static char mem_pool[max_size];
. . .
int* allocate_int()
{
   . . .
   return (int*)(&mem_pool[index_aligned_to_int]);  // To UB or not to UB? 
}


Имеет ли место strict aliasing violation в этом случае?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: Умный вопрос по C/C++
От: Cyberax Марс  
Дата: 23.08.07 02:25
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Но malloc возващает void*, который по идее надо преобразовать к нужному типу. Что, это становится нелегальным?

Нет, преобразование из void* к нужному типу легально. Тебе гарантировано, что void* вернет указатель на блок с наиболее общим alignment'ом.

MS>Далее, например я делаю свой аллокатор, который, скажем, банально работает в статической памяти.

MS>
MS>static char mem_pool[max_size];
MS>. . .
MS>int* allocate_int()
MS>{
MS>   . . .
MS>   return (int*)(&mem_pool[index_aligned_to_int]);  // To UB or not to UB? 
MS>}
MS>

MS>Имеет ли место strict aliasing violation в этом случае?
Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать
Sapienti sat!
Re[3]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:12
Оценка:
Здравствуйте, Cyberax, Вы писали:

MS>>Имеет ли место strict aliasing violation в этом случае?

C>Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать

Компилятор не может знать ничего про выравнивание в этом случае — это все на моей совести.

По-моему, они чего-то перемудрили с этим строгим алиасингом. Самое неприятное, что предупреждения может и не быть, но оптимизатор возьмет да выкинет реально необходимые операции.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: Умный вопрос по C/C++
От: Cyberax Марс  
Дата: 23.08.07 03:18
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>>>Имеет ли место strict aliasing violation в этом случае?

C>>Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать
MS>Компилятор не может знать ничего про выравнивание в этом случае — это все на моей совести.
Поэтому с точки зрения Стандарта — это UB.

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

Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])
Sapienti sat!
Re[5]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:34
Оценка:
Здравствуйте, Cyberax, Вы писали:


C>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


Ох, ёлы-палы. Это уже какие-то интриги начинаются...
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Умный вопрос по C/C++
От: ilnar Россия  
Дата: 23.08.07 08:03
Оценка:
Здравствуйте, McSeem2, Вы писали:

...skiped

а что если написать так?

void swap_words(uint32_t* arg)
{
    uint16_t* sp = reinterpret_cast<uint16_t*>(arg);
    uint16_t  lo = sp[1];
    sp[1] = sp[0];
    sp[0] = lo;
}
Re[3]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 08:09
Оценка:
Здравствуйте, eao197, Вы писали:

E>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C


E>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.


Вообще, такая проблема наблюдается на процессорах, адресация в которых к данным должна быть выравнена по границе 2, 4 и т.д. (8 не встречал)
Так вот, сразу же налетел на ARM (AT91RM9200) на выравнивание по границе кратной 2.
Потратил на это порядка несольких часов, просто ушли в холостую.
А произошло это из-за того,что надо было из буфера (char*)[1] получить uint32_t или наоборот в (char*)[1] положить uint32_t.
Тут бы по выравниванию памяти вообще механизм блокировок придумать какой-то было бы просто замечательно.
Re[3]: Умный вопрос по C/C++
От: . Великобритания  
Дата: 23.08.07 08:13
Оценка:
eao197 wrote:

> McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки

> для работы непосредственно с железом, то запрещение простого свопинга
> байт/слов выглядит полным маразмом.
Маразмом является приведение неприводимого. А задача своппинга решается битовыми операциями.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.08.07 08:21
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

E>>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C


E>>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.


AP>Вообще, такая проблема наблюдается на процессорах, адресация в которых к данным должна быть выравнена по границе 2, 4 и т.д. (8 не встречал)

AP>Так вот, сразу же налетел на ARM (AT91RM9200) на выравнивание по границе кратной 2.
AP>Потратил на это порядка несольких часов, просто ушли в холостую.
AP>А произошло это из-за того,что надо было из буфера (char*)[1] получить uint32_t или наоборот в (char*)[1] положить uint32_t.
AP>Тут бы по выравниванию памяти вообще механизм блокировок придумать какой-то было бы просто замечательно.

Выравнивание -- это не новая проблема. Но как раз в приведенном McSeem2 механизме свопинга проблем быть не должно. Поскольку uint32_t* будет выравнен на кратную 4-м границу, uint16_t* на кратную 2-м границу, а char* на любой адрес. Такие заморочки были на SPARC-ах когда-то очень давно. И ничего, все работало.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[3]: Умный вопрос по C/C++
От: Bell Россия  
Дата: 23.08.07 08:24
Оценка:
Здравствуйте, Smal, Вы писали:

S>Тоже самое. Надо так.


S>
S>void swap_words(uint32_t* arg)
S>{
S>    union { uint16_t sp[2]; uint32_t v; } u;
S>    u.v = *arg;
S>    uint16_t  hi = u.sp[0];
S>    uint16_t  lo = u.sp[1];

S>    u.sp[1] = hi;
S>    u.sp[0] = lo;
S>    *arg = u.v;
S>} 
S>


Да, этот вариатн как раз подпадает под 9.2/16 .
Любите книгу — источник знаний (с) М.Горький
Re[5]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 08:29
Оценка:
Здравствуйте, eao197, Вы писали:

E>Выравнивание -- это не новая проблема. Но как раз в приведенном McSeem2 механизме свопинга проблем быть не должно. Поскольку uint32_t* будет выравнен на кратную 4-м границу, uint16_t* на кратную 2-м границу, а char* на любой адрес. Такие заморочки были на SPARC-ах когда-то очень давно. И ничего, все работало.


Мда... Сейчас перевожу проект под ARM с Си на С++, так что чувствую, если включить -O2, то в кусках могут появиться интересные глюки, невоспроизводимые при -g
Re[5]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:32
Оценка:
Здравствуйте, eao197, Вы писали:

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


S>>А еще лучше так (и короче, и переносимо).


S>>
S>>void swap_words(uint32_t* arg)
S>>{
S>>    uint16_t  hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
S>>    uint16_t  lo = (uint16_t) ((*arg & 0x0000FFFFu));

S>>    *arg = hi | (lo << 16);
S>>} 
S>>


E>А по скорости?

Лениво тестировать. Но очень хочется верить, что это не bottle-neck %).
Оптимизировать нужно алгоритмы, а не битовые операции. Если окажется, что
данная операция сжирает хоть какое-то значимое время, то значит нужно
оптимизировать алгоритм, её вызывающий.
С уважением, Александр
Re[4]: Умный вопрос по C/C++
От: Bell Россия  
Дата: 23.08.07 08:37
Оценка:
Здравствуйте, Smal, Вы писали:

S>А еще лучше так (и короче, и переносимо).

Первый вариант тоже вполне переносим. И его тоже можно записать достаточно коротко:

void swap_words(uint32_t* arg)
{
    union { uint32_t v; uint16_t sp[2]; } u = {*arg};
    std::swap(u.sp[0], u.sp[1]);
    *arg = u.v;
}
Любите книгу — источник знаний (с) М.Горький
Re: Умный вопрос по C/C++
От: Аноним  
Дата: 23.08.07 08:56
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>void swap_words(uint32_t* arg)

MS>{
MS> uint16_t* sp = (uint16_t*)arg;
MS> uint16_t hi = sp[0];
MS> uint16_t lo = sp[1];
MS> sp[1] = hi;
MS> sp[0] = lo;
MS>}

А можно увидеть ассемблерный листинг этого чуда? Хочется понять почему для 2-х байтового значения потребовалось более другое выравнивание, чем для 4-х байтового.
Re[7]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 23.08.07 09:17
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!!

AP>Правильно ли я понял?

Нет. Не правильно.
sp будет указывать куда-то на стек. Но вот значение переменной v2 (кажется так её там звали?) может оказаться не на стеке, а в регистре, например, в этот момент.

Я думаю, что в приведённом коде происходит следующее. Функция подставляется, зависимость значения переменной v2 от манипуляций с sp компилятором не отслеживается благодаря обсуждаемому правилу, соответсвенно компилятор видит, что v2 инициализируется, потом от него берут адрес, не являющийся алиасом, потом печатают, и скорее всего просто печатает константу...

Можно код посмотреть, в принципе.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Умный вопрос по C/C++
От: Awaken Украина  
Дата: 23.08.07 09:32
Оценка:
А>А можно увидеть ассемблерный листинг этого чуда? Хочется понять почему для 2-х байтового значения потребовалось более другое выравнивание, чем для 4-х байтового.

я делал ассемблерные листинги обоих вариантов, скомпиленных GCC 4.1.x (linux)
интересно то что команды в дебажном и релизном варианте не отличаются вообще.
но в релизном некоторые переставлены местами (для выравнивания команд на границу слова?)
Re[8]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.08.07 09:32
Оценка:
Здравствуйте, Smal, Вы писали:

S>Комментарии?


А я разве утверждал, что ваш вариант будет самым медленным?
Вот приведенные вами цифры показывают, что на битовых операциях не только переносимее, но и быстрее.

На VC++ 7.1 вариант с union вообще самым медленным оказался.

И кстати, VC++ 7.1 не компилирует вариант swap_words3 (а GCC компилирует), поскольку переменные в функции нужно объявлять до использования:

void swap_words3(uint32_t* arg)
{
    union { unsigned short sp[2]; unsigned int v; } u;

    unsigned short  hi;
    unsigned short  lo;

    u.v = *arg;
    hi = u.sp[0];
    lo = u.sp[1];

    u.sp[1] = hi;
    u.sp[0] = lo;
    *arg = u.v;
}


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[9]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 09:40
Оценка:
Здравствуйте, eao197, Вы писали:

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


S>>Комментарии?


E>А я разве утверждал, что ваш вариант будет самым медленным?

Просто замеры для таких "мелких" операций меня всегда напрягают.
Мне только один раз действительно пришлось ускорять некоторую элементарную
операцию — вычисление барицентрических координат. Но я делал это уже после того, как
исчерпал все оптимизации по алгоритму, прикрутил кэши и т.п.

E>Вот приведенные вами цифры показывают, что на битовых операциях не только переносимее, но и быстрее.

Только если без оптимизаций. Хотя, если выкинуть первый вариант как не работающий, то да. .

E>На VC++ 7.1 вариант с union вообще самым медленным оказался.

.

E>И кстати, VC++ 7.1 не компилирует вариант swap_words3 (а GCC компилирует), поскольку переменные в функции нужно объявлять до использования:

Это С++-ные привычки .
С уважением, Александр
Re[2]: Умный вопрос по C/C++
От: elcste  
Дата: 23.08.07 11:11
Оценка:
Здравствуйте, Андрей Коростелев, Вы писали:

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го.


Почему "введенное в С99"? Если Вы о тексте из 6.5/7, то он представляет собой просто другую редакцию ISO/IEC 9899:1990, 6.3:

An object shall have its stored value accessed only by an lvalue that has one of the following types: 36
— the declared type of the object,
— a qualified version of the declared type of the object,
— a type that is the signed or unsigned type corresponding to the declared type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the declared type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.


36 The intent of this list is to specify those circumstances in which an object may or may not be aliased.

Re[3]: Умный вопрос по C/C++
От: elcste  
Дата: 23.08.07 11:14
Оценка:
Здравствуйте, eao197, Вы писали:

E>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C


В C++ все ровно то же самое.

5.17/8 If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.

Re[2]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 23.08.07 11:23
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А ещё одна версия — правда, дикая — то, что на целевой платформе 32-битный байт!

К>Ты уверен, что не для БЭСМ-6 компилируешь?

три вопрпоса
1) Где качнуть gcc для БЭСМ-6
2) АФАИК там байт был 48-битный
3) Как целевая платформа зависит от опции -o ?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От: Аноним  
Дата: 23.08.07 11:28
Оценка:
Здравствуйте, Awaken, Вы писали:

А>>А можно увидеть ассемблерный листинг этого чуда? Хочется понять почему для 2-х байтового значения потребовалось более другое выравнивание, чем для 4-х байтового.


A>я делал ассемблерные листинги обоих вариантов, скомпиленных GCC 4.1.x (linux)

A>интересно то что команды в дебажном и релизном варианте не отличаются вообще.
A>но в релизном некоторые переставлены местами (для выравнивания команд на границу слова?)

А у вас получился "нерабочий" вариант? Т.е. тот который выводит неизмененное значение переменной?
Скорее всего должно получиться, как написал Егор в http://rsdn.ru/forum/message/2631549.1.aspx
Автор: Erop
Дата: 23.08.07
, т.е. значение переменной меняется, но выводится на экран значение константы, а не переменной. Адреса в таком случае должны быть одинаковыми или это уже похоже на баг оптимизатора.
Re[2]: Умный вопрос по C/C++
От: elcste  
Дата: 23.08.07 11:29
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Так вот, не могли бы знатоки популярно объяснить, когда точно возникает этот strict aliasing violation?


Не знаю, что такое strict aliasing violation, а неопределенное поведение возникает здесь:

MS>    uint16_t  hi = sp[0];

(Строго говоря, приведенный Вами код ill-formed и не является переносимым, но, как я понимаю, Вас интересовало не это.)

MS>По идее, всегда при попытке преобразования указателя к другому типу.


Нет, приведение указателей тут ни при чем.

MS>Но malloc возващает void*, который по идее надо преобразовать к нужному типу. Что, это становится нелегальным?


Нет, конечно. Указатель, возвращаемый malloc, специально выровнен так, чтобы позволить размещение в этой памяти любого объекта.

MS>Далее, например я делаю свой аллокатор, который, скажем, банально работает в статической памяти.

MS>static char mem_pool[max_size];
MS>. . .
MS>int* allocate_int()
MS>{
MS>   . . .
MS>   return (int*)(&mem_pool[index_aligned_to_int]);  // To UB or not to UB? 
MS>}

MS>Имеет ли место strict aliasing violation в этом случае?

Здесь только одна проблема: неверное выравнивание. Никаких гарантий о выравнивании статического массива символов нет.
Re[8]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 11:33
Оценка:
Здравствуйте, Erop, Вы писали:

AP>>Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!!

AP>>Правильно ли я понял?

E>Нет. Не правильно.

E>sp будет указывать куда-то на стек. Но вот значение переменной v2 (кажется так её там звали?) может оказаться не на стеке, а в регистре, например, в этот момент.

E>Я думаю, что в приведённом коде происходит следующее. Функция подставляется, зависимость значения переменной v2 от манипуляций с sp компилятором не отслеживается благодаря обсуждаемому правилу, соответсвенно компилятор видит, что v2 инициализируется, потом от него берут адрес, не являющийся алиасом, потом печатают, и скорее всего просто печатает константу...


И все же получается, что sp может указывать куда угодно, ну и что хорошего что стек порушиться?
Возможно в приведенном варианте так и получиться, а в более сложном случае где-нибудь в середине функции?
Хотя что тут можно обсуждать, успешно упадет или успешно неправильно обработает!!!
Re[6]: Умный вопрос по C/C++
От: c-smile Канада http://terrainformatica.com
Дата: 23.08.07 22:48
Оценка:
Здравствуйте, McSeem2, Вы писали:

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


C>>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


MS>Кстати, хрен-на-ны.


Присоединяюсь. Воообще этот aliasing маразм какой-то. А тем более в исполнении GCC

Я вот еще поймал штучку в GCC:

struct header;

header* ptr;

assert((uint64)ptr == (uint64)(uint32)ptr);


assert срабатывает на 32bit откомпилированном коде.

(uint64)ptr в результате имеет значение 0xFFFFFFFF012345678 где 0x012345678 это реальный адрес.

Повбывав бы.
Re[6]: Умный вопрос по C/C++
От: Cyberax Марс  
Дата: 23.08.07 22:53
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>По-моему, это уже клиническая паранойя. И старческий маразм комитета.

Не волнуйся, сейчас напишем багу в GCC, чтобы здесь тоже выдавалось предупреждение
Sapienti sat!
Re[4]: Умный вопрос по C/C++
От: Awaken Украина  
Дата: 23.08.07 23:05
Оценка:
А>А у вас получился "нерабочий" вариант? Т.е. тот который выводит неизмененное значение переменной


получилось в точности как у автора поста


А>Скорее всего должно получиться, как написал Егор в http://rsdn.ru/forum/message/2631549.1.aspx
Автор: Erop
Дата: 23.08.07
, т.е. значение переменной меняется, но выводится на экран значение константы, а не переменной. Адреса в таком случае должны быть одинаковыми или это уже похоже на баг оптимизатора.


дебаг версия:
.globl swap_words
    .type    swap_words, @function
swap_words:
    pushl    %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    movzwl    2(%eax), %ecx
    movzwl    (%eax), %edx
    movw    %dx, 2(%eax)
    movw    %cx, (%eax)
    popl    %ebp
    ret
    .size


релиз:


    .p2align 4,,15
.globl swap_words
    .type    swap_words, @function
swap_words:
    pushl    %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    movzwl    2(%eax), %ecx
    movzwl    (%eax), %edx
    movw    %cx, (%eax)
    movw    %dx, 2(%eax)
    popl    %ebp
    ret


я не знаю что за директива .p2align, видимо какое-то выравнивание
Re[3]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 24.08.07 02:18
Оценка:
Здравствуйте, eao197, Вы писали:

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


MS>>>
MS>>>void swap_words(uint32_t* arg)
MS>>>{
MS>>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>>>   [...]
MS>>>

АК>>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).

E>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C


E>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.


Здесь нет запрещения "простого свопинга байт/слов".
Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода.
Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки.
Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[5]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 24.08.07 02:53
Оценка:
Здравствуйте, Bell, Вы писали:

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


S>>А еще лучше так (и короче, и переносимо).

B>Первый вариант тоже вполне переносим. И его тоже можно записать достаточно коротко:

B>
B>void swap_words(uint32_t* arg)
B>{
B>    union { uint32_t v; uint16_t sp[2]; } u = {*arg};
B>    std::swap(u.sp[0], u.sp[1]);
B>    *arg = u.v;
B>}
B>


Это тоже нелегально. Вот, кстати, цитата из мануаля по GCC.

Pay special attention to code like this:

union a_union {
int i;
double d;
};

int f() {
a_union t;
t.d = 3.0;
return t.i;
}

The practice of reading from a different union member than the one
most recently written to (called “type-punning”) is common. Even with
‘-fstrict-aliasing’, type-punning is allowed, provided the memory is
accessed through the union type. So, the code above will work as expected.
However, this code might not:

int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}

В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 05:36
Оценка:
Здравствуйте, eao197, Вы писали:

E>Насколько я понимаю, в C++ стандарте такого ограничения нет


Всё тоже самое с точностью до запятых (точнее замены type на dynamic type, и добавления пункта про base class type):

If a program attempts to access the stored value of an object through an lvalue of other than one of the following
types the behavior is undefined (48):
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of
the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including,
recursively, a member of a subaggregate or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
__________________
(48) The intent of this list is to specify those circumstances in which an object may or may not be aliased




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 24.08.07 05:48
Оценка:
Здравствуйте, Шахтер, Вы писали:

E>>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.


Ш>Здесь нет запрещения "простого свопинга байт/слов".

Ш>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода.
Ш>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки.
Ш>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.

Ну если оставить в стороне излишнюю самоуверенность, то такие вещи, как:
* буфера ввода-вывода, указатели на которые передаются либо как void*, либо как char*;
* средства отладочных дампов блоков памяти, которые воспринимают void*, а затем преобразовывают его к char*.

они так же нелегальны?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[2]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 05:50
Оценка:
Здравствуйте, Андрей Коростелев, Вы писали:

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).


Ты хотел сказать, что компилятор вправе думать, что sp не является алиасом для объекта *arg. И соотв. нагенерить кривого кода. Я правильно тебя понял?

А если sp не указывает на &(*arg), то куда ж ему ещё указывать?!
Во-первых, ему явно присвоили именно этот адрес.
Во-вторых, если указывать куда-то ещё, то надо создать дополнительный объект.
Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t.
[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: Умный вопрос по C/C++
От: Bell Россия  
Дата: 24.08.07 06:16
Оценка:
Здравствуйте, Шахтер, Вы писали:

Поправь меня, но ИМХО приведенный вариант с объединением в точности соответствует приведенному тобой первому варианту, который "will work as expected".
Любите книгу — источник знаний (с) М.Горький
Re[5]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 24.08.07 06:28
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>
Ш>    rol    ecx, 16                    ; 00000010H
Ш>


А это точно быстрее обмена слов?
Конечно есть штраф за переход между 16 битами и 32, но если обмен можно произвести задолго до использования, скажем при чтении данных в структуру их потока с неправильным endian.

Хотя, если честно, на практике встречал только задачу перехода между big endian и little endian, а вот так, чтобы полуслова менять -- не встречал
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 24.08.07 06:35
Оценка:
Здравствуйте, remark, Вы писали:

R>А если sp не указывает на &(*arg), то куда ж ему ещё указывать?!

Да он скорее всего туда и указывает.
Просто остальные пользователи *arg могут не заметить твоих последующих усилий и прочитать всё раньше, чем ты запишешь, например

R>Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t.

R>[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]

1) Насколько я понимаю, то если ты приведёшь туда-сюда, то вообще не будет ничего плохого. Не понятен только смысл этой деятельности
2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно. Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB
3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Умный вопрос по C/C++
От: CreatorCray  
Дата: 24.08.07 07:04
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Шахтер, Вы писали:


Ш>>
Ш>>    rol    ecx, 16                    ; 00000010H
Ш>>


E>А это точно быстрее обмена слов?

На большой куче процов будет медленнее...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 07:30
Оценка:
Здравствуйте, Erop, Вы писали:

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


R>>А если sp не указывает на &(*arg), то куда ж ему ещё указывать?!

E>Да он скорее всего туда и указывает.
E>Просто остальные пользователи *arg могут не заметить твоих последующих усилий и прочитать всё раньше, чем ты запишешь, например

R>>Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t.

R>>[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]

E>1) Насколько я понимаю, то если ты приведёшь туда-сюда, то вообще не будет ничего плохого. Не понятен только смысл этой деятельности

E>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно. Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB
E>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.

Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
Re: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 07:40
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.


а может кто-нибудь сказать насколько данное использование reinterpret_cast правильно?


static bool is_not_valid_check_sum(const uint8_t* rec, size_t rec_len)
{
    if (rec_len < 3)
        return true;

    const uint8_t* sz_end = rec + rec_len - sizeof(uint16_t);
    uint16_t sum = 0;

    for (; rec < sz_end; ++rec)
        sum += *rec;

    return sum != *reinterpret_cast<const uint16_t *>(sz_end);
}


идея в том что в конце записи есть двухбайтовое поле содержащие checksum,
подсчитывается оно протым сложением байт записи.

ЗЫ код скорее всего надо будет портировать на arm, поэтому мнение людей работавших с
arm тоже очень и очень интересно?
Re[6]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 07:42
Оценка:
Здравствуйте, Awaken, Вы писали:

A>пожалуй стоит привести асмовый код полностью

A>там с /O3 эта функция не вызывается — она инлайнится (тогда зачем компилятор оставил ее код?)

A>/O1

A>
A>    .file    "a.c"
A>    .text
A>.globl swap_words
A>    .type    swap_words, @function
A>swap_words:
A>    pushl    %ebp
A>    movl    %esp, %ebp
A>    movl    8(%ebp), %eax
A>    movzwl    2(%eax), %ecx
A>    movzwl    (%eax), %edx
A>    movw    %dx, 2(%eax)
A>    movw    %cx, (%eax)
A>    popl    %ebp
A>    ret
A>    .size    swap_words, .-swap_words
A>    .section    .rodata.str1.1,"aMS",@progbits,1
A>.LC0:
A>    .string    "%08X %08X\n"
A>    .text
A>.globl main
A>    .type    main, @function
A>main:
A>    leal    4(%esp), %ecx
A>    andl    $-16, %esp
A>    pushl    -4(%ecx)
A>    pushl    %ebp
A>    movl    %esp, %ebp
A>    pushl    %ecx
A>    subl    $36, %esp
A>    movl    $-65536, -8(%ebp)
A>    leal    -8(%ebp), %eax
A>    movl    %eax, (%esp)
A>    call    swap_words
A>    movl    -8(%ebp), %eax
A>    movl    %eax, 8(%esp)
A>    movl    $-65536, 4(%esp)
A>    movl    $.LC0, (%esp)
A>    call    printf
A>    movl    $0, %eax
A>    addl    $36, %esp
A>    popl    %ecx
A>    popl    %ebp
A>    leal    -4(%ecx), %esp
A>    ret
A>    .size    main, .-main
A>    .ident    "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
A>    .section    .note.GNU-stack,"",@progbits
A>


A>/O3

A>
A>    .file    "a.c"
A>    .text
A>    .p2align 4,,15
A>.globl swap_words
A>    .type    swap_words, @function
A>swap_words:
A>    pushl    %ebp
A>    movl    %esp, %ebp
A>    movl    8(%ebp), %eax
A>    movzwl    2(%eax), %ecx
A>    movzwl    (%eax), %edx
A>    movw    %cx, (%eax)
A>    movw    %dx, 2(%eax)
A>    popl    %ebp
A>    ret
A>    .size    swap_words, .-swap_words
A>    .section    .rodata.str1.1,"aMS",@progbits,1
A>.LC0:
A>    .string    "%08X %08X\n"
A>    .text
A>    .p2align 4,,15
A>.globl main
A>    .type    main, @function
A>main:
A>    leal    4(%esp), %ecx
A>    andl    $-16, %esp
A>    pushl    -4(%ecx)
A>    pushl    %ebp
A>    movl    %esp, %ebp
A>    pushl    %ecx
A>    subl    $36, %esp
A>    movzwl    -6(%ebp), %edx
A>    movzwl    -8(%ebp), %eax
A>    movl    $-65536, -8(%ebp)
A>    movw    %dx, -8(%ebp)
A>    movw    %ax, -6(%ebp)
A>    movl    $-65536, 8(%esp)
A>    movl    $-65536, 4(%esp)
A>    movl    $.LC0, (%esp)
A>    call    printf
A>    addl    $36, %esp
A>    xorl    %eax, %eax
A>    popl    %ecx
A>    popl    %ebp
A>    leal    -4(%ecx), %esp
A>    ret
A>    .size    main, .-main
A>    .ident    "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
A>    .section    .note.GNU-stack,"",@progbits
A>


Похоже, что подозрения подтвердились. Компилятор просто не использовал результат вычисления функции, а подставил исходную константу.
И кому только потребовалась такая оптимизация?
Re[2]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 08:05
Оценка:
Здравствуйте, Аноним, Вы писали:

А>    return sum != *reinterpret_cast<const uint16_t *>(sz_end);
А>}
А>


Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью.
На < ARM9 выравнивание до 4-х байт
На ARM9 выравнивание до 2-х байт.

Для проверки поробуй запустить вот этот тестик.

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>


void hex_print(uint8_t *buf, size_t size);

int main()
{

    

    uint8_t buf1[16], buf2[16], buf3[16];
    uint32_t val;
    uint16_t val16;
    
    memset(buf1, 0, sizeof(buf1));
    memset(buf2, 0, sizeof(buf2));
    memset(buf3, 0, sizeof(buf3));

    val = 0x11223344;
    val16 = 0x7799;
        
    *((uint32_t *)&buf1[0]) = val;
    *((uint32_t *)&buf2[2]) = val;
    *((uint32_t *)&buf3[1]) = val;
    
    *((uint16_t *)&buf1[8]) = val16;
    *((uint16_t *)&buf2[10]) = val16;
    *((uint16_t *)&buf3[9]) = val16;
    
    
    printf("buf1[0] = <%#X>, buf2[2] = <%#X>, buf3[1] = <%#X>\n",
        *((uint32_t *)&buf1[0]),
        *((uint32_t *)&buf2[2]),
        *((uint32_t *)&buf3[1])
    );
    
    printf("buf1[8] = <%#X>, buf2[10] = <%#X>, buf3[9] = <%#X>\n",
        *((uint16_t *)&buf1[8]),
        *((uint16_t *)&buf2[10]),
        *((uint16_t *)&buf3[9])
    );

    for(int i = 0; i < 16; i++){
        printf("%02i|", i);
    }
    printf("\n");

    hex_print(buf1, 16);
    hex_print(buf2, 16);
    hex_print(buf3, 16);
    
    return 0;
}

void hex_print(uint8_t *buf, size_t size)
{
    while(size-- > 0){
        printf("%02X ", *(buf++));
    }
    printf("\n");
}
Re[3]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 08:15
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Здравствуйте, Аноним, Вы писали:


AP>
А>>    return sum != *reinterpret_cast<const uint16_t *>(sz_end);
А>>}
А>>


AP>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью.

AP>На < ARM9 выравнивание до 4-х байт
AP>На ARM9 выравнивание до 2-х байт.

предположительно ARM9,
но вопрос решения остался за рамкой,
надо писать так?

uint16_t valid_sum;
memcpy(&valid_sum, sz_end, sizeof(valid_sum));
return sum != valid_sum;
Re[3]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 08:25
Оценка:
AP>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью.
AP>На < ARM9 выравнивание до 4-х байт
AP>На ARM9 выравнивание до 2-х байт.

и еще вопросы (простите за наглость),
1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д.
2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой,
или складывать не побайтово, а по x-разрядных чисел?
3)а скажем packed структура, мне приходит поток байт
const uint8_t *data, size_t data_len;

а на самом деле это
unsigned some_number;
Foo foo;

где Foo:
struct Foo {
} __attribute__ ((packed));

и я делаю так:
Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));

это тоже будет невалидно на ARM?
Re[4]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 08:29
Оценка:
Здравствуйте, Аноним, Вы писали:
А>предположительно ARM9,
А>но вопрос решения остался за рамкой,
А>надо писать так?

А>uint16_t valid_sum;
А>memcpy(&valid_sum, sz_end, sizeof(valid_sum));
А>return sum != valid_sum;
А>


#include <stdint.h>

#define SET_BUF8_VAL16(BUF_UINT8T__, VAL_UINT16T__) \
    { \
        (BUF_UINT8T__)[0] = ((uint8_t *)&(VAL_UINT16T__))[0]; \
        (BUF_UINT8T__)[1] = ((uint8_t *)&(VAL_UINT16T__))[1]; \
    }

#define SET_VAL16_BUF8(VAL_UINT16T__, BUF_UINT8T__) \
    { \
        ((uint8_t *)&(VAL_UINT16T__))[0] = (BUF_UINT8T__)[0]; \
        ((uint8_t *)&(VAL_UINT16T__))[1] = (BUF_UINT8T__)[1]; \
    }
#define SET_BUF8_VAL32(BUF_UINT8T__, VAL_UINT32T__) \
    { \
        (BUF_UINT8T__)[0] = ((uint8_t *)&(VAL_UINT32T__))[0]; \
        (BUF_UINT8T__)[1] = ((uint8_t *)&(VAL_UINT32T__))[1]; \
        (BUF_UINT8T__)[2] = ((uint8_t *)&(VAL_UINT32T__))[2]; \
        (BUF_UINT8T__)[3] = ((uint8_t *)&(VAL_UINT32T__))[3]; \
    }

#define SET_VAL32_BUF8(VAL_UINT32T__, BUF_UINT8T__) \
    { \
        ((uint8_t *)&(VAL_UINT32T__))[0] = (BUF_UINT8T__)[0]; \
        ((uint8_t *)&(VAL_UINT32T__))[1] = (BUF_UINT8T__)[1]; \
        ((uint8_t *)&(VAL_UINT32T__))[2] = (BUF_UINT8T__)[2]; \
        ((uint8_t *)&(VAL_UINT32T__))[3] = (BUF_UINT8T__)[3]; \
    }


Хотя и вариант с ротацией тоже использовал.

uint16_t valid_sum = sz_end | (sz_end << 8);
return sum != valid_sum;
Re[4]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 08:43
Оценка:
Здравствуйте, Аноним, Вы писали:
А>1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д.
А>2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой,
А>или складывать не побайтово, а по x-разрядных чисел?

Для надежности и переносимости между разными процами( в том числе и ARM, x86 )
лучше медленнее, но надежнее.
#include <vector>
typedef std::vector<uint8_t> BufferType;
typedef uint32_t CrcType; // обязательно беззнаковый, иначе получим вопросы знакорасширения типов.

CrcType Crc8Mod2CheckSum::calc(BufferType& rBuf, bool crc_included_fl)
{
    CrcType cs = 0xFF;

    if (crc_included_fl){
        BufferType::iterator it_end = rBuf.end() - getCrcLen();
    }
    
    for (BufferType::iterator it = rBuf.begin(); it != it_end; ++it){
        cs ^= *it;
    }

    cs &= 0xFF;

    return cs;
}


От reinterpert_cast и прочих cast'ов можно отказаться, возможное их применение только для работы с буферами под пакеты приема/передачи.

uint32_t GetLong(BufferType::iterator& it)
{
    uint32_t retval = 0;

    ///\bug проверить валидность итератора
    for (int shift = 24; shift >= 0; shift -= 8){
        retval |= *it << shift;
        ++it;
    }

    return __le32_to_cpu(retval);
}


Как видите, обхожусь без cast'ов

А>или складывать не побайтово, а по x-разрядных чисел?

Безопаснее побайтно. Тем более для телемеханики — надежность на первом месте.

А>а на самом деле это

А>unsigned some_number;
А>Foo foo;

А>где Foo:

А>struct Foo {
А>} __attribute__ ((packed));

А>и я делаю так:

А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));

А>это тоже будет невалидно на ARM?

Это лучше спросить у людей, которые используют разнообразные хаки от gcc/g++.
Re[5]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 08:46
Оценка:
Кстати, группа функций __le32_to_cpu и прочие работают через ротацию, а это уже в ядре сделано, наверное есть причины не использовать разных хаков.
Re[5]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 08:49
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.


Нет.
Такое использование незаконно. Но остальные использования reinterpret_cast законны.
Например, ты можешь скастить указатель на объект в int, а потом обратно, и нормально с ним работать дальше.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 08:51
Оценка:
Здравствуйте, Erop, Вы писали:

E>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно.


А разьве выделенное не то же самое?

E>Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB


Ключевое слово выделено

E>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.


Здесь тоже


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 09:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>3)а скажем packed структура, мне приходит поток байт

А>const uint8_t *data, size_t data_len;

А>а на самом деле это

А>unsigned some_number;
А>Foo foo;

А>где Foo:

А>struct Foo {
А>} __attribute__ ((packed));

А>и я делаю так:

А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));

А>это тоже будет невалидно на ARM?


Вообще здесь больше проблема переносимости и совместимости разного оборудования, в частности самым выжным обмен данными между little endian и big endian. Ваша структура struct Foo возможно коректно будет обрабатываться между одними типами (le -> le) и (be -> be), но при работе (le -> be) и (be -> le) протокол обмена будет работать некорректно, т.к. порядок слудования байтов на этих устройствах будет разный.
В этом случае необходимо применять __leXX_cpu /__cpu_leXX и __beXX_cpu/__cpu_beXX. Просто определиться с протоколом обмена и порядком следования байт в протоколе обмена (LE или BE). и тогда использовать одну из пар макросов.
Re[4]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 10:00
Оценка:
Здравствуйте, Аноним, Вы писали:

AP>>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью.

AP>>На < ARM9 выравнивание до 4-х байт
AP>>На ARM9 выравнивание до 2-х байт.

А>и еще вопросы (простите за наглость),

А>1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д.
А>2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой,
А>или складывать не побайтово, а по x-разрядных чисел?
А>3)а скажем packed структура, мне приходит поток байт
А>const uint8_t *data, size_t data_len;

А>а на самом деле это

А>unsigned some_number;
А>Foo foo;

А>где Foo:

А>struct Foo {
А>} __attribute__ ((packed));

А>и я делаю так:

А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));

А>это тоже будет невалидно на ARM?


Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
а
struct {
  unsigned some_number;
  Foo foo;
} data;


Тогда можно пользоваться тем способом, как вы написали. (вопрос об aliasing-е не рассматриваем).
Что касается __attribute__ ((packed)), то это определяется компилятором. Он может сам сгенерировать код для доступа к невыравненным данным. Тогда действительно будет плотная упаковка. А может и не иметь такой возможности. Тогда структура будет иметь пустоты, т.е. смещения полей в структуре на ARM и x86 будут разными.
Re[2]: Умный вопрос по C/C++
От: Pavel Dvorkin Россия  
Дата: 24.08.07 10:15
Оценка:
Здравствуйте, Андрей Коростелев, Вы писали:

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


АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).


А, простите, как быть, если у меня два указателя показывают на один и тот же адрес, но один char*, а другой нечто вроде

typedef char (*PCHAR20)[20];

Иными словами, я трактую этот адрес как адрес символа, с одной стороны, и строки символов длины 20 — с другой. Классика, в общем,многомерные массивы...
With best regards
Pavel Dvorkin
Re[6]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 10:25
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Аноним, Вы писали:


А>>Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.


R>Нет.

R>Такое использование незаконно. Но остальные использования reinterpret_cast законны.
R>Например, ты можешь скастить указатель на объект в int, а потом обратно, и нормально с ним работать дальше.

R>


Польза от такой оптимизации минимальна. Практически во всех случаях изменения типа оптимизатору нельзя отбрасывать это. Поскольку просто так адрес объекта брать никто не будет, пусть даже и используя другой тип указателя.
Ох, чует мое сердце, что это нововведение принесёт больше беды, чем пользы.
Лучшее враг хорошего.
Re[5]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 10:26
Оценка:
А>Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];

Это только в случае выделения памяти в куче или на стеке.
А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Re[5]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 24.08.07 11:03
Оценка:
Здравствуйте, remark, Вы писали:

E>>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно.


R>А разьве выделенное не то же самое?

Нет, как только ты фиксируешь реализацию, так большинство UB становятся вполне детерминированными
Возможно я непонятно выражаюсь. Извини.

E>>Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB

R>Ключевое слово выделено
Ну UB-то разные бывают. Просто стандарт снимает с себя ответсвенность за последствия. Но никто не обязывает реализацию тоже снимать с себя ответсвенность...

E>>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.

R>Здесь тоже
Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Умный вопрос по C/C++
От: Аноним  
Дата: 24.08.07 11:05
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:


А>>Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];


AP>Это только в случае выделения памяти в куче или на стеке.

AP>А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.

Может. Но в таком случае не получится описать такую структуру буфера.
Если буфер описан, например, так:
struct Foo {
  unsigned SomeData;
};

struct DataBuf {
  unsigned char Byte;
  Foo foo;
};

то смещение поля foo зависит от компилятора. Обычно оно будет кратно 4. Заставить разместить foo по смещению 1 не получится. Если все же нужен именно упакованный буфер и переставить местами поля нельзя, то обращаться к невыравненным полям нужно с помощью соответствующих функций или макросов. В таком случае макросы будут разными для разных платформ.
Re[6]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 24.08.07 11:10
Оценка:
AP>А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Просто скопируй...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 24.08.07 11:13
Оценка:
Здравствуйте, Erop, Вы писали:

E>Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...


Показательный пример ты можешь видеть в первом посте этого топика


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 24.08.07 11:28
Оценка:
Здравствуйте, Erop, Вы писали:

AP>>А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.

E>Просто скопируй...

Полностью согласен.
В принципе, про другое говорил — не нужно в телемеханике жертвовать надежностью ради сомнительного выигрыша по скорости.
Вот.
Re[5]: aliasing и aligning
От: McSeem2 США http://www.antigrain.com
Дата: 24.08.07 16:19
Оценка:
Здравствуйте, remark, Вы писали:


R>Не путайте aliasing и aligning!

R>Это обе засады в С++. Но тем не менее — очень разные вещи

Совершенно верно. Путаница возможно была инициирована мной, http://www.rsdn.ru/forum/message/2631085.1.aspx
Автор: McSeem2
Дата: 23.08.07

Вот этой строчкой:
return (int*)(&mem_pool[index_aligned_to_int]);  // To UB or not to UB?


Но эти вещи ортогональны, хотя в данном случае необходимо учитывать и то и другое.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 24.08.07 16:23
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Здесь нет запрещения "простого свопинга байт/слов".

Ш>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода.
Ш>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки.
Ш>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.

Вот ты, как мощный Шахтер и объясни мне, как я могу написать собственный аллокатор, 100% соблюдая правило strict aliasing. Или аллокаторы пишутся тоже из за "патологической глупости помноженной на самоуверенность"?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[6]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 24.08.07 23:33
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Шахтер, Вы писали:


Ш>>
Ш>>    rol    ecx, 16                    ; 00000010H
Ш>>


E>А это точно быстрее обмена слов?


В большинстве современных процессоров самая дорогая операция -- обмен с памятью. Битовые же операции обычно очень дешевые (что не удивительно -- они тривиально реализуютя схемотехнически).
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[5]: Умный вопрос по C/C++
От: Шахтер Интернет  
Дата: 24.08.07 23:37
Оценка:
Здравствуйте, eao197, Вы писали:

E>Здравствуйте, Шахтер, Вы писали:


E>>>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.


Ш>>Здесь нет запрещения "простого свопинга байт/слов".

Ш>>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода.
Ш>>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки.
Ш>>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.

E>Ну если оставить в стороне излишнюю самоуверенность, то такие вещи, как:

E>* буфера ввода-вывода, указатели на которые передаются либо как void*, либо как char*;
E>* средства отладочных дампов блоков памяти, которые воспринимают void*, а затем преобразовывают его к char*.

E>они так же нелегальны?


Что именно нелегально?
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 24.08.07 23:57
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>>>
Ш>>>    rol    ecx, 16                    ; 00000010H
Ш>>>


E>>А это точно быстрее обмена слов?


Ш>В большинстве современных процессоров самая дорогая операция -- обмен с памятью. Битовые же операции обычно очень дешевые (что не удивительно -- они тривиально реализуютя схемотехнически).


С памятью — да. С кэшем -- не факт...
А с другой стороны тут на 16 двигать надо. Если это таки в цикле делается...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Умный вопрос по C/C++
От: Megabyte Россия  
Дата: 26.08.07 15:47
Оценка:
On Thu, 23 Aug 2007 12:49:03 +0600, Smal <54740@users.rsdn.ru> wrote:

> void swap_words3(uint32_t* arg)

> {
> union { uint16_t sp[2]; uint32_t v; } u;
> u.v = *arg;
> uint16_t hi = u.sp[0];
> uint16_t lo = u.sp[1];
>
> u.sp[1] = hi;
> u.sp[0] = lo;
> *arg = u.v;
> }

> Комментарии?


если я не ошибаюсь, запихивать в один член юниона, а вытаскивать из
другого (и наоборот) — тоже неправильно.

--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Posted via RSDN NNTP Server 2.0
Re[4]: Умный вопрос по C/C++
От: Lorenzo_LAMAS  
Дата: 27.08.07 07:24
Оценка:
Здравствуйте, elcste, Вы писали:

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


E>>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C


E>В C++ все ровно то же самое.


E>

5.17/8 If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.


Возможно, я неправильно Вас понял, но мне кажется, что цитата относится к такому:
a = b; где b overlaps a: т.е. "the first object" и "an object" это а. А в оригинальном вопросе, ИМХО, подойдет лучше 3.10/15
Of course, the code must be complete enough to compile and link.
Re: Умный вопрос по C/C++
От: MasterZiv СССР  
Дата: 27.08.07 11:11
Оценка:
McSeem2 пишет:

> void swap_words(uint32_t* arg)

> {
> uint16_t* sp = (uint16_t*)arg;
> uint16_t hi = sp[0];
> uint16_t lo = sp[1];

В последних 3 строках считается, что это — big endien платформа.
Я не знаю, как там это называется с точки зрения стандарта, но
это — просто неправильно.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Умный вопрос по C/C++
От: remark Россия http://www.1024cores.net/
Дата: 04.04.08 12:22
Оценка:
Здравствуйте, Аноним, Вы писали:

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

А>...
MS>>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
А>...
MS>>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.


А>Что я могу сказать, за такие стандарты и компиляторы расстреливать надо. Недостатки аппаратуры выдавать за стандарт это не просто глупость. Куда мы катимся?! И это на обычных скалярных процессорах, а что же на векторных будет. Выравнивали бы по 64байта.

А>И вообще тенденция не здоровая аппаратные проблемы сваливать на программистов, причем в такой извращенной форме. Это ж не вам ассемблер.


Если ты не в курсе, то С/С++ изначально был и назывался как "портабельный ассемблер".
В С++ добавлили некоторые высокоуровневые конструкции, но суть никак не меняли.


А>Такой C++ нам не нужен!



Никто никого и не заставляет...



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 04.04.08 12:35
Оценка:
Здравствуйте, remark, Вы писали:

E>>Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...


R>Показательный пример ты можешь видеть в первом посте этого топика


К char* приводить вроде как законно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От: Аноним  
Дата: 04.04.08 13:24
Оценка:
Здравствуйте, remark, Вы писали:

...
R>Если ты не в курсе, то С/С++ изначально был и назывался как "портабельный ассемблер".
R>В С++ добавлили некоторые высокоуровневые конструкции, но суть никак не меняли.
Ага поэтому его стремятся сделать всё мене портабельным.
Да конешно пусть компилятор умеет обращаться с жуткими абстракциями, но не умеет делать примитивные операции типа борьбы с выравниванием, а взваливает всё с чем было лень бороться разработчикам компилятора на конечных пользователей этого самого компилятора, а ещё лучше включить это в стандарт, чтобы было чем оправдываться. Зачем создавать проблемы на ровном месте? Я вот не понимаю это стремление к сексу стоя в гомоке.

А>>Такой C++ нам не нужен!

R>Никто никого и не заставляет...
Еще как заставляет
Re: Умный вопрос по C/C++
От: Аноним  
Дата: 04.04.08 13:56
Оценка:
Здравствуйте, McSeem2, Вы писали:

...

Хорошо. Но вот правильно ли я понимаю, что такой код тоже не всегда будет работать?

typedef struct
{
   int type;
} BaseObject;

typedef struct
{
   int type;

   int foo;
} DerivedObject;

enum
{
  OBJTYPE_UNDEFINED,
  OBJTYPE_DERIVED,
  /* ... */
};

...

void incFoo( BaseObject * d )
{
   if( d->type == OBJTYPE_DERIVED )
   {
       DerivedObject * alias = (DerivedObject *)d;
       ++ alias->foo;
   }
}

...
/* somewhere in code */
BaseObject b;

b.type = OBJTYPE_UNDEFINED;
incFoo( &b ); /* crash? */

/* ... */
DerivedObject d;

d.type = OBJTYPE_DERIVED;
d.foo = 0;
incFoo( (BaseObject *)&d ); /* crash? */


Если да, то мать-перемать, что ж я натворил
Re[2]: Умный вопрос по C/C++
От: gear nuke  
Дата: 04.04.08 14:07
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Недостатки аппаратуры выдавать за стандарт это не просто глупость.


Например, если так делать для double, даже на x86 можно получить исключение.
Требование обосновано Representations of types

§6.2.6.1/5 Certain object representations need not represent a value of the object type. If the stored
value of an object has such a representation and is read by an lvalue expression that does
not have character type, the behavior is undefined. If such a representation is produced
by a side effect that modifies all or any part of the object by an lvalue expression that
does not have character type, the behavior is undefined.41) Such a representation is called
a trap representation.

People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[2]: Умный вопрос по C/C++
От: skeptik_  
Дата: 04.04.08 14:10
Оценка:
Здравствуйте, Андрей Коростелев, Вы писали:

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).


Пардон, а что тут нового? Точно такой же текст стоит в С90 6.3. Если речь об этом, конечно:

7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.

Re[2]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 04.04.08 14:36
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Если да, то мать-перемать, что ж я натворил

Пока твои структуры останутся POD гарантируется, что этот код будет работать правильно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От: Аноним  
Дата: 04.04.08 19:11
Оценка:
Здравствуйте, gear nuke, Вы писали:

GN>Здравствуйте, <Аноним>, Вы писали:


А>>Недостатки аппаратуры выдавать за стандарт это не просто глупость.


GN>Например, если так делать для double, даже на x86 можно получить исключение.

??? правда
volatile double x=1.0;
char* p=(char*)&x;

Теперь по новым стандартам это бред? И я даже не смогу проверить как оно лежит в памяти?

GN>Требование обосновано Representations of types


GN>

§6.2.6.1/5 Certain object representations need not represent a value of the object type. If the stored
GN>value of an object has such a representation and is read by an lvalue expression that does
GN>not have character type, the behavior is undefined. If such a representation is produced
GN>by a side effect that modifies all or any part of the object by an lvalue expression that
GN>does not have character type, the behavior is undefined.41) Such a representation is called
GN>a trap representation.

Пусть они себе такое обоснование засунут куда-нибуть.
Нет чтоб определить "representations of Certain object", так они делают детские грабли, с топором на конце.
И пусть они свои фантазии называют как угодно. Я определил в программе как оно лежит в памяти.
И меня не чешут их филосовские изыскания что на какой то другой машине это будет представлено иначе, мне это нужно для конкретной.
Может я просто хочу выяснить как оно в памяти лежит, а меня тут наглым образом начинает разводить компилятор. Повбивалбы.

Радует только одно что такие стандарты нарушают совместимость с предыдущими версиями. И старый код скорее всего может не работать. Что в свою очередь приведет к тому что на такой стандарт пользователи положут болт.
Re[4]: Умный вопрос по C/C++
От: Roman Odaisky Украина  
Дата: 04.04.08 21:19
Оценка:
Здравствуйте, Аноним, Вы писали:

GN>>Например, если так делать для double, даже на x86 можно получить исключение.

А>??? правда
А>
А>volatile double x=1.0;
А>char* p=(char*)&x;
А>

А>Теперь по новым стандартам это бред? :no: И я даже не смогу проверить как оно лежит в памяти? :???:

Это будет работать (можно кастить к char * и void *). Не обязано работать (double *)someRandomCharArray.

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

Да и стандарт 1998 г. в. как-то не очень похож на новый.
До последнего не верил в пирамиду Лебедева.
Re[5]: Умный вопрос по C/C++
От: Аноним  
Дата: 04.04.08 21:38
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Здравствуйте, Аноним, Вы писали:


GN>>>Например, если так делать для double, даже на x86 можно получить исключение.

А>>??? правда
А>>
А>>volatile double x=1.0;
А>>char* p=(char*)&x;
А>>

А>>Теперь по новым стандартам это бред? И я даже не смогу проверить как оно лежит в памяти?

RO>Это будет работать (можно кастить к char * и void *). Не обязано работать (double *)someRandomCharArray.

RO>А лезть в байты тебе ни к чему, разные платформы будут иметь разные представления double.
И как я по вашему тогда вынужден буду делать эмуляцию чисел с плавающей точкой. Если компилятор мне строит препятствия на ровном месте?
И потом для разных платформ и api разное и abi да и компиляторы у одних 48бит аккумулятор, у других операции векторные (сразу пачками обрабатывает), одни RISC другие CISC, одни DSP а другие только целочисленно умножать умеют. Даже POSIX у всех свой. Короче у меня есть разная реализация под разные платформы и неипёт.
Я хочу сказать что такие синтетические ограничения это полный маразм. Мне пофиг как оно устроено на разных платформах которые я не использую.
Мне важен факт что один и тот же компилятор на одной и той же платформе обманывает и генерит ерись. Причем проблема совершенно надуманная и искуственная!
Если arm работает со словами а не с байтами. Какого я должен над этим суетиться, а не компилятор? Если процессор не умеет что то делать, то почему я что дожен это делать за компилятор?
Задача компилятора облегчить написание программ, а тут всё наоборот. (И мы еще боримся за звание дома высокой культуры быта)

RO>Да и стандарт 1998 г. в. как-то не очень похож на новый.

А там то что не так?
Re[4]: Умный вопрос по C/C++
От: gear nuke  
Дата: 05.04.08 00:49
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>
А>volatile double x=1.0;
А>char* p=(char*)&x;
А>

А>Теперь по новым стандартам это бред? И я даже не смогу проверить как оно лежит в памяти?

Так можно, я же привёл цитату

GN>>

If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined.


Проблемы могут быть, если последовательность char, представляющую собой репрезентацию NaN, прочитать в duoble. В место "неожданного" результата, как в оригинальном примере, будет сгенереировано исключение (хотя по умолчанию оно замаскировано) — это на самой распростанённой платформе, а с другими может быть еще хуже.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[5]: Умный вопрос по C/C++
От: Аноним  
Дата: 05.04.08 08:25
Оценка:
Здравствуйте, gear nuke, Вы писали:

GN>Проблемы могут быть, если последовательность char, представляющую собой репрезентацию NaN, прочитать в double. В место "неожданного" результата, как в оригинальном примере, будет сгенереировано исключение (хотя по умолчанию оно замаскировано) — это на самой распростанённой платформе, а с другими может быть еще хуже.

И что NaN этот тоже последовательность бит в памяти. В чем проблема то? Где будет хуже? Что нафиг за цензура еще такая?
Re[6]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 06.04.08 13:08
Оценка:
Здравствуйте, Аноним, Вы писали:

А>В чем проблема то?

В системе команд CPU
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Умный вопрос по C/C++
От: Аноним  
Дата: 06.04.08 20:55
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Аноним, Вы писали:


А>>В чем проблема то?

E>В системе команд CPU
Система команд CPU. Как раз проблема разработчиков компилятора, а не того кто пишет на C/C++
Re[8]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 06.04.08 23:16
Оценка:
Здравствуйте, Аноним, Вы писали:

E>>В системе команд CPU

А>Система команд CPU. Как раз проблема разработчиков компилятора, а не того кто пишет на C/C++
Ну фокус в том, что то, что нельзя сделать на асме, то и на С++ нельзя тем более. Так что у авторов компиляторов нет проблем. Проблемы есть у проектировщиков ПО, которые хотят сделать то, что аппаратура не очень умеет и не особо должна к тому же

На всякий случай. К char* приводить можно любой указатель и потом по байтам читать память. Это для тех, кто эмуляторы писать хочет очень даже удобно. А для тех, кто хочет то эмуляторы, а то FPU попользоваться ждёт сюрприз в виде того, что даже и не FPU данные иногда в регистры поднимаются, например. И не все состяния процессора и регистров отображаются в биты в памяти при этом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Умный вопрос по C/C++
От: Erop Россия  
Дата: 07.04.08 10:58
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Категорически не согласен, если это не специализированный чип, то на асме можно сделать всё что угодно. И эффективность реализации тут совершенно не важна.

Да? Ну вычисли точно число пи, например...

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

Проектировщики ПО? Да хотят... Вот ты, например, хотешь, вроде бы того, чтобы авторы компиляторов эмулировали какое-то нужное тебе поведение...

А>Вот очень странно если у меня N чисел (сотни и миллионы) с плавающей точкой, то как процессор может их держать у себя в регистрах? Если регистров у него значительно меньше. Мне совершенно не важна внутренняя разрядность и архитектура математического сопроцессора. Но когда он числа сохраняет в память он это делает вполне детерминированно.

Даже числа не всегда, но у процессора, кроме чисел, есть ещё и состояние. Состояния он сохранять в память вообще-то не обязан... Скажем такое вот состояние "результат последней операции неотрицателен". Как ты собираешься в формате числа прихранивать?

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


Ну некоторым людям (скажу по секрету, их большинстово) нужна быстрая и эффективная работа приложений, а не эмуляция поведения одного железа на другом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.