Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset", потому как в некоторых случаях этой функции нет в подключаемых библиотеках.
Очень хочется, чтобы любой компилятор встраивал эту функцию всегда.
Варианты типа:
— самому написать memset с помощью циклов — не интересны
— написать вставку на asm не подходят, т.к. на разных компиляторах разные синтаксисы.
P.S.
memcpy(dst, src, size) легко заменяется примерно таким кодом:
*(A*)dst = *(A*)src, где A — произвольная структура размера size.
Для memset хочется примерно такой же вариант, поскольку если написать
MS>Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset", потому как в некоторых случаях этой функции нет в подключаемых библиотеках. MS>Очень хочется, чтобы любой компилятор встраивал эту функцию всегда.
Я думаю добиться этого можно только с помощью макроса, например:
MShura wrote:
> memcpy(dst, src, size) легко заменяется примерно таким кодом: > > (A)dst = (A)src, где A — произвольная структура размера size. >
Пишешь много структур разного размера, затем по заданному во время
компиляции размеру подбираешь набор структур для покрытия всего массива
(тоже кстати во время компиляции).
Например, memcpy_ct<10>(dst, src) заменяется автоматически на что-то вроде
где sizeof(S4) == 4 && sizeof(S2) == 2
--
Александр Насонов,
Независимый консультант и разработчик ПО
alnsn-mycop@yandex.ru (для более быстрого ответа удалите -мусор из адреса)
Здравствуйте, 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.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, 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.
На компилятор надейяся и сам не плошай, так кажется?
Здравствуйте, 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.
Оба на, сюрприз! Причина -- в процессе заполнения массива может изменить своё значение переменная, величина которой используется для заполнения. Так что дальнейшая оптимизация невозможна.
_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
Здравствуйте, 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 (для более быстрого ответа удалите -мусор из адреса)
и 7.1 хороший компайлер. правильно разбирает ситуацию на stosd и stosb.
Интересно, а что будет с невыровненными данными?
Т.е. стандартное определение
namespace std {
void fill(ForwardIterator first,
ForwardIterator last, const T&);
void fill_n(OutputIterator, Size, const T&);
}
не может быть до конца оптимизировано в голом виде для pod типов.
MS>но без вызова функции memset.
MS>Ограничение в том, чтобы при использовании любого компилятора, с любыми ключами, не было вызова внешней функции "memset",
По-моему вы занимаетесь ерундой. Любой компилятор в релизе и так должен сделать ее инлайновой.
Здравствуйте, 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;
}
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"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, c-smile, Вы писали:
c> а std::fill разве не вызывает memset? Если нет то зря....
Иногда — зря, иногда — не зря: например, для маленьких массивов хорошо
оптимизированный цикл (Duff's device, танцы вокруг копирования словами etc.)
иногда быстрее, чем memset. На некоторых реализациях (например, STLport)
std::fill для встроенных типов, действительно, сводится к memset.
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, MShura, Вы писали:
MS>Есть подзадача: MS> написать эквивалент MS>
MS>memset( mem, 0, size );
MS>
MS>но без вызова функции memset.
MS>Варианты типа: MS>- самому написать memset с помощью циклов — не интересны MS>- написать вставку на asm не подходят, т.к. на разных компиляторах разные синтаксисы.
YНе, это самое лучшее, учитывая условия задачи. Тем более, что это делается цепочечной командой.
А с синтаксисом оператора asm можно как раз и помозговать. Хоть в командной строке задавать макрос, ( )например.
Тогда меняется командная строка, а программа-то остается без изменений.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Шахтер, Вы писали:
Ш>Дело в том, что эти вещи семантически не эквивалентны. Так что прямое сравнение производительности тут некорректно. Причина -- в передаче FillValue по ссылке.
Ш>Оба на, сюрприз! Причина -- в процессе заполнения массива может изменить своё значение переменная, величина которой используется для заполнения. Так что дальнейшая оптимизация невозможна.
Да, это действительно интересный нюанс.
Получается, что передача по значению приводит к одному дополнительному копированию, зато позволяет более глубоко оптимизировать.
А чтобы форсировать передачу по ссылке (если конструктор копирования нежелателен) — нужно воспользоваться boost::ref|cref.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Шахтер, Вы писали:
CS>Класс, спасибо!
CS>и 7.1 хороший компайлер. правильно разбирает ситуацию на stosd и stosb. CS>Интересно, а что будет с невыровненными данными?
Это надо эксперементировать. Но вообще, у современных Pentium-ов внутренняя шина данных (процессор -- кэш) 128-битная, по идее, это должно скомпенсировать блочные операции с невыровненными данными.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Шахтер, Вы писали:
Ш>>Дело в том, что эти вещи семантически не эквивалентны. Так что прямое сравнение производительности тут некорректно. Причина -- в передаче FillValue по ссылке.
Ш>>Оба на, сюрприз! Причина -- в процессе заполнения массива может изменить своё значение переменная, величина которой используется для заполнения. Так что дальнейшая оптимизация невозможна.
К>Да, это действительно интересный нюанс. К>Получается, что передача по значению приводит к одному дополнительному копированию, зато позволяет более глубоко оптимизировать.
К>А чтобы форсировать передачу по ссылке (если конструктор копирования нежелателен) — нужно воспользоваться boost::ref|cref.
Тут ведь фишка в чем. Если нужно делать fill, то тип должен быть копирабельным. Значит, в данном шаблоне использование для передачи параметра ссылки просто не оправдано. Это дефект дизайна.
Вообще, по моим наблюдениям, многие программисты забывают, что передача параметра по значению и по константной ссылке не эквивалентны. Иногда это приводит к сюрпризам.