Re: Быстрейший способ конвертации int в hex-строку
От: MaximE Великобритания  
Дата: 29.09.06 18:46
Оценка: 4 (1) +1
Tuo_Bellas wrote:

> GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего

> времени выполнения программы:
>
> static const char * const s_hexChars[256] =
> {
> "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
> /* ... */
> "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
> };
>
> bool putLong(char *buffer, int value)
> {
> if (buffer)
> {
> buffer[0] = s_hexChars[(value & 0xFF000000) >> 24][0];
> buffer[1] = s_hexChars[(value & 0xFF000000) >> 24][1];
> buffer[2] = s_hexChars[(value & 0xFF0000) >> 16][0];
> buffer[3] = s_hexChars[(value & 0xFF0000) >> 16][1];
> buffer[4] = s_hexChars[(value & 0xFF00) >> 8][0];
> buffer[5] = s_hexChars[(value & 0xFF00) >> 8][1];
> buffer[6] = s_hexChars[(value & 0xFF)][0];
> buffer[7] = s_hexChars[(value & 0xFF)][1];
> }
>
> return !!buffer;
> }

На скорость может влиять в худшую сторону то, что ты тут доступаешься к массиву.
Обычно L1 кэш линейки все заняты, придется из кэша выкидывать какие-то другие
данные. Т.е. лишнего доступа к памяти желательно избежать если важна скорость.

> Возможна ли еще более быстрая реализация? О сокращении числа вызовов

> (10М+) — думаем.

Может что-то похожее на http://rsdn.ru/Forum/?mid=1514879
Автор: MaximE
Дата: 30.11.05
?

--
Maxim Yegorushkin

No Microsoft product was used in any way to write or send this text.
If you use a Microsoft product to read it, you're doing so at your own risk
Posted via RSDN NNTP Server 2.0
Быстрейший способ конвертации int в hex-строку
От: Tuo_Bellas Россия  
Дата: 29.09.06 16:14
Оценка: :))
Всем привет!

GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего времени выполнения программы:

static const char * const s_hexChars[256] =
{
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
/* ... */
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
};

bool putLong(char *buffer, int value)
{
  if (buffer)
  {
    buffer[0] = s_hexChars[(value & 0xFF000000) >> 24][0];
    buffer[1] = s_hexChars[(value & 0xFF000000) >> 24][1];
    buffer[2] = s_hexChars[(value & 0xFF0000) >> 16][0];
    buffer[3] = s_hexChars[(value & 0xFF0000) >> 16][1];
    buffer[4] = s_hexChars[(value & 0xFF00) >> 8][0];
    buffer[5] = s_hexChars[(value & 0xFF00) >> 8][1];
    buffer[6] = s_hexChars[(value & 0xFF)][0];
    buffer[7] = s_hexChars[(value & 0xFF)][1];
  }

  return !!buffer;
}


Возможна ли еще более быстрая реализация? О сокращении числа вызовов (10М+) — думаем.

Спасибо,
Tuo_Bellas.
Re[3]: Быстрейший способ конвертации int в hex-строку
От: korzhik Россия  
Дата: 30.09.06 12:35
Оценка: :))
Здравствуйте, korzhik, Вы писали:

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


ME>>Может что-то похожее на http://rsdn.ru/Forum/?mid=1514879
Автор: MaximE
Дата: 30.11.05
?


K>Если следовать идеям, который указаны по ссылке, то получаем такой код:


что то я стормозил
оказывается по ссылке уже был нужный код, просто я посмотрел на функцию hex2bin,
а bin2hex чего то не заметил
Ну в общем утро субботы, так что это простительно
Re[7]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 02.10.06 13:46
Оценка: +1 :)
Угу а автор пропал
Re: Быстрейший способ конвертации int в hex-строку
От: MaximE Великобритания  
Дата: 01.10.06 21:04
Оценка: 6 (1)
Tuo_Bellas wrote:

> GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего

> времени выполнения программы:
>
> static const char * const s_hexChars[256] =
> {
> "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
> /* ... */
> "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
> };
>
> bool putLong(char *buffer, int value)
> {
> if (buffer)
> {
> buffer[0] = s_hexChars[(value & 0xFF000000) >> 24][0];
> buffer[1] = s_hexChars[(value & 0xFF000000) >> 24][1];
> buffer[2] = s_hexChars[(value & 0xFF0000) >> 16][0];
> buffer[3] = s_hexChars[(value & 0xFF0000) >> 16][1];
> buffer[4] = s_hexChars[(value & 0xFF00) >> 8][0];
> buffer[5] = s_hexChars[(value & 0xFF00) >> 8][1];
> buffer[6] = s_hexChars[(value & 0xFF)][0];
> buffer[7] = s_hexChars[(value & 0xFF)][1];
> }
>
> return !!buffer;
> }
>
>
>
> Возможна ли еще более быстрая реализация? О сокращении числа вызовов
> (10М+) — думаем.

Решил погонять код на досуге, оказалось, что это самый быстрый из основных
способов , предложенных в ветке (ассемблер не пробовал, т.к. у меня нет msvc):

a) оригинальный метод, с таблицей на байт.
b) метод с таблицей на пол-байта.
c) метод без таблицы, с тернаным оператором (?: — потенциальное ветвление).
d) c) без ветвления.

Места распределились так: adbc.

Детали и код:

[max@k-pax test]$ awk '/name/{print $0}' /proc/cpuinfo
model name    : Intel(R) Pentium(R) M processor 1.86GHz
[max@k-pax test]$ g++ --version
g++ (GCC) 4.1.1 20060525 (Red Hat 4.1.1-1)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[max@k-pax test]$ make NDEBUG=1
g++ -c -march=pentium-m -O3 -fomit-frame-pointer -funroll-loops -o obj/test.o
g++ ./obj/test.o -march=pentium-m  -o bin/test
[max@k-pax test]$ bin/test; bin/test; bin/test
A: 1009 usec
B: 11256 usec
C: 237426 usec
D: 6644 usec
A: 1009 usec
B: 11325 usec
C: 395163 usec
D: 6187 usec
A: 1107 usec
B: 11154 usec
C: 239838 usec
D: 6209 usec


$ awk '/name/{print $0}' /proc/cpuinfo
model name    : Dual Core AMD Opteron(tm) Processor 275
model name    : Dual Core AMD Opteron(tm) Processor 275
model name    : Dual Core AMD Opteron(tm) Processor 275
model name    : Dual Core AMD Opteron(tm) Processor 275
$ make NDEBUG=1
g++ -c -march=opteron -O3 -fomit-frame-pointer -funroll-loops -o obj/test.o test.cc
g++ ./obj/test.o -march=opteron -o bin/test
$ bin/test; bin/test; bin/test
A: 1149 usec
B: 5707 usec
C: 72972 usec
D: 4703 usec
A: 1143 usec
B: 5702 usec
C: 72971 usec
D: 4805 usec
A: 1143 usec
B: 5701 usec
C: 68453 usec
D: 4931 usec


#include <stdio.h>
#include <limits>
#include "stopwatch.h"

typedef unsigned value_type;

struct A
{
     static char bytes[256][2];
     static bool init_bytes;

     A()
     {
         if(!init_bytes)
         {
             init_bytes = true;
             char(*p)[2] = bytes;
             for(unsigned i = 0; i != 0x100; ++i, ++p)
             {
                 static char const hex[] = "0123456789abcdef";
                 0[*p] = hex[i >> 4];
                 1[*p] = hex[i & 0x0f];
             }
         }
     }

     void operator()(value_type n, char* p)
     {
         p[0] = bytes[(n & 0xff000000) >> 24][0];
         p[1] = bytes[(n & 0xff000000) >> 24][1];
         p[2] = bytes[(n & 0xff0000) >> 16][0];
         p[3] = bytes[(n & 0xff0000) >> 16][1];
         p[4] = bytes[(n & 0xff00) >> 8][0];
         p[5] = bytes[(n & 0xff00) >> 8][1];
         p[6] = bytes[(n & 0xff)][0];
         p[7] = bytes[(n & 0xff)][1];
     }
};

char A::bytes[256][2];
bool A::init_bytes;

struct B
{
     void operator()(value_type n, char* p)
     {
         unsigned nibbles = std::numeric_limits<value_type>::digits / 4;
         p += nibbles;
         for(unsigned i = nibbles; i--;)
         {
             static char const hex[] = "0123456789abcdef";
             *--p = hex[n & 0x0f];
             n >>= 4;
         }
     }
};

struct C
{
     void operator()(value_type n, char* p)
     {
         unsigned nibbles = std::numeric_limits<value_type>::digits / 4;
         p += nibbles;
         for(unsigned i = 0; i != nibbles; ++i)
         {
             unsigned char t = n & 0x0f;
             n >>= 4;
             *--p = t + (t < 10 ? '0' : 'a' - 10);
         }
     }
};

struct D
{
     void operator()(value_type n, char* p)
     {
         unsigned nibbles = std::numeric_limits<value_type>::digits / 4;
         p += nibbles;
         for(unsigned i = nibbles; i--;)
         {
             unsigned char t = n & 0x0f;
             n >>= 4;
             *--p = '0' + t + (-(t > 9) & ('a' - '0' - 10));
         }
     }
};

template<class F>
void test_loop(F f, value_type n, char* buf, unsigned iter)
{
     while(iter--)
         f(n, buf);
}

typedef util::stopwatch::usec_t usec_t;

template<class F>
usec_t test(F f, value_type n)
{
     char buf[std::numeric_limits<value_type>::digits / 4 + 1] = {};
     test_loop(f, n, buf, 100); // warm up
     util::stopwatch s;
     test_loop(f, n, buf, 10000000);
     usec_t t = s.elapsed();
     printf("%s\n", buf);
     return t;
}

int main(int, char** av)
{
     value_type no = *reinterpret_cast<value_type*>(&av);

     A a;
     usec_t ta = test(a, no);
     B b;
     usec_t tb = test(b, no);
     C c;
     usec_t tc = test(c, no);
     D d;
     usec_t td = test(d, no);

     printf("A: %llu usec\n", ta);
     printf("B: %llu usec\n", tb);
     printf("C: %llu usec\n", tc);
     printf("D: %llu usec\n", td);
}


--
Maxim Yegorushkin

No Microsoft product was used in any way to write or send this text.
If you use a Microsoft product to read it, you're doing so at your own risk
Posted via RSDN NNTP Server 2.0
Re: Быстрейший способ конвертации int в hex-строку
От: Roman Odaisky Украина  
Дата: 29.09.06 17:15
Оценка: 4 (1)
Здравствуйте, Tuo_Bellas, Вы писали:

T_B>GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего времени выполнения программы:


T_B>
T_B>static const char * const s_hexChars[256] =
T_B>{
T_B>"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
T_B>/* ... */
T_B>"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
T_B>};

T_B>bool putLong(char *buffer, int value)
T_B>{
T_B>  if (buffer)
T_B>  {
T_B>    buffer[0] = s_hexChars[(value & 0xFF000000) >> 24][0];
T_B>    buffer[1] = s_hexChars[(value & 0xFF000000) >> 24][1];
T_B>    buffer[2] = s_hexChars[(value & 0xFF0000) >> 16][0];
T_B>    buffer[3] = s_hexChars[(value & 0xFF0000) >> 16][1];
T_B>    buffer[4] = s_hexChars[(value & 0xFF00) >> 8][0];
T_B>    buffer[5] = s_hexChars[(value & 0xFF00) >> 8][1];
T_B>    buffer[6] = s_hexChars[(value & 0xFF)][0];
T_B>    buffer[7] = s_hexChars[(value & 0xFF)][1];
T_B>  }

T_B>  return !!buffer;
T_B>}
T_B>


T_B>Возможна ли еще более быстрая реализация? О сокращении числа вызовов (10М+) — думаем.


Может, примерно так? Постараться выровнять все используемые данные?
void u32ToHex(U32 value, char buffer[8])
{
    static U32 const hexBytes[256] = { // 32 -- для выравнивания, используются только 16 бит
        . . .
        ('A' << 8) + '9',
        ('A' << 8) + 'A',
        ('A' << 8) + 'B',
        . . .
    };

    U32 hi = (hexBytes[. . .] << 16) ^ hexBytes[. . .]; // для него же (ведь 4 байта?)
    U32 lo = (hexBytes[. . .] << 16) ^ hexBytes[. . .];
    reinterpret_cast<U32 &>(buffer[0]) = hi; // если buffer -- массив, полученный new [], то по стандарту он выровнен так,
    reinterpret_cast<U32 &>(buffer[4]) = lo; // чтобы подходить под любой тип -- и 32-байтный unsigned в том числе
}

P. S. Проверка представляется явно лишней, параметр value так и просится unsigned.
До последнего не верил в пирамиду Лебедева.
Re[2]: Быстрейший способ конвертации int в hex-строку
От: korzhik Россия  
Дата: 30.09.06 12:06
Оценка: 4 (1)
Здравствуйте, MaximE, Вы писали:

ME>Может что-то похожее на http://rsdn.ru/Forum/?mid=1514879
Автор: MaximE
Дата: 30.11.05
?


Если следовать идеям, который указаны по ссылке, то получаем такой код:
//-----------------------------------------------------------------------------
typedef unsigned char int8u;
typedef unsigned int  int32u;
//-----------------------------------------------------------------------------
inline void byte2hex(int8u v, char* l, char* h)
{
    int8u l_part = v & 0x0F;
    int8u h_part = v & 0xF0;
    
    h_part >>= 4;

    h_part += -(h_part > 9) & ('A' - '9' - 1); // for lower case use 'a'-'9'-1
    l_part += -(l_part > 9) & ('A' - '9' - 1);
    
    h_part += '0';    
    l_part += '0';

    *l = l_part;
    *h = h_part;
}
//-----------------------------------------------------------------------------
char* put_long(char *buffer, int32u value)
{
    int8u* p = (int8u*)(&value);

    byte2hex(p[0], buffer + 7, buffer + 6);
    byte2hex(p[1], buffer + 5, buffer + 4);
    byte2hex(p[2], buffer + 3, buffer + 2);
    byte2hex(p[3], buffer + 1, buffer + 0);

    return buffer;
}
//-----------------------------------------------------------------------------


Заметьте: данный код требует 2's compliment and little endian.
Re[3]: Быстрейший способ конвертации int в hex-строку
От: Roman Odaisky Украина  
Дата: 30.09.06 12:50
Оценка: 4 (1)
Здравствуйте, korzhik, Вы писали:

ME>>Может что-то похожее на http://rsdn.ru/Forum/?mid=1514879
Автор: MaximE
Дата: 30.11.05
?


K>Если следовать идеям, который указаны по ссылке, то получаем такой код:

K>Заметьте: данный код требует 2's compliment and little endian.

А я попробовал — и компиляторы (а какой, кстати, использует автор?) выказали мало желания инлайнить эти вложенные функции. Вроде лучше так:
template <unsigned> struct U;

template<> struct U<8> : boost::mpl::identity<unsigned char > { };
template<> struct U<16>: boost::mpl::identity<unsigned short> { };
template<> struct U<32>: boost::mpl::identity<unsigned int  > { };

#define IS_U(u, value) ( (value) >= 0 && (value) < (1u << (u)) )

#define GET_HALFBYTE_BY_INDEX(value, index) ( assert(IS_U(3, (index))), \
    ( (value) & (0xF << ((index) * 4)) ) >> ((index) * 4) ) 

#define U4_TO_HEX_CHAR(value) ( assert(IS_U(4, (value))), \
    '0' + (value) + (-( (value) > 9) & ('a' - '9' - 1)) )

#define FOUR_U8_TO_U32(p, q, r, s) ( assert(IS_U(8, (p)) && IS_U(8, (q)) && IS_U(8, (r)) && IS_U(8, (s))), \
    ((((p) << 8) ^ (q)) << 16) ^ ((r) << 8) ^ (s) )

inline void u32ToHex(U<32>::type const value, char buffer[8])
{
    reinterpret_cast<U<32>::type &>(buffer[0]) = FOUR_U8_TO_U32(
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 4)),
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 5)),
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 6)),
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 7))
    );

    reinterpret_cast<U<32>::type &>(buffer[4]) = FOUR_U8_TO_U32(
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 0)),
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 1)),
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 2)),
        U4_TO_HEX_CHAR(GET_HALFBYTE_BY_INDEX(value, 3))
    );
}

Опять же требуются маленькие (?) добренькие индейцы, и вообще всё это UB. Зато избавились от обращения по невыровненным адресам. Кто хочет побенчмаркить?

P. S. Если всё так плохо, то, может, лучше переписать сие сразу на асме?

P. P. S. compl[ie]ment — разные слова

P. P. P. S. Как кино?
До последнего не верил в пирамиду Лебедева.
Re: Быстрейший способ конвертации int в hex-строку
От: trophim Россия  
Дата: 30.09.06 23:29
Оценка: 4 (1)
Да будет начато тестирование...
Мне удалось ускорить первоначалльный вариант в 2.4 раза. Резервы для дальнейшего обдумывания еще есть.

const unsigned short s_hexCharsUS[256] =
{
    0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730,0x3830,0x3930,0x4130,0x4230,0x4330,0x4430,0x4530,0x4630,
    0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731,0x3831,0x3931,0x4131,0x4231,0x4331,0x4431,0x4531,0x4631,
    0x3032,0x3132,0x3232,0x3332,0x3432,0x3532,0x3632,0x3732,0x3832,0x3932,0x4132,0x4232,0x4332,0x4432,0x4532,0x4632,
    0x3033,0x3133,0x3233,0x3333,0x3433,0x3533,0x3633,0x3733,0x3833,0x3933,0x4133,0x4233,0x4333,0x4433,0x4533,0x4633,
    0x3034,0x3134,0x3234,0x3334,0x3434,0x3534,0x3634,0x3734,0x3834,0x3934,0x4134,0x4234,0x4334,0x4434,0x4534,0x4634,
    0x3035,0x3135,0x3235,0x3335,0x3435,0x3535,0x3635,0x3735,0x3835,0x3935,0x4135,0x4235,0x4335,0x4435,0x4535,0x4635,
    0x3036,0x3136,0x3236,0x3336,0x3436,0x3536,0x3636,0x3736,0x3836,0x3936,0x4136,0x4236,0x4336,0x4436,0x4536,0x4636,
    0x3037,0x3137,0x3237,0x3337,0x3437,0x3537,0x3637,0x3737,0x3837,0x3937,0x4137,0x4237,0x4337,0x4437,0x4537,0x4637,
    0x3038,0x3138,0x3238,0x3338,0x3438,0x3538,0x3638,0x3738,0x3838,0x3938,0x4138,0x4238,0x4338,0x4438,0x4538,0x4638,
    0x3039,0x3139,0x3239,0x3339,0x3439,0x3539,0x3639,0x3739,0x3839,0x3939,0x4139,0x4239,0x4339,0x4439,0x4539,0x4639,
    0x3041,0x3141,0x3241,0x3341,0x3441,0x3541,0x3641,0x3741,0x3841,0x3941,0x4141,0x4241,0x4341,0x4441,0x4541,0x4641,
    0x3042,0x3142,0x3242,0x3342,0x3442,0x3542,0x3642,0x3742,0x3842,0x3942,0x4142,0x4242,0x4342,0x4442,0x4542,0x4642,
    0x3043,0x3143,0x3243,0x3343,0x3443,0x3543,0x3643,0x3743,0x3843,0x3943,0x4143,0x4243,0x4343,0x4443,0x4543,0x4643,
    0x3044,0x3144,0x3244,0x3344,0x3444,0x3544,0x3644,0x3744,0x3844,0x3944,0x4144,0x4244,0x4344,0x4444,0x4544,0x4644,
    0x3045,0x3145,0x3245,0x3345,0x3445,0x3545,0x3645,0x3745,0x3845,0x3945,0x4145,0x4245,0x4345,0x4445,0x4545,0x4645,
    0x3046,0x3146,0x3246,0x3346,0x3446,0x3546,0x3646,0x3746,0x3846,0x3946,0x4146,0x4246,0x4346,0x4446,0x4546,0x4646
};

bool putLong4 (char *buffer, int value)
{
    unsigned short* out = (unsigned short*)buffer;
    unsigned int x = value;

    if (buffer)
    {
        out[3] = s_hexCharsUS[ x & 0xFF ];
        x >>= 8;
        out[2] = s_hexCharsUS[ x & 0xFF ];
        x >>= 8;
        out[1] = s_hexCharsUS[ x & 0xFF ];
        x >>= 8;
        out[0] = s_hexCharsUS[ x ];
    }
    return !!buffer;
}


P.S. Как выложить на форум тестовую программку? Сделал давеча. Как ее залить, чтоб желающие смогли проверить на других платформах и архитектурах? (У меня винда и AMD Athlon 2000).
[EOF]
Let it be! — Давайте есть пчелу!
Re[2]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 02:16
Оценка: 4 (1)
#include "stdafx.h"

#include <stdio.h>
#include <windows.h>
#include <assert.h>

#define TESTVER    0            //plain sprintf
//#define TESTVER    1        //code by Tuo_Bellas
//#define TESTVER    2        //code by trophim
//#define TESTVER    3        //code by apple-antonovka

#if (TESTVER==0)
void init_hexChars()
{
};

bool putLong(char *buffer, int value)
{
  if(buffer)
  {
      sprintf(buffer,"%08X",value);
  }
  return !!buffer;
}
#endif


#if (TESTVER==1)
void init_hexChars()
{
};

static const char * const s_hexChars[256] =
{
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
"A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
"B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
"E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
"F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
};

bool putLong(char *buffer, int value)
{
  if (buffer)
  {
    buffer[0] = s_hexChars[(value & 0xFF000000) >> 24][0];
    buffer[1] = s_hexChars[(value & 0xFF000000) >> 24][1];
    buffer[2] = s_hexChars[(value & 0xFF0000) >> 16][0];
    buffer[3] = s_hexChars[(value & 0xFF0000) >> 16][1];
    buffer[4] = s_hexChars[(value & 0xFF00) >> 8][0];
    buffer[5] = s_hexChars[(value & 0xFF00) >> 8][1];
    buffer[6] = s_hexChars[(value & 0xFF)][0];
    buffer[7] = s_hexChars[(value & 0xFF)][1];
  }

  return !!buffer;
}
#endif


#if (TESTVER==2)
void init_hexChars()
{
};

const unsigned short s_hexCharsUS[256] =
{
    0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730,0x3830,0x3930,0x4130,0x4230,0x4330,0x4430,0x4530,0x4630,
    0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731,0x3831,0x3931,0x4131,0x4231,0x4331,0x4431,0x4531,0x4631,
    0x3032,0x3132,0x3232,0x3332,0x3432,0x3532,0x3632,0x3732,0x3832,0x3932,0x4132,0x4232,0x4332,0x4432,0x4532,0x4632,
    0x3033,0x3133,0x3233,0x3333,0x3433,0x3533,0x3633,0x3733,0x3833,0x3933,0x4133,0x4233,0x4333,0x4433,0x4533,0x4633,
    0x3034,0x3134,0x3234,0x3334,0x3434,0x3534,0x3634,0x3734,0x3834,0x3934,0x4134,0x4234,0x4334,0x4434,0x4534,0x4634,
    0x3035,0x3135,0x3235,0x3335,0x3435,0x3535,0x3635,0x3735,0x3835,0x3935,0x4135,0x4235,0x4335,0x4435,0x4535,0x4635,
    0x3036,0x3136,0x3236,0x3336,0x3436,0x3536,0x3636,0x3736,0x3836,0x3936,0x4136,0x4236,0x4336,0x4436,0x4536,0x4636,
    0x3037,0x3137,0x3237,0x3337,0x3437,0x3537,0x3637,0x3737,0x3837,0x3937,0x4137,0x4237,0x4337,0x4437,0x4537,0x4637,
    0x3038,0x3138,0x3238,0x3338,0x3438,0x3538,0x3638,0x3738,0x3838,0x3938,0x4138,0x4238,0x4338,0x4438,0x4538,0x4638,
    0x3039,0x3139,0x3239,0x3339,0x3439,0x3539,0x3639,0x3739,0x3839,0x3939,0x4139,0x4239,0x4339,0x4439,0x4539,0x4639,
    0x3041,0x3141,0x3241,0x3341,0x3441,0x3541,0x3641,0x3741,0x3841,0x3941,0x4141,0x4241,0x4341,0x4441,0x4541,0x4641,
    0x3042,0x3142,0x3242,0x3342,0x3442,0x3542,0x3642,0x3742,0x3842,0x3942,0x4142,0x4242,0x4342,0x4442,0x4542,0x4642,
    0x3043,0x3143,0x3243,0x3343,0x3443,0x3543,0x3643,0x3743,0x3843,0x3943,0x4143,0x4243,0x4343,0x4443,0x4543,0x4643,
    0x3044,0x3144,0x3244,0x3344,0x3444,0x3544,0x3644,0x3744,0x3844,0x3944,0x4144,0x4244,0x4344,0x4444,0x4544,0x4644,
    0x3045,0x3145,0x3245,0x3345,0x3445,0x3545,0x3645,0x3745,0x3845,0x3945,0x4145,0x4245,0x4345,0x4445,0x4545,0x4645,
    0x3046,0x3146,0x3246,0x3346,0x3446,0x3546,0x3646,0x3746,0x3846,0x3946,0x4146,0x4246,0x4346,0x4446,0x4546,0x4646
};

bool putLong (char *buffer, int value)
{
    unsigned short* out = (unsigned short*)buffer;
    unsigned int x = value;

    if (buffer)
    {
        out[3] = s_hexCharsUS[ x & 0xFF ];
        x >>= 8;
        out[2] = s_hexCharsUS[ x & 0xFF ];
        x >>= 8;
        out[1] = s_hexCharsUS[ x & 0xFF ];
        x >>= 8;
        out[0] = s_hexCharsUS[ x ];
    }
    return !!buffer;
}
#endif


#if (TESTVER==3)
static unsigned int s_hexChars[0x10000];

void init_hexChars()
{
    char tmp[sizeof(unsigned int)+1];
    for(int i=0;i<0x10000;i++)
    {
        sprintf(tmp,"%04X",i);
        memcpy(&s_hexChars[i],tmp,sizeof(unsigned int));
    }
}

bool putLong(char *buffer, unsigned int value)
{
  if (buffer)      
  {
  *(unsigned int *)(buffer) = s_hexChars[(value & 0xFFFF0000) >> 16];
  *(unsigned int *)(buffer+4) = s_hexChars[(value & 0xFFFF)];
  }

  return !!buffer;
}
#endif

int main(int argc, char* argv[])
{
    init_hexChars();
    char buff[32];
    DWORD tm1=::GetTickCount();
#ifndef _DEBUG
    for(int ii=0;ii<50;ii++)
#endif
    for (int i=0;i<1000000;i++)
    {
        putLong(buff, i);
#ifdef _DEBUG
        char checkbuff[32];
        sprintf(checkbuff,"%08X",i);
        assert(!memcmp(buff,checkbuff,8));
#endif
    }
    printf("%u\n",GetTickCount()-tm1);
    return 0;
}

win32, sempron@1700Mhz

debug версии прошли все ассерты нормально
release, speed optimizations:
VC6:
TESTVER=0 — 49181 msec (itoa почти в 4 раза быстрее, но не умеет делать padding нулями слева)
TESTVER=1 — 1350 msec
TESTVER=2 — 580 msec
TESTVER=3 — 430 msec

iCPP9:
TESTVER=0 — 46137 msec
TESTVER=1 — 840 msec
TESTVER=2 — 420 msec
TESTVER=3 — 280 msec
Re[2]: Быстрейший способ конвертации int в hex-строку
От: MaximE Великобритания  
Дата: 01.10.06 13:18
Оценка: 4 (1)
Xander Zerge wrote:

> T_B>GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего

> времени выполнения программы:
> T_B>...
> T_B>Возможна ли еще более быстрая реализация? О сокращении числа вызовов
> (10М+) — думаем.
>
> А зачем вообще этот массив? Для преобразования тетрады числа в
> шестнадцатеричный символ достаточно вот этого:
>
> int t;
> char str[9] = "";
> // ...
> str = ( ( t = n & ( 0x0F << i * 4 ) >> i * 4 ) < 0x0A ? '0' : 'A' — 0x0A ) + t;
>
> Имеем четыре константы (пропишутся в код), четыре однотактовых
> арифметических операции

С тактами вопрос тонкий. На суперскалярных процессорах несколько инструкций
выполняются параллельно, поэтому такты теряют смысл, говорят о instruction
throughput and latency.

> и одну операцию сравнения с возможностью

> предсказания ветвления ( чаще будет случаться "< 0x0A" ). Операция с
> памятью — прописывание символа.

От ненужного ветвления избавится достаточно просто:
http://rsdn.ru/Forum/?mid=1514879
Автор: MaximE
Дата: 30.11.05


--
[i]Maxim Yegorushkin

No Microsoft product was used in any way to write or send this text.
If you use a Microsoft product to read it, you're doing so at your own risk
Posted via RSDN NNTP Server 2.0
Re: А у меня в 2 раза быстрее!
От: remark Россия http://www.1024cores.net/
Дата: 02.10.06 08:08
Оценка: 4 (1)
Здравствуйте, Tuo_Bellas, Вы писали:

Ах, была не была. Если уж менять память на скорость, так уж до конца
У меня работает в ~2 раза быстрее, чем остальные варианты. Нужно 256 кб памяти:

char toHex(int value)
{
    return value <= 9 ? value + '0' : value - 10 + 'A';
}

void main()
{
    int data_[0xffff];

    for (int i = 0; i < 0xffff; ++i)
    {
        (reinterpret_cast<char*>(&data_[i]))[0] = toHex((i & 0xf000) >> 12);
        (reinterpret_cast<char*>(&data_[i]))[1] = toHex((i & 0x0f00) >> 8);
        (reinterpret_cast<char*>(&data_[i]))[2] = toHex((i & 0x00f0) >> 4);
        (reinterpret_cast<char*>(&data_[i]))[3] = toHex((i & 0x000f));
    }

    char buf[9] = {};
    int value = 0xdead0123;

    reinterpret_cast<int&>(buf[0]) = data_[unsigned(value) >> 16];
    reinterpret_cast<int&>(buf[4]) = data_[value & 0xffff];
}



Вот код преобразования:

   172:     reinterpret_cast<int&>(buf[0]) = data_[unsigned(value) >> 16];
0043D7A0  mov         eax,dword ptr [value] 
0043D7A6  shr         eax,10h 
0043D7A9  mov         ecx,dword ptr data_[eax*4] 
0043D7B0  mov         dword ptr [buf],ecx 
   173:     reinterpret_cast<int&>(buf[4]) = data_[value & 0xffff];
0043D7B6  mov         eax,dword ptr [value] 
0043D7BC  and         eax,0FFFFh 
0043D7C1  mov         ecx,dword ptr data_[eax*4] 
0043D7C8  mov         dword ptr [ebp-40028h],ecx



Идти далее и заводить таблицу из 0xffffffff элементов размером в 32 Гб памяти, видимо, не стоит ради преобразования в hex


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Быстрейший способ конвертации int в hex-строку
От: sc Россия  
Дата: 01.10.06 10:12
Оценка: 2 (1)
не смог не удержаться)) решил тряхнуть стариной и вспомнить ассемблерную молодость))
#if (TESTVER==4)
void init_hexChars()
{
};

bool putLong(char *buffer, unsigned int value)
{
    char* tbl = "0123456789ABCDEF";
    __asm{
        mov ebx, tbl;
        mov ecx, value;
        mov edx, buffer;
        xor esi, esi;
        xor edi, edi;
        ;1        
        mov eax, ecx;
        shr eax, 28;
        xlat;
        or esi, eax;
        ;2
        mov eax, ecx;
        shr eax, 24;
        and eax, 0x0000000f;
        xlat;
        shl eax, 8;
        or esi, eax;
        ;3
        mov eax, ecx;
        shr eax, 20;
        and eax, 0x0000000f;
        xlat;
        shl eax, 16;
        or esi, eax;
        ;4
        mov eax, ecx;
        shr eax, 16;
        and eax, 0x0000000f;
        xlat;
        shl eax, 24;
        or esi, eax;
        ;5
        mov eax, ecx;
        shr eax, 12;
        and eax, 0x0000000f;
        xlat;
        or edi, eax;
        ;6
        mov eax, ecx;
        shr eax, 8;
        and eax, 0x0000000f;
        xlat;
        shl eax, 8;
        or edi, eax;
        ;7
        mov eax, ecx;
        shr eax, 4;
        and eax, 0x0000000f;
        xlat;
        shl eax, 16;
        or edi, eax;
        ;8
        mov eax, ecx;
        and eax, 0x0000000f;
        xlat;
        shl eax, 24;
        or edi, eax;

        mov dword ptr [edx], esi;
        add edx, 4;
        mov dword ptr [edx], edi;
        mov eax, esi;
        and eax, 0x000000ff;
    }
}
#endif
Re[6]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 16:10
Оценка: 2 (1)
Алгоримический fналог на С:
#if (TESTVER==5)
const char s_hexChars[] = "0123456789ABCDEF";
void init_hexChars()
{
};

bool putLong(char *buffer, unsigned int value)
{
    if (buffer)
    {
        buffer+=7;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
        *(buffer--)=s_hexChars[value&0xf];
        value>>=4;
    }

    return !!buffer;
}
#endif


VC6:
TESTVER=4: 1590 msec //asm
TESTVER=5: 1000 msec //C

iCPP9:
TESTVER=4: 1530 msec //asm, чуть быстрее за счет оптимизации кода в main, а асм остался тем же самым
TESTVER=5: 830 msec //C intel по любому шустрее VC6

Вот вам, мои маленкие любители ассемблера
Re[7]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 16:35
Оценка: 2 (1)
static char tbl[] = "0123456789ABCDEF";

__declspec(naked) bool __fastcall putLong(char *buffer, unsigned int value)
{
    __asm{
        push esi
        push edi
        push ebx

        lea ebx, tbl;
        xor esi, esi;
        xor edi, edi;

        ;1        
        mov eax, edx;
        shr eax, 28;
        xlat;
        or esi, eax;
        ;2
        mov eax, edx;
        shr eax, 24;
        and eax, 0x0000000f;
        xlat;
        shl eax, 8;
        or esi, eax;
        ;3
        mov eax, edx;
        shr eax, 20;
        and eax, 0x0000000f;
        xlat;
        shl eax, 16;
        or esi, eax;
        ;4
        mov eax, edx;
        shr eax, 16;
        and eax, 0x0000000f;
        xlat;
        shl eax, 24;
        or esi, eax;
        ;5
        mov eax, edx;
        shr eax, 12;
        and eax, 0x0000000f;
        xlat;
        or edi, eax;
        ;6
        mov eax, edx;
        shr eax, 8;
        and eax, 0x0000000f;
        xlat;
        shl eax, 8;
        or edi, eax;
        ;7
        mov eax, edx;
        shr eax, 4;
        and eax, 0x0000000f;
        xlat;
        shl eax, 16;
        or edi, eax;
        ;8
        mov eax, edx;
        and eax, 0x0000000f;
        xlat;
        shl eax, 24;
        or edi, eax;

        mov dword ptr [ecx], esi;
        add ecx, 4;
        mov dword ptr [ecx], edi;
        mov eax, esi;
        and eax, 0x000000ff;

        pop ebx
        pop edi
        pop esi
        ret
    }
}
#endif


такой вариант у меня дает 1530 msec в VC. В принципе сравнимо (но се равно медленнее) с оригинальным вариантом от Tuo_Bellas, но в 1.5 раза медленнее _такого_же_ алгоритма на С. Я кстати тоже написал вчера любопытсва ради на асме, получилось вроде шустрее чем так, нь решил не публиковать всвязи с тем что получилось таки медленнее аналога сгенеренного компилятором )
Re[4]: Быстрейший способ конвертации int в hex-строку
От: ДимДимыч Украина http://klug.org.ua
Дата: 02.10.06 08:41
Оценка: 2 (1)
Здравствуйте, sc, Вы писали:

sc>не смог не удержаться)) решил тряхнуть стариной и вспомнить ассемблерную молодость))


Ну пробенчмаркайте и мой вариант:

global bin2hex

%include "c32.mac"

proc bin2hex
%$b    arg
%$n    arg

    mov edi, [ebp + %$b]   ; указатель на буфер
    mov edx, [ebp + %$n]   ; число
    cld
    mov ecx,8
align 4
.loop:    
    rol edx,4
    mov al,dl
    and al,0xF
    cmp al,10
    sbb al,69h
    das
    mov [edi],al
    inc edi
    dec ecx
    jnz short .loop
    mov byte [edi],cl
endproc
Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
Re[5]: Быстрейший способ конвертации int в hex-строку
От: Слоноежик  
Дата: 02.10.06 09:06
Оценка: 1 (1)
Здравствуйте, ДимДимыч, Вы писали:

ДД>Ну пробенчмаркайте и мой вариант:

даж бенчмаркить не надо. и так видно что код очень медленный.

ДД>
ДД>    mov al,dl  
// работа с младшей частью регистра eax без его предварительного зануления. не уверен насчет последних процессоров
// но на процессорах класса p2 - p3 такая инициализация без предварительного обнуления регистра занимала до 5 тактов
ДД>    and al,0xF
ДД>    cmp al,10
ДД>    sbb al,69h
ДД>    das
ДД>    mov [edi],al
// вся основная логика работы завязана на 1 регистр, и следующая команда обязательно ждет выполнения предидущей
// следовательно никак не распараллеливется и доп задержки на получение результата .
ДД>


З.Ы. В числодробленни на стандартном наборе инструкций оптимизирующие компиляторы победить ОЧЕНЬ сложно.
Гораздо проще попытаться использовать расширенные наборы инструкций (MMX, SSE, SSE2).
для забивания гвоздя надо выбирать правильный микроскоп.
Re[4]: Быстрейший способ конвертации int в hex-строку
От: MaximE Великобритания  
Дата: 02.10.06 15:50
Оценка: 1 (1)
Здравствуйте, SexMachine, Вы писали:

SM>Здравствуйте, apple-antonovka, Вы писали:


AA>>release, speed optimizations:

AA>>VC6:
AA>>TESTVER=0 — 49181 msec (itoa почти в 4 раза быстрее, но не умеет делать padding нулями слева)
AA>>TESTVER=1 — 1350 msec
AA>>TESTVER=2 — 580 msec
AA>>TESTVER=3 — 430 msec

AA>>iCPP9:

AA>>TESTVER=0 — 46137 msec
AA>>TESTVER=1 — 840 msec
AA>>TESTVER=2 — 420 msec
AA>>TESTVER=3 — 280 msec

SM>Неужели отсюда так сложно сделать вывод, что скорость обратно пропорциональна кол-ву операций "&" ?

SM>Кстати это вам любой , даже школьный, учитель информатики скажет

Похоже, что древние люди также простодушно заключили, что земля плоская.
Re[3]: А у меня в 2 раза быстрее!
От: CiViLiS Россия  
Дата: 03.10.06 06:15
Оценка: +1
Здравствуйте, trophim, Вы писали:

T>В общем у меня тоже оказалось, что табличный метод рулит. Ни один из предложенных чисто алгоритмических методов не смог сравниться с табличным. Хотя в чисто вычислительном нет лишних обращений к памяти, зато в табличном происходит за один раз вычисление 2 или 4 литер за раз. Получается быстрее, несмотря на то, что есть обращение к таблице. Вот.

не думаю что в реале табличные методы будут сильно рулить. Из-за лишних обращений к памяти, может получиться так, что из кеша будут выкинуты все полезные данные, а на их месте будет безстолковая таблица.
... << RSDN@Home 1.2.0 alpha rev. 655>>
"Бог не терпит голой сингулярности" -- Роджер Пенроуз
Re[4]: Быстрейший способ конвертации int в hex-строку
От: trophim Россия  
Дата: 04.10.06 22:03
Оценка: :)
Здравствуйте, remark, Вы писали:

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


S>>>А чем sprintf плох?

А>>Он более чем в 100 раз медленнее самого быстрого представленного тут способа

R>Ещё есть boost::format... но мы лучше умолчим о его производительности


R>


А я уже помидоры заготовил. Мягонькие такие — больно не будет...
[EOF]
Let it be! — Давайте есть пчелу!
Re: Быстрейший способ конвертации int в hex-строку
От: korzhik Россия  
Дата: 29.09.06 18:00
Оценка:
Здравствуйте, Tuo_Bellas, Вы писали:

T_B>Всем привет!


T_B>GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего времени выполнения программы:


а так?
bool putLong(char *buffer, int value)
{
    static char s_hexChars[] = "0123456789ABCDEF";

    if (buffer)
    {
        buffer[0] = s_hexChars[(value & 0xF0000000) >> 28];
        buffer[1] = s_hexChars[(value & 0x0F000000) >> 24];
        buffer[2] = s_hexChars[(value & 0xF00000) >> 20];
        buffer[3] = s_hexChars[(value & 0x0F0000) >> 16];
        buffer[4] = s_hexChars[(value & 0xF000) >> 12];
        buffer[5] = s_hexChars[(value & 0x0F00) >> 8];
        buffer[6] = s_hexChars[(value & 0xF0) >> 4];
        buffer[7] = s_hexChars[(value & 0x0F)];
    }

    return !!buffer;
}


код писал на ходу, так что может ошибся... всё побежал в кинотеатр
Re[4]: Быстрейший способ конвертации int в hex-строку
От: korzhik Россия  
Дата: 30.09.06 13:03
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>А я попробовал — и компиляторы (а какой, кстати, использует автор?) выказали мало желания инлайнить эти вложенные функции.


byte2hex все заинлайнились (VC7.1 /O2)

>Вроде лучше так:

[...]
возможно.
бенчмаркить надо.


RO>P. P. S. compl[ie]ment — разные слова


+1

RO>P. P. P. S. Как кино?


Смотрел Клерки-2. Редкостное дерьмо.
Re: Быстрейший способ конвертации int в hex-строку
От: glyph  
Дата: 30.09.06 19:03
Оценка:
Здравствуйте, Tuo_Bellas, Вы писали:

T_B>Возможна ли еще более быстрая реализация? О сокращении числа вызовов (10М+) — думаем.


А можно сформулировать задачу в более общем виде? Почему нельзя использовать crt? Можно ли отказаться от табличной подстановки? Какова таблица символов на целевой машине? Это должно быть переносимо?
Имхо, если вы уперлись в предел производительности, то оптимизация возможна только под конкретную машину. Кстати, какой на ней тип процессора?
... << Пикник — Мы как трепетные птицы (на польском языке) (бонус-трек)>>
Re[5]: Быстрейший способ конвертации int в hex-строку
От: trophim Россия  
Дата: 01.10.06 01:08
Оценка:
Здравствуйте, korzhik, Вы писали:

K>Здравствуйте, Roman Odaisky, Вы писали:


RO>>А я попробовал — и компиляторы (а какой, кстати, использует автор?) выказали мало желания инлайнить эти вложенные функции.


K>byte2hex все заинлайнились (VC7.1 /O2)


>>Вроде лучше так:

K>[...]
K>возможно.
K>бенчмаркить надо.

Я уже того. Накропал программу. Как ее забросить на форум?
[EOF]
Let it be! — Давайте есть пчелу!
Re[3]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 03:17
Оценка:
Кстати забавно — если проставить тип вызова __fastcall вместо дефолтового __cdecl то скорость работы 2 и 3 значительно увеличивается для VC6 и уменьшаются для iCPP
Re[6]: Быстрейший способ конвертации int в hex-строку
От: korzhik Россия  
Дата: 01.10.06 07:51
Оценка:
Здравствуйте, trophim, Вы писали:

T>Я уже того. Накропал программу. Как ее забросить на форум?


Заходишь в свой профайл, там в верху немного левее будет ссылка "файлы" жмёшь на неё, ну а там думаю разберёшься
Re: Быстрейший способ конвертации int в hex-строку
От: Xander Zerge Россия www.zerge.com
Дата: 01.10.06 09:48
Оценка:
Здравствуйте, Tuo_Bellas, Вы писали:

T_B>GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего времени выполнения программы:

T_B>...
T_B>Возможна ли еще более быстрая реализация? О сокращении числа вызовов (10М+) — думаем.

А зачем вообще этот массив? Для преобразования тетрады числа в шестнадцатеричный символ достаточно вот этого:
int t;
char str[9] = "";
// ...
str[i] = ( ( t = n & ( 0x0F << i * 4 ) >> i * 4 ) < 0x0A ? '0' : 'A' - 0x0A ) + t;


Имеем четыре константы (пропишутся в код), четыре однотактовых арифметических операции и одну операцию сравнения с возможностью предсказания ветвления ( чаще будет случаться "< 0x0A" ). Операция с памятью — прописывание символа.
По сравнению с исходным кодом, добавились две арифметических операции и операция сравнения, но исчезло обращение к памяти к двумерному массиву (а это плюс две арифметических операции).
Такие простые вычисления в регистрах пойдут быстрее, чем обращения к памяти. А цикл компилятор и сам развернёт.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Серёжа Новиков,
программист
Re[4]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 15:11
Оценка:
Вариант на асме — даже медленнее чем оригинальный вариант от Tuo_Bellas в VC6. Скока мона твердить миру — не соревнуйтесь с компиляторами
Re[5]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 15:15
Оценка:
Кстати этот самый варианы на асме даже медленнее его алгоритмического аналога на С скомпиленного в VC6.
Re[5]: Быстрейший способ конвертации int в hex-строку
От: sc Россия  
Дата: 01.10.06 15:49
Оценка:
Здравствуйте, apple-antonovka, Вы писали:

AA>Вариант на асме — даже медленнее чем оригинальный вариант от Tuo_Bellas в VC6. Скока мона твердить миру — не соревнуйтесь с компиляторами

Ну в общем-то да)) , примерно в 2 раза. Но зато таблица всего 17 байт (которую можно уменьшить до 16), против 256*2 у 2-го варианта (разница ~30 раз) и 65536*4 у 3-го варианта (разница ~15000 раз!).
А компилятор тут скорее всего ни при чем. Четко прослеживается зависимость скорости от размера таблицы. Чем больше таблица, тем меньше кода, следовательно работы компилятору и оптимизатору. Причем заметно, что для небольшого увеличения скорости нужно существенно увеличить размер таблицы.
Может кто-нибудь еще быстрее напишет?)
Re[6]: Быстрейший способ конвертации int в hex-строку
От: sc Россия  
Дата: 01.10.06 16:20
Оценка:
если вытащить строчку char* tbl = "0123456789ABCDEF" из ф-ции.
то вроде бы немного быстрее первоначального, 1-го варианта, получается (vc6)
Re[2]: Быстрейший способ конвертации int в hex-строку
От: trophim Россия  
Дата: 01.10.06 17:38
Оценка:
Здравствуйте, Xander Zerge, Вы писали:

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


T_B>>GCC 3.4.x. Пришли к тому, что следующая функция занимает 12% всего времени выполнения программы:

T_B>>...
T_B>>Возможна ли еще более быстрая реализация? О сокращении числа вызовов (10М+) — думаем.

XZ>А зачем вообще этот массив? Для преобразования тетрады числа в шестнадцатеричный символ достаточно вот этого:

XZ>
XZ>int t;
XZ>char str[9] = "";
XZ>// ...
XZ>str[i] = ( ( t = n & ( 0x0F << i * 4 ) >> i * 4 ) < 0x0A ? '0' : 'A' - 0x0A ) + t;
XZ>


XZ>Имеем четыре константы (пропишутся в код), четыре однотактовых арифметических операции и одну операцию сравнения с возможностью предсказания ветвления ( чаще будет случаться "< 0x0A" ). Операция с памятью — прописывание символа.

XZ>По сравнению с исходным кодом, добавились две арифметических операции и операция сравнения, но исчезло обращение к памяти к двумерному массиву (а это плюс две арифметических операции).
XZ>Такие простые вычисления в регистрах пойдут быстрее, чем обращения к памяти. А цикл компилятор и сам развернёт.

Вы не поверите, но эти вычисления работают медленнее, чем использование таблицы. Во всяком случае на моем атлон 2000. Я смотрел ассемблерный код после компилера — никаких переходов, песня просто. Процессор подумал иначе... Может ему не понравились манипуляции 8-битными данными?
[EOF]
Let it be! — Давайте есть пчелу!
Re: Быстрейший способ конвертации int в hex-строку
От: trophim Россия  
Дата: 01.10.06 17:38
Оценка:
Испробуйте тестовую программку http://rsdn.ru/File/15822/test_fastest_inttohex.rar (проект на MSVC 2003) особо интересует поведение на intel машинах, а также код, сгенерированный Intel С++.
Она гоняет тест для каждого варианта, а в конце пишет насколько очередной вариант стал быстрее в сравнении с тем, что предложил автор топика.
[EOF]
Let it be! — Давайте есть пчелу!
Re[3]: Быстрейший способ конвертации int в hex-строку
От: Xander Zerge Россия www.zerge.com
Дата: 01.10.06 18:48
Оценка:
Здравствуйте, trophim, Вы писали:

T>Вы не поверите, но эти вычисления работают медленнее, чем использование таблицы. Во всяком случае на моем атлон 2000. Я смотрел ассемблерный код после компилера — никаких переходов, песня просто. Процессор подумал иначе... Может ему не понравились манипуляции 8-битными данными?


Может быть. Предложу копнуть в сторону записи в память не побайтно, а подвусловно — собирать в DWORD четыре шестнадцетиричных цифры, списывать в память, потом делать то же самое со второй половинкой.
Если есть возможность, я на асме написал бы этот критический кусочек, и не парился.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Серёжа Новиков,
программист
Re[4]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 19:31
Оценка:
http://www.rsdn.ru/Forum/Message.aspx?mid=2139170&amp;only=1
Автор: apple-antonovka
Дата: 01.10.06

Там же по ходу ветки с этого места и про асм все ясно становится
Re[5]: Быстрейший способ конвертации int в hex-строку
От: Xander Zerge Россия www.zerge.com
Дата: 01.10.06 20:24
Оценка:
Здравствуйте, apple-antonovka, Вы писали:

AA>http://www.rsdn.ru/Forum/Message.aspx?mid=2139170&amp;only=1
Автор: apple-antonovka
Дата: 01.10.06

AA>Там же по ходу ветки с этого места и про асм все ясно становится
Я это видел. На выходе компилятора разве не асм получается? Значит ручками можно написать как минимум не хуже.
А приведённый там ассемблерный код очень плох, конечно. Уж про дёргание памяти за каждым байтиком, по неровным адресам, нечего и говорить. Ещё каждая команда работает с одним и тем же регистром — процессору не расшиться, бедняжке, всё в один конвейер работает. Да и выбор команд неудачен — зачем мучить весь 32-разрядный регистр операцией, целью которой является только младший байт?
Да, там результат DWORD-ами пишется, как я тут и говорил (не посмотрел выше), но неправильно пишется опять же — операция "add edx, 4" лишняя совершенно.

Так и чего ж вы хотели?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Серёжа Новиков,
программист
Re[6]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 20:30
Оценка:
XZ>Так и чего ж вы хотели?

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

А еще там есть реализация способа "Предложу копнуть в сторону записи в память не побайтно, а подвусловно — собирать в DWORD четыре шестнадцетиричных цифры, списывать в память, потом делать то же самое со второй половинкой." — под TESVER=3
Re[2]: Быстрейший способ конвертации int в hex-строку
От: apple-antonovka  
Дата: 01.10.06 22:44
Оценка:
ME>Решил погонять код на досуге, оказалось, что это самый быстрый из основных
ME>способов , предложенных в ветке (ассемблер не пробовал, т.к. у меня нет msvc):

ME>a) оригинальный метод, с таблицей на байт.

ME>b) метод с таблицей на пол-байта.
ME>c) метод без таблицы, с тернаным оператором (?: — потенциальное ветвление).
ME>d) c) без ветвления.

ME>Места распределились так: adbc.



Прошу ознакомиться с результатами внизу мессаги
http://www.rsdn.ru/Forum/Message.aspx?mid=2139170&amp;only=1
Автор: apple-antonovka
Дата: 01.10.06


Там 3 способа (0й на sprintf не в счет), переключаются сменой дефайна TESTVER
самым быстрым оказался метод таблично-строкового (каждая строка — 4 байта) представления чисел 0x0000..0xffff
Re[3]: Быстрейший способ конвертации int в hex-строку
От: MaximE Великобритания  
Дата: 02.10.06 07:22
Оценка:
Здравствуйте, apple-antonovka, Вы писали:

ME>>Решил погонять код на досуге, оказалось, что это самый быстрый из основных

ME>>способов , предложенных в ветке (ассемблер не пробовал, т.к. у меня нет msvc):

ME>>a) оригинальный метод, с таблицей на байт.

ME>>b) метод с таблицей на пол-байта.
ME>>c) метод без таблицы, с тернаным оператором (?: — потенциальное ветвление).
ME>>d) c) без ветвления.

ME>>Места распределились так: adbc.


AA>Прошу ознакомиться с результатами внизу мессаги

AA>http://www.rsdn.ru/Forum/Message.aspx?mid=2139170&amp;only=1
Автор: apple-antonovka
Дата: 01.10.06


AA>Там 3 способа (0й на sprintf не в счет), переключаются сменой дефайна TESTVER

AA>самым быстрым оказался метод таблично-строкового (каждая строка — 4 байта) представления чисел 0x0000..0xffff

Смотрел. Мне пришлось написать новый тест по следующим соображениям:
[list]
  • Нет "прогревочных циклов". Они нужны, чтобы не считать время затраченное на заполнение кэшей процессора и время, потраченное операционной системой на отображение страниц.
  • char* buffer каститься unsigned short*/int*, что требует, чтобы буфер был aligned
  • byte order предполагается little-endian
  • Не нравится компилировать каждый раз, чтобы протестировать какой-то способ.
    [/lits]
  • Re[4]: Быстрейший способ конвертации int в hex-строку
    От: Roman Odaisky Украина  
    Дата: 02.10.06 07:39
    Оценка:
    Здравствуйте, MaximE, Вы писали:

    ME>• char* buffer каститься unsigned short*/int*, что требует, чтобы буфер был aligned

    Если он получен с помощью new [], то он aligned.

    ME>* byte order предполагается little-endian

    Некрасиво, конечно, но в результирующем коде можно понавешать ifdef и всё будет в порядке.
    До последнего не верил в пирамиду Лебедева.
    Re[5]: ?????????? ?????? ??????????? int ? hex-??????
    От: MaximE Великобритания  
    Дата: 02.10.06 08:24
    Оценка:
    Roman Odaisky wrote:

    > ME>
  • char* buffer ????????? unsigned short*/int*, ??? ???????, ?????
    > ????? ??? aligned

    > ???? ?? ??????? ? ??????? new [], ?? ?? aligned.


    ? ??? ???? ????? ??????? ?? ? ????? ?????? ??????, ??? ??? ????? ??????? ?????,
    ?-??? ?????? ??????, ??? ???? ????? ??? ???????, ????????????, ??? ?????
    ???????, ?????????? ??? alignment ?? ??????? ????? char* buf.

    --
    Maxim Yegorushkin

    No Microsoft product was used in any way to write or send this text.
    If you use a Microsoft product to read it, you're doing so at your own risk
    Posted via RSDN NNTP Server 2.0
  • Re[6]: Быстрейший способ конвертации int в hex-строку
    От: ДимДимыч Украина http://klug.org.ua
    Дата: 02.10.06 09:11
    Оценка:
    Здравствуйте, Слоноежик, Вы писали:

    ДД>>Ну пробенчмаркайте и мой вариант:

    С>даж бенчмаркить не надо. и так видно что код очень медленный.

    Да, согласен, сам убедился
    Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
    Re[5]: Быстрейший способ конвертации int в hex-строку
    От: MaximE Великобритания  
    Дата: 02.10.06 10:20
    Оценка:
    Roman Odaisky wrote:

    > ME>char* buffer каститься unsigned short*/int*, что требует, чтобы

    > буфер был aligned

    > Если он получен с помощью new [], то он aligned.


    Какова, интересно, применимость ф-ции, которая может выводить hex только в
    начало буфера
    , выделенного new/malloc?

    Можно, конечно, определить alignment буфера по младшим битам char* buf, но мне
    больше по душе ф-ция, которой достаточен натуральный alignment char на данной
    платформе.

    --
    Maxim Yegorushkin

    No Microsoft product was used in any way to write or send this text.
    If you use a Microsoft product to read it, you're doing so at your own risk
    Posted via RSDN NNTP Server 2.0
    Re[6]: Быстрейший способ конвертации int в hex-строку
    От: Roman Odaisky Украина  
    Дата: 02.10.06 10:57
    Оценка:
    Здравствуйте, MaximE, Вы писали:

    >> Если он получен с помощью new [], то он aligned.


    ME>Какова, интересно, применимость ф-ции, которая может выводить hex только в

    ME>начало буфера
    , выделенного new/malloc?

    Так она, вроде бы, будет работать для любого выравнивания, но в определенных случаях она будет работать быстрее.

    ME>Можно, конечно, определить alignment буфера по младшим битам char* buf, но мне

    ME>больше по душе ф-ция, которой достаточен натуральный alignment char на данной
    ME>платформе.

    Тут нужна такая функция, которая будет по душе автору и его gcc
    До последнего не верил в пирамиду Лебедева.
    Re[3]: Быстрейший способ конвертации int в hex-строку
    От: SexMachine Украина www.is.svitonline.com/sashko1
    Дата: 02.10.06 15:06
    Оценка:
    Здравствуйте, apple-antonovka, Вы писали:

    AA>release, speed optimizations:

    AA>VC6:
    AA>TESTVER=0 — 49181 msec (itoa почти в 4 раза быстрее, но не умеет делать padding нулями слева)
    AA>TESTVER=1 — 1350 msec
    AA>TESTVER=2 — 580 msec
    AA>TESTVER=3 — 430 msec

    AA>iCPP9:

    AA>TESTVER=0 — 46137 msec
    AA>TESTVER=1 — 840 msec
    AA>TESTVER=2 — 420 msec
    AA>TESTVER=3 — 280 msec

    Неужели отсюда так сложно сделать вывод, что скорость обратно пропорциональна кол-ву операций "&" ?
    Кстати это вам любой , даже школьный, учитель информатики скажет
    У кого-то варит голова, у кого-то — желудок...
    Re[8]: Быстрейший способ конвертации int в hex-строку
    От: Tuo_Bellas Россия  
    Дата: 02.10.06 17:02
    Оценка:
    Здравствуйте, apple-antonovka, Вы писали:

    AA>Угу а автор пропал


    Автор зачитался

    Всем ответившим огромное спасибо! Ушел тестить различные варианты на производительность.

    Tuo_Bellas.
    Re[2]: А у меня в 2 раза быстрее!
    От: trophim Россия  
    Дата: 02.10.06 19:00
    Оценка:
    Здравствуйте, remark, Вы писали:

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


    R>Ах, была не была. Если уж менять память на скорость, так уж до конца

    R>У меня работает в ~2 раза быстрее, чем остальные варианты. Нужно 256 кб памяти:

    R>Идти далее и заводить таблицу из 0xffffffff элементов размером в 32 Гб памяти, видимо, не стоит ради преобразования в hex


    В общем у меня тоже оказалось, что табличный метод рулит. Ни один из предложенных чисто алгоритмических методов не смог сравниться с табличным. Хотя в чисто вычислительном нет лишних обращений к памяти, зато в табличном происходит за один раз вычисление 2 или 4 литер за раз. Получается быстрее, несмотря на то, что есть обращение к таблице. Вот.
    [EOF]
    Let it be! — Давайте есть пчелу!
    Re[2]: Быстрейший способ конвертации int в hex-строку
    От: MaximE Великобритания  
    Дата: 02.10.06 22:19
    Оценка:
    MaximE wrote:

    []

    > Решил погонять код на досуге, оказалось, что это самый быстрый из основных

    > способов , предложенных в ветке (ассемблер не пробовал, т.к. у меня нет
    > msvc):
    >
    > a) оригинальный метод, с таблицей на байт.
    > b) метод с таблицей на пол-байта.
    > c) метод без таблицы, с тернаным оператором (?: — потенциальное ветвление).
    > d) c) без ветвления.
    >
    > Места распределились так: adbc.

    Интересно, что на стареньком ultrasparc 2 512mhz расклад метод b вырвался
    вперед: badc.

    $ uname -a
    SunOS xxx 5.8 Generic_117350-28 sun4u sparc SUNW,Sun-Blade-100
    $ bin/test
    A: 650023 usec
    B: 578241 usec
    C: 1962825 usec
    D: 1755974 usec


    --
    Maxim Yegorushkin

    No Microsoft product was used in any way to write or send this text.
    If you use a Microsoft product to read it, you're doing so at your own risk
    Posted via RSDN NNTP Server 2.0
    Re[3]: А у меня в 2 раза быстрее!
    От: Xander Zerge Россия www.zerge.com
    Дата: 03.10.06 04:19
    Оценка:
    Здравствуйте, trophim, Вы писали:

    T>В общем у меня тоже оказалось, что табличный метод рулит. Ни один из предложенных чисто алгоритмических методов не смог сравниться с табличным. Хотя в чисто вычислительном нет лишних обращений к памяти, зато в табличном происходит за один раз вычисление 2 или 4 литер за раз. Получается быстрее, несмотря на то, что есть обращение к таблице. Вот.

    Да все вычислительные обращаются к памяти не так, как надо, а побайтно. Я видел только один алгоритм, который писал двойными словами, вот только он в основной своей части был далёк от оптимального.
    ... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
    Серёжа Новиков,
    программист
    Re: Офтоп
    От: minorlogic Украина  
    Дата: 03.10.06 06:32
    Оценка:
    Если не секрет , что это за программа такая ? где нужно ОЧЕНЬ быстро переводить в 16 ричные . ?
    Ищу работу, 3D, SLAM, computer graphics/vision.
    Re[2]: Офтоп
    От: Tuo_Bellas Россия  
    Дата: 03.10.06 07:19
    Оценка:
    Здравствуйте, minorlogic, Вы писали:

    M>Если не секрет , что это за программа такая ? где нужно ОЧЕНЬ быстро переводить в 16 ричные . ?


    Как обычно, протокол обмена данными (16-ричные — данность, какой-нибудь base64 не получится использовать).

    Tuo_Bellas.
    Re[4]: А у меня в 2 раза быстрее!
    От: MaximE Великобритания  
    Дата: 03.10.06 07:27
    Оценка:
    Здравствуйте, CiViLiS, Вы писали:

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


    T>>В общем у меня тоже оказалось, что табличный метод рулит. Ни один из предложенных чисто алгоритмических методов не смог сравниться с табличным. Хотя в чисто вычислительном нет лишних обращений к памяти, зато в табличном происходит за один раз вычисление 2 или 4 литер за раз. Получается быстрее, несмотря на то, что есть обращение к таблице. Вот.


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

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


    Согласен абсолютно.

    В ядре линукса они избегают лишних обращений к памяти, чтобы не выкидывать из кэша данные пользовательских процессов.
    Re[5]: Keep your mind open
    От: remark Россия http://www.1024cores.net/
    Дата: 03.10.06 08:50
    Оценка:
    Здравствуйте, MaximE, Вы писали:

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


    ME>Согласен абсолютно.


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



    Вот именно, ключевое слово "лишние обращения к памяти". Только отчего же Вы так однобоко смотрите на проблему? Я, конечно, понимаю, что MaximE придумал клёвый алгоритм и теперь защищает его

    Но давайте посмотрим на проблему более детально. Почему Вы зациклились на обращениях к памяти только за данными? Памяти всё равно за чем к ней обращаются за данными или за командами.
    Скомпилил алгоритм MaximE здесь
    Автор: MaximE
    Дата: 30.11.05
    для перевода int (4 байта) в hex. Весь asm приводить долго, но в общем получилось 74 ассемблерных комманды. Возьмём на вскидку 4 байта на команду, получается 296 байт.
    Скомпилил табличный алгоритм здесь
    Автор: remark
    Дата: 02.10.06
    . У меня получилось 7 команд, т.е. 28 байт.
    Если считать кэш линия для команд 32 байта (если не так поправьте, давно это было), то получается табличнй алгоритм займёт 1 линию для команд и 2 для данных. А вычисляющий 8 линий для комманд.
    Итого 3 против 8.
    Может я что напутал с длинами команд или размером кэша, но суть, я думаю, останется такая же.
    Так же слышал, что скорость работы очень сильно влияет, когда выполнение переходит через границу страницы в памяти (видимо связано с защитой памяти). Очевидно, что с алгоритмом в 296 байт это словить гораздо легче, чем с алгоритмом в 28 байт.

    Т.ч. табличный алгоритм будет не только в 2 раза быстрее считать, когда всё в кэше. Но и в 3 раза лучше в плане локальности памяти.


    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[6]: Keep your mind open
    От: MaximE Великобритания  
    Дата: 03.10.06 09:08
    Оценка:
    Здравствуйте, remark, Вы писали:

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


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


    ME>>Согласен абсолютно.


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


    R>Вот именно, ключевое слово "лишние обращения к памяти". Только отчего же Вы так однобоко смотрите на проблему?


    []

    Согласен, лишние те обращения или нет зависит от конкретной задачи.

    > Я, конечно, понимаю, что MaximE придумал клёвый алгоритм и теперь защищает его


    Как же я защищаю, если тесты http://rsdn.ru/forum/?mid=2139633
    Автор: MaximE
    Дата: 02.10.06
    показывают, что он не лучший?
    Re[7]: Keep your mind open
    От: remark Россия http://www.1024cores.net/
    Дата: 03.10.06 09:25
    Оценка:
    Здравствуйте, MaximE, Вы писали:

    R>>Вот именно, ключевое слово "лишние обращения к памяти". Только отчего же Вы так однобоко смотрите на проблему?


    ME>[]


    ME>Согласен, лишние те обращения или нет зависит от конкретной задачи.


    Предлагаю переформулировать бенчмарк так: сколько алгоритм требует загрузить из ОП байт/кэш линий для данных и команд. Т.е. более комплексный параметр.

    Пока я так вижу, что и по обращениям к памяти вычисляющие алгоритмы сливают табличым (особенно с таблицей на 64к).


    >> Я, конечно, понимаю, что MaximE придумал клёвый алгоритм и теперь защищает его


    ME>Как же я защищаю, если тесты http://rsdn.ru/forum/?mid=2139633
    Автор: MaximE
    Дата: 02.10.06
    показывают, что он не лучший?


    Это я пошутил

    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[6]: Быстрейший способ конвертации int в hex-строку
    От: ДимДимыч Украина http://klug.org.ua
    Дата: 03.10.06 12:11
    Оценка:
    Здравствуйте, Слоноежик, Вы писали:

    С>З.Ы. В числодробленни на стандартном наборе инструкций оптимизирующие компиляторы победить ОЧЕНЬ сложно.

    С>Гораздо проще попытаться использовать расширенные наборы инструкций (MMX, SSE, SSE2).

    Кстати, еще не факт, что сохранение/восстановление контекста FPU в некоторых условиях будет выгоднее вытеснения кэша.
    Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
    Re[8]: Keep your mind open
    От: CiViLiS Россия  
    Дата: 03.10.06 14:30
    Оценка:
    Здравствуйте, remark, Вы писали:

    R>Предлагаю переформулировать бенчмарк так: сколько алгоритм требует загрузить из ОП байт/кэш линий для данных и команд. Т.е. более комплексный параметр.

    А может посмотреть что скажет VTune? Насколько я помню -- он показывает и кеш промахи и такты и что может работать параллельно. Я посмотреть не могу -- у меня и на работе и дома AMD. На них вэтюн не работает

    >>> Я, конечно, понимаю, что MaximE придумал клёвый алгоритм и теперь защищает его


    ME>>Как же я защищаю, если тесты http://rsdn.ru/forum/?mid=2139633
    Автор: MaximE
    Дата: 02.10.06
    показывают, что он не лучший?

    Кстати не понимаю почему там в общем случае не будет ветвления. Там же есть проверка: (t > 9). На 386 и выше, вроде бы, есть инструкция чтобы установить регистр взависимости от флагов, но точно есть процы, на которых подобных комманд нет.
    ... << RSDN@Home 1.2.0 alpha rev. 655>>
    "Бог не терпит голой сингулярности" -- Роджер Пенроуз
    Re[9]: Keep your mind open
    От: remark Россия http://www.1024cores.net/
    Дата: 03.10.06 14:37
    Оценка:
    Здравствуйте, CiViLiS, Вы писали:

    ME>>>Как же я защищаю, если тесты http://rsdn.ru/forum/?mid=2139633
    Автор: MaximE
    Дата: 02.10.06
    показывают, что он не лучший?

    CVL>Кстати не понимаю почему там в общем случае не будет ветвления. Там же есть проверка: (t > 9). На 386 и выше, вроде бы, есть инструкция чтобы установить регистр взависимости от флагов, но точно есть процы, на которых подобных комманд нет.

    Там именно такая команда и получается. Видимо имеется в виду, что это под семейство 80x86. Под все возможные процессоры всё равно не получится написать одну функцию, которая бы могла "оптимально" работать.


    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[6]: Быстрейший способ конвертации int в hex-строку
    От: ДимДимыч Украина http://klug.org.ua
    Дата: 03.10.06 15:30
    Оценка:
    Здравствуйте, Слоноежик, Вы писали:

    С>Гораздо проще попытаться использовать расширенные наборы инструкций (MMX, SSE, SSE2).


    Вот вариант с MMX, который у меня работает на 13% быстрее, чем в исходном посте:

    section .text
    
    ; инициализация MMX - вызывается единожды в начале работы
    mmx_init:
        emms
        movq mm3,[m0]
        movq mm4,[m1]
        movq mm5,[m2]
        movq mm6,[m3]
        movq mm7,[m4]
        retn
    
    ; первый параметр - указатель на буфер. buf[8] должен содержать завершающий нуль, т.к. функция его не записывает
    ; второй параметр - значение для конвертации
    bin2hex_mmx:
        push ebp
        mov ebp,esp
    
        mov edi, [ebp + 0x8]    ; указатель
        mov eax, [ebp + 0xC]    ; значение
        bswap eax
        movd mm0,eax 
    
        punpcklbw mm0,mm0
        movq mm1,mm0
    
        pand mm0,mm3
        pand mm1,mm4
        psraw mm0,4
        por mm0,mm1
        movq mm1,mm0
    
        pcmpgtb mm1,mm6
        pand mm1,mm7
        paddb mm0,mm1
        paddb mm0,mm5
    
        movq [edi], mm0
    
        pop ebp
        retn
    
    section .data
    m0:
        dd    0x00F000F0
        dd    0x00F000F0
    m1:
        dd    0x0F000F00
        dd    0x0F000F00
    m2:
        dd    0x30303030
        dd    0x30303030
    m3:
        dd    0x09090909
        dd    0x09090909
    m4:
        dd    0x07070707
        dd    0x07070707


    Еще повысить производительность можно инлайнизацией функции.
    Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
    Re[7]: Быстрейший способ конвертации int в hex-строку
    От: ДимДимыч Украина http://klug.org.ua
    Дата: 03.10.06 15:32
    Оценка:
    ДД>Еще повысить производительность можно инлайнизацией функции.

    А если сократить количество загружаемых в начале констант с 5 до 4, то можно преобразовывать 2 числа без branch'ей.
    Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
    Re: Быстрейший способ конвертации int в hex-строку
    От: Shmakov Россия  
    Дата: 03.10.06 15:32
    Оценка:
    А чем sprintf плох?
    Re[2]: Быстрейший способ конвертации int в hex-строку
    От: Аноним  
    Дата: 03.10.06 15:41
    Оценка:
    S>А чем sprintf плох?
    Он более чем в 100 раз медленнее самого быстрого представленного тут способа
    Re[3]: Быстрейший способ конвертации int в hex-строку
    От: remark Россия http://www.1024cores.net/
    Дата: 03.10.06 16:20
    Оценка:
    Здравствуйте, Аноним, Вы писали:

    S>>А чем sprintf плох?

    А>Он более чем в 100 раз медленнее самого быстрого представленного тут способа

    Ещё есть boost::format... но мы лучше умолчим о его производительности


    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[3]: Быстрейший способ конвертации int в hex-строку
    От: Shmakov Россия  
    Дата: 03.10.06 18:57
    Оценка:
    Если честно — все это хорошо, но возник вопрос.
    А зачем производить 10М+ преобразование в hex-string-формат?
    Может дело в проблеме архитектуры?
    Re[7]: Быстрейший способ конвертации int в hex-строку
    От: sc Россия  
    Дата: 04.10.06 05:27
    Оценка:
    Здравствуйте, ДимДимыч, Вы писали:
    <skipped>

    Я думаю, что можно попробовать сделать быстрее, чем самый быстрый вариант (табл. 64K int-ов), если обрабатывать по 4 числа за раз.
    Т.е. ф-ция примет примерно такой вид:
    bool putLong(char *buffer, unsigned int n0, unsigned int n1, unsigned int n2, unsigned int n3)
    {
        ;; далее псевдо-код))
        __asm{
            lea   eax, s_hexChars;
            mov   ebx, buffer;
            
            movd  mm0, n0;
            movd  mm1, mm0;
    
            psrld mm0, 16;          ;получили старшее dword
            pslld mm0, 2;           ;умношили на 4
            paddd mm0, eax;         ;получили адрес в таблице
            movd  mm0, [mm0];       ;как так сделать? загрузить в mm0 dword по адресу mm0
    
            pand  mm1, 0x0000ffff;  ;получили младшее dword (так тоже нельзя?)
            pslld mm1, 2;           ;умношили на 4
            paddd mm1, eax;         ;получили адрес в таблице
            movd  mm1, [mm1];       ;??? нужно загрузить в mm0 dword по адресу mm0
            psllq mm1, 32;          ;подвинули        
            por   mm0, mm1;         ;в mm0 результат
    
            movq  [ebx], mm0;       ;записали результат
    
            ;; тоже самое для n1, n2, n3 где будут задейстованы регистры mm2/mm3, mm4/mm5, mm6/mm7
            ;; результат класть  movq [ebx+8], mm2  и т.д.        
        }
    }

    цикл тогда примет примерно такой вид:
    for(int i=0; i<1000000 / 4; i += 4)
        putLong(buff, i, i + 1, i + 2, i + 3);

    возможно процессор выполнит часть инструкций параллельно (если их правильно перемешать)), типа:
    чтение
    выполнение--чтение
    запись------выполнение--чтение
    ------------запись------выполнение--чтение
    ------------------------запись------выполнение
    ------------------------------------запись
    насколько я понял из мануалов, чтение не может выполняться паралелльно чтению (и запись тоже)
    а вот чтение/запись паралелльно выполняться может
    Re[8]: Быстрейший способ конвертации int в hex-строку
    От: Аноним  
    Дата: 04.10.06 05:36
    Оценка:
    т.е. я имел ввиду, ускорить самый быстрый вариант)
    Re[7]: Быстрейший способ конвертации int в hex-строку
    От: MaximE Великобритания  
    Дата: 04.10.06 07:01
    Оценка:
    ДимДимыч wrote:

    > С>Гораздо проще попытаться использовать расширенные наборы инструкций

    > (MMX, SSE, SSE2).
    >
    > Вот вариант с MMX, который у меня работает на 13% быстрее, чем в
    > исходном <Message.aspx?mid=2138136&only=1> посте:

    []

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

    --
    Maxim Yegorushkin

    No Microsoft product was used in any way to write or send this text.
    If you use a Microsoft product to read it, you're doing so at your own risk
    Posted via RSDN NNTP Server 2.0
    Re[7]: Быстрейший способ конвертации int в hex-строку
    От: MaximE Великобритания  
    Дата: 04.10.06 07:06
    Оценка:
    ДимДимыч wrote:

    > С>Гораздо проще попытаться использовать расширенные наборы инструкций

    > (MMX, SSE, SSE2).
    >
    > Вот вариант с MMX, который у меня работает на 13% быстрее, чем в
    > исходном <Message.aspx?mid=2138136&only=1> посте:

    Как он в сравнении с
    http://groups.google.co.uk/group/comp.lang.asm.x86/msg/97df4e371eab3636
    ?

    --
    Maxim Yegorushkin

    No Microsoft product was used in any way to write or send this text.
    If you use a Microsoft product to read it, you're doing so at your own risk
    Posted via RSDN NNTP Server 2.0
    Re[8]: Быстрейший способ конвертации int в hex-строку
    От: ДимДимыч Украина http://klug.org.ua
    Дата: 04.10.06 14:18
    Оценка:
    Здравствуйте, MaximE, Вы писали:

    ME>Как он в сравнении с

    ME>http://groups.google.co.uk/group/comp.lang.asm.x86/msg/97df4e371eab3636
    ME>?

    Да, этот код быстрее моего.
    Вот, я его портировал на gcc inline asm:

    inline void mmx_init(void)
    {
        static const unsigned long long consts[3] = 
        {
        0x3030303030303030LL,
        0x0f0f0f0f0f0f0f0fLL,
        0x0909090909090909LL
        };
    
        __asm__ __volatile__
        (
        "emms            \n"
        "movq (%0),  %%mm3    \n"
        "movq 8(%0),%%mm2    \n"
        "movq 16(%0), %%mm4    \n"
        : : "r" (consts)
        );
    }
    
    
    inline void bin2hex_mmx(char *buffer, unsigned int value)
    {
        __asm__  __volatile__
        (
        "movl %0,%%eax        \n"
        "bswap %%eax        \n"
    
        "movq %%mm3,%%mm5    \n"
    
            "psubb %%mm4,%%mm5    \n"
            "movd %%eax, %%mm0    \n"
            "movq %%mm0,%%mm1    \n"
            "psrlq $4, %%mm0    \n"
            "pand %%mm2,%%mm0    \n"
            "pand %%mm2,%%mm1    \n"
            "punpcklbw %%mm1,%%mm0    \n"
            "movq %%mm0,%%mm1    \n"
            "pcmpgtb %%mm4,%%mm0    \n"
            "pand %%mm5,%%mm0    \n"
            "paddb %%mm3,%%mm1    \n"
            "paddb %%mm0,%%mm1    \n"
            "movq %%mm1,(%1)    \n"
    
        : :  "r" (value), "r" (buffer) : "eax"
        );
    }


    Но как оказалось, исходный вариант с таблицей, если его заинлайнить, работает еще быстрее этого
    Обязательно бахнем! И не раз. Весь мир в труху! Но потом. (ДМБ)
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.