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[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[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[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
От: Аноним  
Дата: 12.02.04 06:21
Оценка: +1
MS>Есть подзадача:
MS> написать эквивалент
MS>
MS>memset( mem, 0, size );
MS>

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

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


По-моему вы занимаетесь ерундой. Любой компилятор в релизе и так должен сделать ее инлайновой.
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[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. Парадоксы
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.