Re[5]: inline memset
От: Шахтер Интернет  
Дата: 12.02.04 02:48
Оценка: 65 (6)
Здравствуйте, c-smile, Вы писали:

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


К>>Здравствуйте, c-smile, Вы писали:


CS>>>Павел, а std::fill разве не вызывает memset ?

CS>>>Если нет то зря....

К>>
К>>template<class InputIter, class FillValue>
К>>void fill(InputIter i, InputIter e, const FillValue& v)
К>>{
К>>    while(i != e) { *i = v; ++i; }
К>>}
К>>


CS>Проверено на Intell 8.0: memset работает в 3-4 раза быстрее чем такой вот цикл.

CS>То же самое и memmove memcopy для POD типов.

Дело в том, что эти вещи семантически не эквивалентны. Так что прямое сравнение производительности тут некорректно. Причина -- в передаче FillValue по ссылке.
Смотрим на VC7.1.

Пример 1.

/* main.cpp */ 

#include <iostream>

using namespace std;

template<class InputIter,class FillValue>
void fill(InputIter i,InputIter e,const FillValue& v)
 {
  while(i != e) { *i = v; ++i; }
 }

/* main() */ 

int main()
 {
  char buf[10];
  
  ::fill(buf,buf+10,char(1));
  
  cout << buf ;
  
  std::fill(buf,buf+10,char(1));
  
  cout << buf ;
  
  memset(buf,1,10);
 
  cout << buf ;
  
  return 0;
 }


; 17   :   char buf[10];
; 18   :   
; 19   :   ::fill(buf,buf+10,char(1));
; 20   :   
; 21   :   cout << buf ;

    mov    edx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    push    esi
    mov    esi, DWORD PTR __imp_??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z
    mov    eax, 16843009                ; 01010101H
    lea    ecx, DWORD PTR _buf$[esp+16]
    mov    DWORD PTR _buf$[esp+16], eax
    push    ecx
    mov    DWORD PTR _buf$[esp+24], eax
    push    edx
    mov    WORD PTR _buf$[esp+32], ax
    call    esi

; 22   :   
; 23   :   std::fill(buf,buf+10,char(1));
; 24   :   
; 25   :   cout << buf ;

    mov    edx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    mov    eax, 16843009                ; 01010101H
    lea    ecx, DWORD PTR _buf$[esp+24]
    mov    DWORD PTR _buf$[esp+24], eax
    push    ecx
    mov    DWORD PTR _buf$[esp+32], eax
    push    edx
    mov    WORD PTR _buf$[esp+40], ax
    call    esi

; 26   :   
; 27   :   memset(buf,1,10);
; 28   :  
; 29   :   cout << buf ;

    mov    edx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    mov    eax, 16843009                ; 01010101H
    lea    ecx, DWORD PTR _buf$[esp+32]
    mov    DWORD PTR _buf$[esp+32], eax
    push    ecx
    mov    DWORD PTR _buf$[esp+40], eax
    push    edx
    mov    WORD PTR _buf$[esp+48], ax
    call    esi


Тождественный по существу код.

Пример 2. Более интересный.

template<class InputIter,class FillValue>
void fill(InputIter i,InputIter e,const FillValue& v)
 {
  while(i != e) { *i = v; ++i; }
 }
 
template<class InputIter,class FillValue>
void fill1(InputIter i,InputIter e,FillValue v)
 {
  while(i != e) { *i = v; ++i; }
 }
 
/* test...() */  

void test1(char *pt,size_t pt_size)
 {
  ::fill(pt,pt+pt_size,char(1));
 }
 
void test2(char *pt,size_t pt_size)
 {
  std::fill(pt,pt+pt_size,char(1));
 }
 
void test3(char *pt,size_t pt_size)
 {
  memset(pt,1,pt_size);
 }
 
void test4(char *pt,size_t pt_size)
 {
  ::fill1(pt,pt+pt_size,char(1));
 }


_TEXT    SEGMENT
_pt$ = 8                        ; size = 4
_pt_size$ = 12                        ; size = 4
?test1@@YAXPADI@Z PROC NEAR                ; test1, COMDAT

; 23   :   ::fill(pt,pt+pt_size,char(1));

    mov    eax, DWORD PTR _pt_size$[esp-4]
    push    edi
    mov    edi, DWORD PTR _pt$[esp]
    lea    ecx, DWORD PTR [edi+eax]
    cmp    edi, ecx
    je    SHORT $L13795
    sub    ecx, edi
    mov    edx, ecx
    shr    ecx, 2
    mov    eax, 16843009                ; 01010101H
    rep stosd
    mov    ecx, edx
    and    ecx, 3
    rep stosb
$L13795:
    pop    edi

; 24   :  }

    ret    0
?test1@@YAXPADI@Z ENDP                    ; test1
_TEXT    ENDS


_TEXT    SEGMENT
_pt$ = 8                        ; size = 4
_pt_size$ = 12                        ; size = 4
?test2@@YAXPADI@Z PROC NEAR                ; test2, COMDAT

; 28   :   std::fill(pt,pt+pt_size,char(1));

    mov    eax, DWORD PTR _pt_size$[esp-4]
    push    edi
    mov    edi, DWORD PTR _pt$[esp]
    lea    ecx, DWORD PTR [edi+eax]
    cmp    edi, ecx
    je    SHORT $L13808
    sub    ecx, edi
    mov    edx, ecx
    shr    ecx, 2
    mov    eax, 16843009                ; 01010101H
    rep stosd
    mov    ecx, edx
    and    ecx, 3
    rep stosb
$L13808:
    pop    edi

; 29   :  }

    ret    0
?test2@@YAXPADI@Z ENDP                    ; test2
_TEXT    ENDS


_TEXT    SEGMENT
_pt$ = 8                        ; size = 4
_pt_size$ = 12                        ; size = 4
?test3@@YAXPADI@Z PROC NEAR                ; test3, COMDAT

; 33   :   memset(pt,1,pt_size);

    mov    ecx, DWORD PTR _pt_size$[esp-4]
    mov    edx, ecx
    push    edi
    mov    edi, DWORD PTR _pt$[esp]
    shr    ecx, 2
    mov    eax, 16843009                ; 01010101H
    rep stosd
    mov    ecx, edx
    and    ecx, 3
    rep stosb
    pop    edi

; 34   :  }

    ret    0
?test3@@YAXPADI@Z ENDP                    ; test3
_TEXT    ENDS


_TEXT    SEGMENT
_pt$ = 8                        ; size = 4
_pt_size$ = 12                        ; size = 4
?test4@@YAXPADI@Z PROC NEAR                ; test4, COMDAT

; 38   :   ::fill1(pt,pt+pt_size,char(1));

    mov    eax, DWORD PTR _pt_size$[esp-4]
    push    edi
    mov    edi, DWORD PTR _pt$[esp]
    lea    ecx, DWORD PTR [edi+eax]
    cmp    edi, ecx
    je    SHORT $L13821
    sub    ecx, edi
    mov    edx, ecx
    shr    ecx, 2
    mov    eax, 16843009                ; 01010101H
    rep stosd
    mov    ecx, edx
    and    ecx, 3
    rep stosb
$L13821:
    pop    edi

; 39   :  }

    ret    0
?test4@@YAXPADI@Z ENDP                    ; test4
_TEXT    ENDS


Опять тождественный по существу код во всех примерах. А вот теперь сравним реализации fill и fill1. И это уже интересно.

_TEXT    SEGMENT
_i$ = 8                            ; size = 4
_e$ = 12                        ; size = 4
_v$ = 16                        ; size = 4
??$fill@PADD@@YAXPAD0ABD@Z PROC NEAR            ; fill<char *,char>, COMDAT

; 10   :   while(i != e) { *i = v; ++i; }

    mov    eax, DWORD PTR _i$[esp-4]
    mov    ecx, DWORD PTR _e$[esp-4]
    cmp    eax, ecx
    je    SHORT $L12548
    push    esi
    mov    esi, DWORD PTR _v$[esp]
$L12547:
    mov    dl, BYTE PTR [esi]
    mov    BYTE PTR [eax], dl
    inc    eax
    cmp    eax, ecx
    jne    SHORT $L12547
    pop    esi
$L12548:

; 11   :  }

    ret    0
??$fill@PADD@@YAXPAD0ABD@Z ENDP                ; fill<char *,char>
_TEXT    ENDS


Оба на, сюрприз! Причина -- в процессе заполнения массива может изменить своё значение переменная, величина которой используется для заполнения. Так что дальнейшая оптимизация невозможна.

_TEXT    SEGMENT
_i$ = 8                            ; size = 4
_e$ = 12                        ; size = 4
_v$ = 16                        ; size = 1
??$fill1@PADD@@YAXPAD0D@Z PROC NEAR            ; fill1<char *,char>, COMDAT

; 16   :   while(i != e) { *i = v; ++i; }

    mov    ecx, DWORD PTR _e$[esp-4]
    push    edi
    mov    edi, DWORD PTR _i$[esp]
    cmp    edi, ecx
    je    SHORT $L12555
    mov    al, BYTE PTR _v$[esp]
    push    ebx
    mov    bl, al
    mov    bh, bl
    sub    ecx, edi
    mov    edx, ecx
    shr    ecx, 2
    mov    eax, ebx
    shl    eax, 16                    ; 00000010H
    mov    ax, bx
    pop    ebx
    rep stosd
    mov    ecx, edx
    and    ecx, 3
    rep stosb
$L12555:
    pop    edi

; 17   :  }

    ret    0
??$fill1@PADD@@YAXPAD0D@Z ENDP                ; fill1<char *,char>
_TEXT    ENDS


Нормально.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[7]: inline memset
От: Шахтер Интернет  
Дата: 13.02.04 02:05
Оценка: 64 (5)
Здравствуйте, Кодт, Вы писали:

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


Ш>>Дело в том, что эти вещи семантически не эквивалентны. Так что прямое сравнение производительности тут некорректно. Причина -- в передаче FillValue по ссылке.


Ш>>Оба на, сюрприз! Причина -- в процессе заполнения массива может изменить своё значение переменная, величина которой используется для заполнения. Так что дальнейшая оптимизация невозможна.


К>Да, это действительно интересный нюанс.

К>Получается, что передача по значению приводит к одному дополнительному копированию, зато позволяет более глубоко оптимизировать.

К>А чтобы форсировать передачу по ссылке (если конструктор копирования нежелателен) — нужно воспользоваться boost::ref|cref.


Тут ведь фишка в чем. Если нужно делать fill, то тип должен быть копирабельным. Значит, в данном шаблоне использование для передачи параметра ссылки просто не оправдано. Это дефект дизайна.
Вообще, по моим наблюдениям, многие программисты забывают, что передача параметра по значению и по константной ссылке не эквивалентны. Иногда это приводит к сюрпризам.

Вот пример из моего собственного bug листа.

/* main.cpp */ 

#include <iostream>

using namespace std;

struct Complex
 {
  double re;
  double im;
  
  Complex(double re_=0.,double im_=0.) : re(re_),im(im_) {}
  
  const Complex & operator *= (const Complex &);
 };

const Complex & Complex::operator *= (const Complex &x)
 {
  double re_=re;
 
  re = re*x.re - im*x.im ;
  im = re_*x.im + im*x.re ;
  
  return *this;
 }
 
ostream & operator << (ostream &out,const Complex &x) 
 {
  out << "(" << x.re << "," << x.im << ")" ;
 
  return out;
 }
 
/* main() */ 

int main()
 {
  Complex a(2),b(2);
  
  cout << "a*=b " << (a*=b) << "\n" ;
  
  a=Complex(1,1);
  b=Complex(1,1);
  
  cout << "a*=b " << (a*=b) << "\n" ;
  
  a=Complex(1,1);
  
  cout << "a*=a " << (a*=a) << "\n" ;
  
  return 0;
 }


Вывод

a*=b (4,0)
a*=b (0,2)
a*=a (0,1)
Press any key to continue


Оба на!!!
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[4]: inline memset
От: folk Россия  
Дата: 15.02.04 02:38
Оценка: 4 (1)
Здравствуйте, lextasy, Вы писали:

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


F>>Для типов, размер которых >= sizeof(int), но требования к выравниванию менее строгие чем у int, это может привести к alignment fault.


F>>Например:


F>>
F>>char array[4];
F>>zero_fill(array);

F>>// будет эквивалентно
F>>char array[4];
F>>*reinterpret_cast<int*>(&array) = 0; // здесь возможен fault, т.к. array не обязательно будет выровнен на 4
F>>


L>В общем случае — так оно и есть...

L>Интересно только, для x86 это замечание актуально? Если да, то насколько?

Во сколько точно обойдется копирование на невыровненный адрес в x86 я не знаю, но здравый смысл подсказывает что падение производительности будет не менее чем в 2-3 раза.
Простейшее решение — вставить компайл-тайм проверку __alignof(X) >= __alignof(int), чтобы шаблон работал только с допустимыми типами.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[4]: inline memset
От: c-smile Канада http://terrainformatica.com
Дата: 11.02.04 21:50
Оценка: :)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, c-smile, Вы писали:


CS>>Павел, а std::fill разве не вызывает memset ?

CS>>Если нет то зря....

К>
К>template<class InputIter, class FillValue>
К>void fill(InputIter i, InputIter e, const FillValue& v)
К>{
К>    while(i != e) { *i = v; ++i; }
К>}
К>


Проверено на Intell 8.0: memset работает в 3-4 раза быстрее чем такой вот цикл.
То же самое и memmove memcopy для POD типов.

К>Можно, конечно, написать специализацию для случая InputIter = char*.


Можно, но не написали... Бум ждать...

К>Но не факт, что получится много быстрее. Тем более, с оптимизирующим компилятором.

К>Если твой профайлер скажет, что да, именно fill тормозит — пожалуйста, переписывай хоть на MOVSD, хоть на MMX.

На компилятор надейяся и сам не плошай, так кажется?
Re: inline memset
От: Аноним  
Дата: 12.02.04 06:21
Оценка: +1
MS>Есть подзадача:
MS> написать эквивалент
MS>
MS>memset( mem, 0, size );
MS>

MS>но без вызова функции memset.

MS>Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset",


По-моему вы занимаетесь ерундой. Любой компилятор в релизе и так должен сделать ее инлайновой.
Re[8]: inline memset
От: Анатолий Широков СССР  
Дата: 13.02.04 15:05
Оценка: +1
Это не сюрприз, это ошибка. Ведь с умножением матриц Вы бы так не поступили?
Re[11]: inline memset
От: Павел Кузнецов  
Дата: 14.02.04 11:33
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К> 2) В матричной алгебре нет оператора *= произведение имеет другую

К> размерность.

Это если матрицы не квадратные...
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
inline memset
От: MShura  
Дата: 10.02.04 13:54
Оценка:
Есть подзадача:
написать эквивалент
memset( mem, 0, size );

но без вызова функции memset.

Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset", потому как в некоторых случаях этой функции нет в подключаемых библиотеках.
Очень хочется, чтобы любой компилятор встраивал эту функцию всегда.

Варианты типа:
— самому написать memset с помощью циклов — не интересны
— написать вставку на asm не подходят, т.к. на разных компиляторах разные синтаксисы.




P.S.
memcpy(dst, src, size) легко заменяется примерно таким кодом:

*(A*)dst = *(A*)src, где A — произвольная структура размера size.

Для memset хочется примерно такой же вариант, поскольку если написать

A a = {0};


то сгенерится почти нужный код.
Re: inline memset
От: maq Россия http://www.maqdev.com
Дата: 10.02.04 14:20
Оценка:
MS>Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset", потому как в некоторых случаях этой функции нет в подключаемых библиотеках.
MS>Очень хочется, чтобы любой компилятор встраивал эту функцию всегда.
Я думаю добиться этого можно только с помощью макроса, например:

INLINE_MEMSET(PTR, VAL, SZ) \
{ int nCount_ = SZ; char* ptr_=reinterpret_cast<char*>(PTR); while(nCount>0){ *ptr_ = VAL; ptr_++; nCount_--; } }
Re: inline memset
От: Павел Кузнецов  
Дата: 10.02.04 14:22
Оценка:
Здравствуйте, MShura, Вы писали:

M>
 M> memset( mem, 0, size );
 M>

M> но без вызова функции memset.

std::fill(mem, (char*)mem + size, 0) ?
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: inline memset
От: Tonal- Россия www.promsoft.ru
Дата: 11.02.04 20:16
Оценка:
Здравствуйте, MShura, Вы писали:

MS>Есть подзадача:

MS> написать эквивалент
MS>
MS>memset( mem, 0, size );
MS>

MS>но без вызова функции memset.

Если размер известен при компиляции, пойдёт примерно такой код:
#include <iostream>

template <unsigned N> 
struct memset_t : memset_t<(N - 1)> {
  char buf;
  memset_t() : buf(0) {}
};

template <> 
struct memset_t<1> {
  char buf;
  memset_t() : buf(0) {}
};

template <unsigned N> 
inline void fill_mem(void* t) {
  *static_cast<memset_t<N>* >(t) = memset_t<N>();
}

int main() {
  double vvv = 8.9;
  fill_mem<sizeof(vvv)>(&vvv);
  std::cout<<vvv<<std::endl;
}


Здесть не учитывается выравнивание, но идея надеюсь ясна.
... << RSDN@Home 1.1.0 stable >>
Re[2]: inline memset
От: c-smile Канада http://terrainformatica.com
Дата: 11.02.04 20:44
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

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


M>>
 M>> memset( mem, 0, size );
 M>>

M>> но без вызова функции memset.

ПК>std::fill(mem, (char*)mem + size, 0) ?


Павел, а std::fill разве не вызывает memset ?
Если нет то зря....
Re: inline memset
От: alnsn Великобритания http://nasonov.blogspot.com
Дата: 11.02.04 21:27
Оценка:
MShura wrote:

> memcpy(dst, src, size) легко заменяется примерно таким кодом:

>
> (A)dst = (A)src, где A — произвольная структура размера size.
>
Пишешь много структур разного размера, затем по заданному во время
компиляции размеру подбираешь набор структур для покрытия всего массива
(тоже кстати во время компиляции).
Например, memcpy_ct<10>(dst, src) заменяется автоматически на что-то вроде
*((S4*)&dst[0]) = *((S4*)&src[0]);
*((S4*)&dst[4]) = *((S4*)&src[4]);
*((S2*)&dst[8]) = *((S2*)&src[8]);

где sizeof(S4) == 4 && sizeof(S2) == 2
--
Александр Насонов,
Независимый консультант и разработчик ПО
alnsn-mycop@yandex.ru (для более быстрого ответа удалите -мусор из адреса)
Posted via RSDN NNTP Server 1.8 beta
Re[3]: inline memset
От: Кодт Россия  
Дата: 11.02.04 21:44
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Павел, а std::fill разве не вызывает memset ?

CS>Если нет то зря....

А с чего бы? Произвольный итератор (это с равным успехом может быть какой-нибудь адаптер...)
template<class InputIter, class FillValue>
void fill(InputIter i, InputIter e, const FillValue& v)
{
    while(i != e) { *i = v; ++i; }
}

Можно, конечно, написать специализацию для случая InputIter = char*.
Но не факт, что получится много быстрее. Тем более, с оптимизирующим компилятором.
Если твой профайлер скажет, что да, именно fill тормозит — пожалуйста, переписывай хоть на MOVSD, хоть на MMX.
... << RSDN@Home 1.1.2 stable >>
Перекуём баги на фичи!
Re[2]: inline memset
От: Шахтер Интернет  
Дата: 12.02.04 03:01
Оценка:
Здравствуйте, alnsn, Вы писали:

A>MShura wrote:


>> memcpy(dst, src, size) легко заменяется примерно таким кодом:

>>
>> (A)dst = (A)src, где A — произвольная структура размера size.
>>
A>Пишешь много структур разного размера, затем по заданному во время
A>компиляции размеру подбираешь набор структур для покрытия всего массива
A>(тоже кстати во время компиляции).
A>Например, memcpy_ct<10>(dst, src) заменяется автоматически на что-то вроде
A>
A>*((S4*)&dst[0]) = *((S4*)&src[0]);
A>*((S4*)&dst[4]) = *((S4*)&src[4]);
A>*((S2*)&dst[8]) = *((S2*)&src[8]);
A>

A>где sizeof(S4) == 4 && sizeof(S2) == 2
A>--
A>Александр Насонов,
A>Независимый консультант и разработчик ПО
A>alnsn-mycop@yandex.ru (для более быстрого ответа удалите -мусор из адреса)

На современных компиляторах это не имеет смысла.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[6]: inline memset
От: c-smile Канада http://terrainformatica.com
Дата: 12.02.04 05:33
Оценка:
Здравствуйте, Шахтер, Вы писали:

Класс, спасибо!

и 7.1 хороший компайлер. правильно разбирает ситуацию на stosd и stosb.
Интересно, а что будет с невыровненными данными?

Т.е. стандартное определение
namespace std {
void fill(ForwardIterator first,
ForwardIterator last, const T&);
void fill_n(OutputIterator, Size, const T&);
}
не может быть до конца оптимизировано в голом виде для pod типов.

Хотя поддается оптимизации, например
http://www.boost.org/libs/type_traits/examples/fill_example.cpp
Re: inline memset
От: e-Xecutor Россия  
Дата: 12.02.04 07:49
Оценка:
Здравствуйте, MShura, Вы писали:

MS>Есть подзадача:

MS> написать эквивалент
MS>
MS>memset( mem, 0, size );
MS>

MS>но без вызова функции memset.

MS>Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset", потому как в некоторых случаях этой функции нет в подключаемых библиотеках.

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

MS>Варианты типа:

MS>- самому написать memset с помощью циклов — не интересны
MS>- написать вставку на asm не подходят, т.к. на разных компиляторах разные синтаксисы.




MS>P.S.

MS>memcpy(dst, src, size) легко заменяется примерно таким кодом:

MS>*(A*)dst = *(A*)src, где A — произвольная структура размера size.


MS>Для memset хочется примерно такой же вариант, поскольку если написать


MS>
MS>A a = {0};
MS>


MS>то сгенерится почти нужный код.



template <int N>
struct Filler{
  char buf[N];
};

struct A{
  int x,y,z,u,v;
};

int main(int argc,char* argv[])
{
  A a={1,2,3,4,5};
  typedef Filler<sizeof(A)> AFiller;
  AFiller f={0,};
  *((AFiller*)&a)=f;
  printf("%d,%d,%d,%d,%d\n",a.x,a.y,a.z,a.u,a.v);
  return 0;
}


типа такого?
Re[3]: inline memset
От: Павел Кузнецов  
Дата: 12.02.04 08:25
Оценка:
Здравствуйте, c-smile, Вы писали:

M>>>
 M>>> memset( mem, 0, size );
 M>>>

M>>> но без вызова функции memset.

ПК>> std::fill(mem, (char*)mem + size, 0) ?


c> Павел, а std::fill разве не вызывает memset ?


Может и вызывать, но к исходной задаче это уже не относится, т.к. автора
вопроса в memset смущало то, что "в некоторых случаях" ее "нет в подключаемых
библиотеках". В случае, если какая-то реализация std::fill вызовет
memset, можно быть вполне уверенным, что в данной поставке стандартной
библиотеки memset есть.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: inline memset
От: Павел Кузнецов  
Дата: 12.02.04 08:30
Оценка:
Здравствуйте, c-smile, Вы писали:

c> а std::fill разве не вызывает memset? Если нет то зря....


Иногда — зря, иногда — не зря: например, для маленьких массивов хорошо
оптимизированный цикл (Duff's device, танцы вокруг копирования словами etc.)
иногда быстрее, чем memset. На некоторых реализациях (например, STLport)
std::fill для встроенных типов, действительно, сводится к memset.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: inline memset
От: LaptevVV Россия  
Дата: 12.02.04 08:31
Оценка:
Здравствуйте, MShura, Вы писали:

MS>Есть подзадача:

MS> написать эквивалент
MS>
MS>memset( mem, 0, size );
MS>

MS>но без вызова функции memset.

MS>Варианты типа:

MS>- самому написать memset с помощью циклов — не интересны
MS>- написать вставку на asm не подходят, т.к. на разных компиляторах разные синтаксисы.
YНе, это самое лучшее, учитывая условия задачи. Тем более, что это делается цепочечной командой.
А с синтаксисом оператора asm можно как раз и помозговать. Хоть в командной строке задавать макрос, ( )например.
Тогда меняется командная строка, а программа-то остается без изменений.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[6]: inline memset
От: Кодт Россия  
Дата: 12.02.04 09:20
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Дело в том, что эти вещи семантически не эквивалентны. Так что прямое сравнение производительности тут некорректно. Причина -- в передаче FillValue по ссылке.


Ш>Оба на, сюрприз! Причина -- в процессе заполнения массива может изменить своё значение переменная, величина которой используется для заполнения. Так что дальнейшая оптимизация невозможна.


Да, это действительно интересный нюанс.
Получается, что передача по значению приводит к одному дополнительному копированию, зато позволяет более глубоко оптимизировать.

А чтобы форсировать передачу по ссылке (если конструктор копирования нежелателен) — нужно воспользоваться boost::ref|cref.
Перекуём баги на фичи!
Re: inline memset
От: MShura  
Дата: 12.02.04 12:18
Оценка:
Всем спасибо, особенно Шахтеру, за отклик.
Re[7]: inline memset
От: Шахтер Интернет  
Дата: 13.02.04 02:05
Оценка:
Здравствуйте, c-smile, Вы писали:

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


CS>Класс, спасибо!


CS>и 7.1 хороший компайлер. правильно разбирает ситуацию на stosd и stosb.

CS>Интересно, а что будет с невыровненными данными?

Это надо эксперементировать. Но вообще, у современных Pentium-ов внутренняя шина данных (процессор -- кэш) 128-битная, по идее, это должно скомпенсировать блочные операции с невыровненными данными.
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[8]: inline memset
От: Кодт Россия  
Дата: 13.02.04 14:44
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Оба на!!!


Долго втыкал, но когда воткнул... a*=a абзац.
Щас народу в конторе расскажу.
Перекуём баги на фичи!
Re[8]: inline memset
От: Andrew S Россия http://alchemy-lab.com
Дата: 13.02.04 16:25
Оценка:
CS>>и 7.1 хороший компайлер. правильно разбирает ситуацию на stosd и stosb.
CS>>Интересно, а что будет с невыровненными данными?

Ш>Это надо эксперементировать. Но вообще, у современных Pentium-ов внутренняя шина данных (процессор -- кэш) 128-битная, по идее, это должно скомпенсировать блочные операции с невыровненными данными.


Однако невыравненности это никак не компенсирует. Отнюдь, увеличение конвейера требует большой осторожности при выравнивании, поскольку производительность подсистемы памяти тут крайне важна. В некоторых краевых случаях можно в разы поднять быстродейстиве только за счет выравнивания.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: inline memset
От: lextasy Украина www.mira-tech.com.ua
Дата: 13.02.04 19:34
Оценка:
Здравствуйте, MShura, Вы писали:

MS>Есть подзадача:

MS> написать эквивалент
MS>
MS>memset( mem, 0, size );
MS>

MS>но без вызова функции memset.

MS>Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset", потому как в некоторых случаях этой функции нет в подключаемых библиотеках.

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

MS>Варианты типа:

MS>- самому написать memset с помощью циклов — не интересны
MS>- написать вставку на asm не подходят, т.к. на разных компиляторах разные синтаксисы.




MS>P.S.

MS>memcpy(dst, src, size) легко заменяется примерно таким кодом:

MS>*(A*)dst = *(A*)src, где A — произвольная структура размера size.


MS>Для memset хочется примерно такой же вариант, поскольку если написать


MS>
MS>A a = {0};
MS>


MS>то сгенерится почти нужный код.



Я уже давно пользуюсь такой конструкцией:

template<int N> class dword_fill_t{
public:
    static inline void fill(int* buf, const int& value)
    {
        *buf = value;
        dword_fill_t<N-1>::fill(buf + 1, value);
    }
};

template<> class dword_fill_t<0>{
public:
    static inline void fill(int* buf, const int& value){}
};

template<int N> class byte_fill_t{
public:
    static inline void fill(char* buf, const char& value)
    {
        *buf = value;
        byte_fill_t<N-1>::fill(buf + 1, value);
    }
};

template<> class byte_fill_t<0>{
public:
    static inline void fill(char* buf, const char& value){}
};

template<class X> void zero_fill(X& x)
{
    dword_fill_t<sizeof(x)/sizeof(int)>::fill((int*)&x, 0);
    byte_fill_t<sizeof(x)%sizeof(int)>::fill(((char*)&x) + sizeof(x) - sizeof(x)%sizeof(int), 0);
}


Например,

inline void clear_guid(GUID& x)
{
    zero_fill(x);
}



вызов функции clear_guid в релизе транслируется в 4 ассемблерных инструкции mov
Re[9]: inline memset
От: Шахтер Интернет  
Дата: 14.02.04 02:06
Оценка:
Здравствуйте, Анатолий Широков, Вы писали:

АШ>Это не сюрприз, это ошибка.


Ну да. По-моему, я так и написал -- bug.

АШ>Ведь с умножением матриц Вы бы так не поступили?


Честно говоря, не понял про матрицы. Как, так?
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: inline memset
От: folk Россия  
Дата: 14.02.04 04:32
Оценка:
Здравствуйте, lextasy, Вы писали:

L>Я уже давно пользуюсь такой конструкцией:


L>
L>template<int N> class dword_fill_t{
L>public:
L>    static inline void fill(int* buf, const int& value)
L>    {
L>        *buf = value;
L>        dword_fill_t<N-1>::fill(buf + 1, value);
L>    }
L>};

L>template<> class dword_fill_t<0>{
L>public:
L>    static inline void fill(int* buf, const int& value){}
L>};

L>template<int N> class byte_fill_t{
L>public:
L>    static inline void fill(char* buf, const char& value)
L>    {
L>        *buf = value;
L>        byte_fill_t<N-1>::fill(buf + 1, value);
L>    }
L>};

L>template<> class byte_fill_t<0>{
L>public:
L>    static inline void fill(char* buf, const char& value){}
L>};

L>template<class X> void zero_fill(X& x)
L>{
L>    dword_fill_t<sizeof(x)/sizeof(int)>::fill((int*)&x, 0);
L>    byte_fill_t<sizeof(x)%sizeof(int)>::fill(((char*)&x) + sizeof(x) - sizeof(x)%sizeof(int), 0);
L>}

L>


L>Например,


L>
L>inline void clear_guid(GUID& x)
L>{
L>    zero_fill(x);
L>}
L>



L>вызов функции clear_guid в релизе транслируется в 4 ассемблерных инструкции mov


Для типов, размер которых >= sizeof(int), но требования к выравниванию менее строгие чем у int, это может привести к alignment fault.

Например:

char array[4];
zero_fill(array);

// будет эквивалентно
char array[4];
*reinterpret_cast<int*>(&array) = 0; // здесь возможен fault, т.к. array не обязательно будет выровнен на 4
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[3]: inline memset
От: zrs  
Дата: 14.02.04 09:05
Оценка:
Здравствуйте, folk, Вы писали:

L>>вызов функции clear_guid в релизе транслируется в 4 ассемблерных инструкции mov


F>Для типов, размер которых >= sizeof(int), но требования к выравниванию менее строгие чем у int, это может привести к alignment fault.


F>Например:


F>
F>char array[4];
F>zero_fill(array);

F>// будет эквивалентно
F>char array[4];
F>*reinterpret_cast<int*>(&array) = 0; // здесь возможен fault, т.к. array не обязательно будет выровнен на 4
F>


Это можно решить, заполняя начало блока байтами, а потом уже интами с выравниванием, но дело в том, что данная конструкция не эквивалентна memset и предназначена для более других вещей.
... << RSDN@Home 1.1.3 beta 1 >>
Re[10]: inline memset
От: Кодт Россия  
Дата: 14.02.04 09:32
Оценка:
Здравствуйте, Шахтер, Вы писали:

АШ>>Ведь с умножением матриц Вы бы так не поступили?


Ш>Честно говоря, не понял про матрицы. Как, так?


Вот как раз с матрицами я бы
1) передавал по ссылке, и только по ссылке (слишком велик оригинал). Внутри функции заводил бы локальные переменные и как-то учитывал, что на вход может придти *this.

2) В матричной алгебре нет оператора *= произведение имеет другую размерность. Хотя если матрицы с динамическим размером, то пуркуа бы и не па...
... << RSDN@Home 1.1.2 stable >>
Перекуём баги на фичи!
Re[10]: inline memset
От: Анатолий Широков СССР  
Дата: 14.02.04 10:10
Оценка:
Здравствуйте, Шахтер, Вы писали:

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


АШ>>Это не сюрприз, это ошибка.


Ш>Ну да. По-моему, я так и написал -- bug.


АШ>>Ведь с умножением матриц Вы бы так не поступили?


Ш>Честно говоря, не понял про матрицы. Как, так?


Я имел ввиду это:

...
    //    композиция преобразований
    matrix& operator*= (const matrix &rht)
    {
        matrix temp = {0};

                matrix &lft = (*this);

        for(size_t i = 0; i<4; i++)
        for(size_t j = 0; j<4; j++)
        for(size_t k = 0; k<4; k++)    
            temp[i][j] +=  lft[i][k] * rht[k][j];
                
                lft = temp;

                return (*this);
    } // operator *=..


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

m*=m;

не приводил бы к "удивительному" результату.
Re[3]: inline memset
От: lextasy Украина www.mira-tech.com.ua
Дата: 14.02.04 19:27
Оценка:
Здравствуйте, folk, Вы писали:

F>Для типов, размер которых >= sizeof(int), но требования к выравниванию менее строгие чем у int, это может привести к alignment fault.


F>Например:


F>
F>char array[4];
F>zero_fill(array);

F>// будет эквивалентно
F>char array[4];
F>*reinterpret_cast<int*>(&array) = 0; // здесь возможен fault, т.к. array не обязательно будет выровнен на 4
F>


В общем случае — так оно и есть...
Интересно только, для x86 это замечание актуально? Если да, то насколько?
Re[4]: inline memset
От: lextasy Украина www.mira-tech.com.ua
Дата: 14.02.04 19:30
Оценка:
Здравствуйте, zrs, Вы писали:

zrs> дело в том, что данная конструкция не эквивалентна memset и предназначена для более других вещей.


Для более каких вещей?
Re[5]: inline memset
От: lextasy Украина www.mira-tech.com.ua
Дата: 15.02.04 15:53
Оценка:
Здравствуйте, folk, Вы писали:

F>Простейшее решение — вставить компайл-тайм проверку __alignof(X) >= __alignof(int), чтобы шаблон работал только с допустимыми типами.


Большое спасибо за действительно ценный совет!

Удалено избыточное цитирование. -- ПК.
Re[8]: inline memset
От: e-Xecutor Россия  
Дата: 16.02.04 08:06
Оценка:
Здравствуйте, Шахтер, Вы писали:

А вот так:
const Complex & Complex::operator *= (const Complex &x)
 {
  const Complex& tmp=this==&x?Complex(x):x;
  double re_=re;

  re = re*tmp.re - im*tmp.im ;
  im = re_*tmp.im + im*tmp.re ;

  return *this;
 }



Re[5]: inline memset
От: zrs  
Дата: 16.02.04 08:18
Оценка:
Здравствуйте, lextasy, Вы писали:

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


zrs>> дело в том, что данная конструкция не эквивалентна memset и предназначена для более других вещей.


L>Для более каких вещей?


Для вещей, размер которых известен в compile time и не слишком большой. Оговорюсь, сам ничего против такого подхода не имею и даже использую что то подобное.
... << RSDN@Home 1.1.3 beta 1 >>
Re[9]: inline memset
От: e-Xecutor Россия  
Дата: 16.02.04 08:40
Оценка:
Здравствуйте, e-Xecutor, Вы писали:

Мнда.
В точности как задумывалось этот код работает только
будучи откомпилённым борландом 5.5
Вообще работает под cl 6,7.1, g++ 3.2.3 и CC: Sun C++ 5.5 2003/03/12.
Под icl 7.1 не работает. Темпорал убивается сразу же...
Re[10]: inline memset
От: Шахтер Интернет  
Дата: 16.02.04 23:32
Оценка:
Здравствуйте, e-Xecutor, Вы писали:

EX>Здравствуйте, e-Xecutor, Вы писали:


EX>Мнда.

EX>В точности как задумывалось этот код работает только
EX>будучи откомпилённым борландом 5.5
EX>Вообще работает под cl 6,7.1, g++ 3.2.3 и CC: Sun C++ 5.5 2003/03/12.
EX>Под icl 7.1 не работает. Темпорал убивается сразу же...

const Complex & Complex::operator *= (const Complex &x)
 {
  double re_ = re*x.re - im*x.im ;
  double im_ = re*x.im + im*x.re ;
  
  re=re_;
  im=im_;
  
  return *this;
 }
... << RSDN@Home 1.1.0 stable >>
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[11]: inline memset
От: e-Xecutor Россия  
Дата: 17.02.04 07:31
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, e-Xecutor, Вы писали:


EX>>Здравствуйте, e-Xecutor, Вы писали:


EX>>Мнда.

EX>>В точности как задумывалось этот код работает только
EX>>будучи откомпилённым борландом 5.5
EX>>Вообще работает под cl 6,7.1, g++ 3.2.3 и CC: Sun C++ 5.5 2003/03/12.
EX>>Под icl 7.1 не работает. Темпорал убивается сразу же...

В данном конкретном случае всё понятно
Я ж про общий.
Жаль, такая фитча пропала...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.