Эх, не хватает в C++ макроассемблерных макросов :)
От: McSeem2 США http://www.antigrain.com
Дата: 21.09.04 18:45
Оценка:
Задача:

template<class T> unsigned read_value(const char* ptr, T* ret_val)
{
   for(unsigned i = 0; i < sizeof(T); ++i) (*(char*)T)[i] = *ptr++;
   return sizeof(T);
}


Это работает, но нет никакой гарантии, что оптимтзатор развернет цикл. А если не развернет, то это — 30-50% потери производительности. А хотелось бы с гарантией, чтобы статически, как в MACRO-11, типа:

template<class T> unsigned read_value(const char* ptr, T* ret_val)
{
   $repeat a(sizeof(T))
   {
      (*(char*)T)[a.index] = *ptr++;
   }
   return sizeof(T);
}


Не планирует ли комитет поработать над чем-то типа макрокоманд, встроенных в сам язык, а не в препроцессор?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Эх, не хватает в C++ макроассемблерных макросов :)
От: Владик Россия  
Дата: 21.09.04 19:07
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Задача:


MS>
MS>template<class T> unsigned read_value(const char* ptr, T* ret_val)
MS>{
MS>   for(unsigned i = 0; i < sizeof(T); ++i) (*(char*)T)[i] = *ptr++;
MS>   return sizeof(T);
MS>}
MS>


Как насчет такого варианта?

template<class T> unsigned read_value(const char* ptr, T* ret_val)
{
   *ret_val = *reinterpret_cast<T*>(ptr);
}
Как все запущенно...
Re: Эх, не хватает в C++ макроассемблерных макросов :)
От: korzhik Россия  
Дата: 21.09.04 19:09
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Задача:


MS>
MS>template<class T> unsigned read_value(const char* ptr, T* ret_val)
MS>{
MS>   for(unsigned i = 0; i < sizeof(T); ++i) (*(char*)T)[i] = *ptr++;
MS>   return sizeof(T);
MS>}
MS>


MS>Это работает, но нет никакой гарантии, что оптимтзатор развернет цикл. А если не развернет, то это — 30-50% потери производительности. А хотелось бы с гарантией, чтобы статически, как в MACRO-11, типа:


ну а если применить Duff's Device?
в твоём случае это будет выглядеть примерно так:
(код не компилил)
template<typename T, size_t N>
inline 
unsigned read_value(char const * ptr, T(&ret_val)[N])
{
    char* to = (char*)ret_val;
    unsigned n = (N+7)/8;
    
    switch (N%8)
    {
    case 0:do{*to = *ptr++; to++;
    case 7:*to = *ptr++; to++;
    case 6:*to = *ptr++; to++;
    case 5:*to = *ptr++; to++;
    case 4:*to = *ptr++; to++;
    case 3:*to = *ptr++; to++;
    case 2:*to = *ptr++; to++;
    case 1:*to = *ptr++; to++;
           } while(--n>0);
    }
    
    return N;
}


вот здесь историческая справка про Duff's Device
вот здесь Александреску использует его.
Re[2]: Эх, не хватает в C++ макроассемблерных макросов :)
От: korzhik Россия  
Дата: 21.09.04 19:20
Оценка:
Здравствуйте, korzhik, Вы писали:

K>в твоём случае это будет выглядеть примерно так:

K>(код не компилил)
вернее так, наверно:
template<typename T>
inline 
unsigned read_value(char const * ptr, T* ret_val)
{
  char*    to    = (char*)ret_val;
  unsigned count = sizeof(T);
  unsigned n     = (count+7)/8;

  switch (count % 8)
  {
    case 0:do{*to = *ptr++; ++to;
    case 7:*to = *ptr++; ++to;
    case 6:*to = *ptr++; ++to;
    case 5:*to = *ptr++; ++to;
    case 4:*to = *ptr++; ++to;
    case 3:*to = *ptr++; ++to;
    case 2:*to = *ptr++; ++to;
    case 1:*to = *ptr++; ++to;
           } while(--n>0);
  }
  return count;
}
Re: Эх, не хватает в C++ макроассемблерных макросов :)
От: korzhik Россия  
Дата: 21.09.04 19:25
Оценка: +1
Здравствуйте, McSeem2, Вы писали:

MS>Задача:


MS>
MS>template<class T> unsigned read_value(const char* ptr, T* ret_val)
MS>{
MS>   for(unsigned i = 0; i < sizeof(T); ++i) (*(char*)T)[i] = *ptr++;
MS>   return sizeof(T);
MS>}
MS>


а почему бы не использовать memcpy?
Re[2]: Эх, не хватает в C++ макроассемблерных макросов :)
От: _Winnie Россия C++.freerun
Дата: 21.09.04 22:05
Оценка: 1 (1)
Здравствуйте, korzhik, Вы писали:

k>ну а если применить Duff's Device?


duffdevice — мертв. Его использование — это навешивание гирь на ноги оптимизатору.
Циклы for компилятор развернет сам. Поставив правильные #pragma Intel C++ Compiler еще и равернет его в SSE код.

Кстати, конкретно о развертке —
#pragma loop count (n)
#pragma unroll(n)
Правильно работающая программа — просто частный случай Undefined Behavior
Re[2]: Эх, не хватает в C++ макроассемблерных макросов :)
От: McSeem2 США http://www.antigrain.com
Дата: 21.09.04 23:01
Оценка:
Здравствуйте, Владик, Вы писали:

В>
В>template<class T> unsigned read_value(const char* ptr, T* ret_val)
В>{
В>   *ret_val = *reinterpret_cast<T*>(ptr);
В>}
В>


Не сработает. ptr имеет право быть невыровненным на границу sizeof(T). Любой RISC от этого сразу вылетит по автобусной ошибке.
Или reinterpret_cast такой умный, что скопирует побайтно, типа memcpy? Что-то не внушает доверия.

Кроме того, вместо ptr может быть итератор. В этом случае вообще не гарантировано, что байты расположениы в памяти подряд (std::deque, std::list).
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[3]: Эх, не хватает в C++ макроассемблерных макросов :)
От: SpringMute  
Дата: 21.09.04 23:07
Оценка:
Здравствуйте, korzhik, Вы писали:

K>
K>template<typename T>
K>inline 
K>unsigned read_value(char const * ptr, T* ret_val)
K>{
K>  char*    to    = (char*)ret_val;
K>  unsigned count = sizeof(T);
K>  unsigned n     = (count+7)/8;

K>  switch (count % 8)
K>  {
K>    case 0:do{*to = *ptr++; ++to;
K>    case 7:*to = *ptr++; ++to;
K>    case 6:*to = *ptr++; ++to;
K>    case 5:*to = *ptr++; ++to;
K>    case 4:*to = *ptr++; ++to;
K>    case 3:*to = *ptr++; ++to;
K>    case 2:*to = *ptr++; ++to;
K>    case 1:*to = *ptr++; ++to;
K>           } while(--n>0);
K>  }
K>  return count;
K>}
K>


А можно в кратце, что этот код делает?
Как-то решал тестовое задание, та был такой вопрос, я немного поофигевал и рпопустил.
... << RSDN@Home 1.1.4 @@subversion >>
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
(Пишу из ссылки)
Re[4]: Эх, не хватает в C++ макроассемблерных макросов :)
От: SpringMute  
Дата: 21.09.04 23:23
Оценка:
SM>А можно в кратце, что этот код делает?
SM>Как-то решал тестовое задание, та был такой вопрос, я немного поофигевал и рпопустил.

Вопрос снят, разобрался.
... << RSDN@Home 1.1.4 @@subversion >>
Думай, что хочешь, делай, что хочешь, живи, как хочешь -- всё тебе припомню!!
(Пишу из ссылки)
Re: Эх, не хватает в C++ макроассемблерных макросов :)
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 22.09.04 02:47
Оценка:
Здравствуйте, McSeem2, Вы писали:

M>...


Можно так:
// Маразм из дефайнов:

#define CASE1(N, O) case N: { const int i = N; O; }
#define CASE2(N, O) CASE1(N, O) CASE1(N-1, O)
#define CASE4(N, O) CASE2(N, O) CASE2(N-2, O)
#define CASE8(N, O) CASE4(N, O) CASE4(N-4, O)
#define CASE16(N, O) CASE8(N, O) CASE8(N-8, O)
#define CASE32(N, O) CASE16(N, O) CASE16(N-16, O)
#define CASE64(N, O) CASE32(N, O) CASE32(N-32, O)
#define CASE128(N, O) CASE64(N, O) CASE64(N-64, O)
#define CASE256(N, O) CASE128(N, O) CASE128(N-128, O)
#define CASE512(N, O) CASE256(N, O) CASE256(N-256, O)
#define CASE1024(N, O) CASE512(N, O) CASE512(N-512, O)
#define CASE2048(N, O) CASE1024(N, O) CASE1024(N-1024, O)

// Как бы цикл, делает N раз операцию O.
// N вычисляеться один раз.
// Максимальное значение N равно 2048,
// если нужно, можно увеличить добавив еще дефайнов CASE*.
#define LOOP(N, O) switch(N) { CASE2048(2048, O) }

template<class T> unsigned read_value(const char* ptr, T* ret_val)
{
    LOOP(sizeof(T), (*(char*)T)[i] = *ptr++)
    return sizeof(T);
}


Сам не пользуюсь этим, задач таких нет — что бы оптимизировать так жутко, но вроде все работает.

PS. Смысл read_value не важен, только хотел показать идею развернутого цикла с большим количеством итераций ~ 1024.
getboost.codeplex.com
citylizard.codeplex.com
Re[3]: Эх, не хватает в C++ макроассемблерных макросов :)
От: LaptevVV Россия  
Дата: 22.09.04 06:10
Оценка:
Здравствуйте, McSeem2, Вы писали:

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


В>>
В>>template<class T> unsigned read_value(const char* ptr, T* ret_val)
В>>{
В>>   *ret_val = *reinterpret_cast<T*>(ptr);
В>>}
В>>


MS>Не сработает. ptr имеет право быть невыровненным на границу sizeof(T). Любой RISC от этого сразу вылетит по автобусной ошибке.

MS>Или reinterpret_cast такой умный, что скопирует побайтно, типа memcpy? Что-то не внушает доверия.
Ничего он не скопирует, как раз он — "глупый", фактического преобразования как раз не выполняется.
И Страуструп писал, что это практически ВСЕГДА не переносимо.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Эх, не хватает в C++ макроассемблерных макросов :)
От: korzhik Россия  
Дата: 22.09.04 06:36
Оценка:
Здравствуйте, _Winnie, Вы писали:

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


k>>ну а если применить Duff's Device?


_W>duffdevice — мертв. Его использование — это навешивание гирь на ноги оптимизатору.

_W>Циклы for компилятор развернет сам. Поставив правильные #pragma Intel C++ Compiler еще и равернет его в SSE код.

Насчёт мёртвости Duff's device ничего конкретного сказать не могу, это сложный вопрос, надо исследовать.
вот здесь Александреску как раз сравнивает простой цикл и Duff's device.

Я думаю всё зависит какая задача стоит:
если стоит задача оптимизировать код под конкретную платформу и компилятор, то конечно Duff's device использовать не надо.

Но если стоит задача оптимизировать кроссплатформенный код, то Duff's device может пригодиться.

2McSeem2:
Я думаю лучший вариант будет такой:
вам надо сделать частичную специализацию для POD и в ней использовать memcpy,
а для не POD использовать Duff's device.
Re[3]: Эх, не хватает в C++ макроассемблерных макросов :)
От: Владик Россия  
Дата: 22.09.04 10:11
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Не сработает. ptr имеет право быть невыровненным на границу sizeof(T).


Как он может быть невыровненным, если судя по коду там лежит T?

MS>Кроме того, вместо ptr может быть итератор. В этом случае вообще не гарантировано, что байты расположениы в памяти подряд (std::deque, std::list).


Если там будет итератор, то будет другое решение
Как все запущенно...
Re[4]: Эх, не хватает в C++ макроассемблерных макросов :)
От: McSeem2 США http://www.antigrain.com
Дата: 22.09.04 15:44
Оценка:
Здравствуйте, Владик, Вы писали:

MS>>Не сработает. ptr имеет право быть невыровненным на границу sizeof(T).


В>Как он может быть невыровненным, если судя по коду там лежит T?


В ptr лежат байты. Сначала там bool, потом int, потом — double. И все плотно упаковано.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: Эх, не хватает в C++ макроассемблерных макросов :)
От: McSeem2 США http://www.antigrain.com
Дата: 22.09.04 16:03
Оценка:
Здравствуйте, sergey_shandar, Вы писали:

_>PS. Смысл read_value не важен, только хотел показать идею развернутого цикла с большим количеством итераций ~ 1024.


Тоже не фонтан, увы. Этот switch, IMO, еще сложнее поддается оптимизации, чем простой for, с константой
i < sizeof(T).
Что там компилятор сделает для switch — бинарный поиск? Для копирования одного инта — очень накладно. А сумеет ли компилятор определить, что можно применить приямой вычисляемый jmp — это еще большой вопрос. Вот если бы был еще вычисляемый goto в явном виде, как в Фортране или PL/I...

Вообще, странно, что Керниган с Ричи, как поклонники PDP и MACRO-11 не встроили изначально в препроцессор мощных макро-средств, по типу макроассемблерных. Так же, непонятно, почему не сделали вычисляемого goto, вместо чего приходится использовать вульгарную duff's машину, которая на самом деле ничего не гарантирует, тем более — на тогдашнем уровне возможностей оптимизации компиляторов. Да и вообще, изначальный PDP-шный компилятор с C был на редкость убогим и неэффективным по сравнению со стандарным Фортраном...
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Эх, не хватает в C++ макроассемблерных макросов :)
От: Kluev  
Дата: 23.09.04 08:44
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>Задача:


MS>
MS>template<class T> unsigned read_value(const char* ptr, T* ret_val)
MS>{
MS>   for(unsigned i = 0; i < sizeof(T); ++i) (*(char*)T)[i] = *ptr++;
MS>   return sizeof(T);
MS>}
MS>


можно облегчить жисть компилеру:
template <int n>
inline void byte_copy( const char *src, char *dst )
{
    dst[n-1] = src[n-1];
    byte_copy<n-1>(src,dst);
}

template <>
inline void byte_copy<0>( const char*, char* )
{
}

template <class T>
void str_read( const char *str, T &val )
{
    byte_copy<sizeof(T)>( str, (char*)&val );
}


Правда тоже нет гарантии что все будет развернуто
Re[2]: Эх, не хватает в C++ макроассемблерных макросов :)
От: Kluev  
Дата: 23.09.04 08:50
Оценка:
Здравствуйте, Kluev, Вы писали:

K>можно облегчить жисть компилеру:


Хотя по дефолту можно заюзать мемкопи, и сделать специализацию для остального


template <int n>
inline void byte_copy( char *dst, const char *src )
{
    memcpy( dst, src, n );
}

template <>
inline void byte_copy<1>( char *dst, const char *src )
{
    *dst = *src;
}

template <>
inline void byte_copy<2>( char *dst, const char *src )
{
    dst[0] = src[0]; dst[1] = src[1];
}

template <>
inline void byte_copy<4>( char *dst, const char *src )
{
    dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
}

template <class T>
void str_read( const char *str, T &val )
{
    byte_copy<sizeof(T)>( (char*)&val, str );
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.