memset, 64 бита

dimi61

Есть ли быстрый 64-битный мемсет? Вообще, обычный memset — он быстрый? Как он реализован?

AlexV769

64-битный мемсет
SYNOPSIS
#include <string.h>

void *
memset(void *b, int c, size_t len);


Мьсе, причем тут битность и битность чего?

Maurog

на разных компиляторах сделан по-разному
перформанс плавает и его можно дотачивать под конкретную железку
http://www.hpc.ru/pda/board/index.php?t=77292

dimi61

битность значения, пишущегося в буфер. memset пишет 8 бит, я бы хотел 64.
Мне кажется, такое понимание естественно, и гугль со мной согласен. К сожалению, ответ на свой вопрос я в гугле не нашел, но обнаружил, что сам вопрос задаю не только я.

dimi61

ясно, спасибо

tokuchu

Вообще, обычный memset — он быстрый? Как он реализован?
Я думаю, что по данному вопросу нужно использовать source в качестве documentation. :)

caffeine

Ты хотел, например, массив double инициализировать каким-нибудь числом? :)
Лично я на практике получал то, что memset и memcpy работал медленнее чем обычный цикл. Производительность (по времени) падала на 1-2%. К тому же если включить параметр компиляции -funroll-loops или самому ручками развернуть цикл (т.к. gcc хоть и крут, но двойные \ тройные циклы он разворачивает не очень то оно будет весьма неплохо работать.

dimi61

Да, задача именно такая. Спасибо :)

geja_03

Ну возьми какую-нибудь реализацию и посмотри.
EXTERN_C void * __cdecl memset(void *d, int v, size_t c)
{
if ADDRESS) d) | c) & (sizeof(UINT) - 1 {

BYTE *pD = (BYTE *) d;
BYTE *pE = (BYTE *) ADDRESS) d) + c);

while (pD != pE)
*(pD++) = (BYTE) v;

}
else {

UINT *pD = (UINT *) d;
UINT *pE = (UINT *) (BYTE *) ADDRESS) d) + c);
UINT uv;

uv = UINT) (v & 0xff | UINT) (v & 0xff << 8);

/* Our processors are at least 32 bits
*/
uv |= uv << 16;

#if (_UINTSIZE == 64)
/* They might be 64 bits
*/
uv |= uv << 32;
#endif

while (pD != pE)
*(pD++) = uv;
}
return d;
}

Если адрас не выровнен или размер неудачный, то шлепается по байту, если выровнен, то готовится UINT значение и шлепается по 4\8 байт в зависимости от архитектуры.

vall

это где UINT 64 битный? :shocked:

geja_03

Видимо где-то есть, я первую попавшуюся реализацию схватил )
UPD: Ох это какая-то экспериментальная ерь от мелкомягких...

Ivan8209

> это где UINT 64 битный?
Например, на NetBSD/amd64, NetBSD/sparc64, NetBSD/mips64, NetBSD/powerpc64.
---
"NetBSD is JIHBED!"

Ivan8209

> memset пишет 8 бит
Это неправда, и простой взгляд на исходные тексты libc
прекрасно показывает это.
---
"Расширь своё сознание!"

caffeine

> memset пишет 8 бит
Это неправда, и простой взгляд на исходные тексты libc
прекрасно показывает это.
MEMSET(3) Linux Programmer's Manual MEMSET(3)
NAME
memset - fill memory with a constant byte
SYNOPSIS
#include <string.h>
void *memset(void *s, int c, size_t n);
DESCRIPTION
The memset function fills the first n bytes of the memory area pointed to by s with the constant byte c.
RETURN VALUE
The memset function returns a pointer to the memory area s.
CONFORMING TO
SVr4, 4.3BSD, C89, C99, POSIX.1-2001.
SEE ALSO
bzero(3 swab(3 wmemset(3)
COLOPHON
This page is part of release 3.21 of the Linux man-pages project. A description of the project, and information about reporting bugs, can be found at http://www.ker-
nel.org/doc/man-pages/.
GNU 1993-04-11 MEMSET(3)
PS Мое скромное мнение: 1 байт есть 8 бит.

alfadred

     The memset function fills the first n bytes of the memory area pointed to by s with the constant byte c.
Это как-то мешает в реализации писать по 32 или 64 бита?

Ivan8209

Ты не понял вопрос.
> PS Мое скромное мнение: 1 байт есть 8 бит.
Оставайся при своём скромном мнении.
---
"Математик может говорить, что ему хочется,
но физик должен, хотя бы в какой-то мере, быть в здравом рассудке."

caffeine

Нет, никак не мешает. Но там написано что делает memset.
Чего чего я не понял?
Ну, приведи пример кода, инициализирующий массив int значениями 257 с помощью memset.
Автору, кстати, можешь воспользоваться fill из STL (<algorithm>).

Gunsleader


/*
FUNCTION
<<memset>>---set an area of memory

INDEX
memset

ANSI_SYNOPSIS
#include <string.h>
void *memset(const void *<[dst]>, int <[c]>, size_t <[length]>);

TRAD_SYNOPSIS
#include <string.h>
void *memset(<[dst]>, <[c]>, <[length]>)
void *<[dst]>;
int <[c]>;
size_t <[length]>;

DESCRIPTION
This function converts the argument <[c]> into an unsigned
char and fills the first <[length]> characters of the array
pointed to by <[dst]> to the value.

RETURNS
<<memset>> returns the value of <[m]>.

PORTABILITY
<<memset>> is ANSI C.

<<memset>> requires no supporting OS subroutines.

QUICKREF
memset ansi pure
*/

#include <string.h>

#define LBLOCKSIZE (sizeof(long
#define UNALIGNED(X) long)X & (LBLOCKSIZE - 1
#define TOO_SMALL(LEN) LEN) < LBLOCKSIZE)

_PTR
_DEFUN (memset, (m, c, n
_PTR m _AND
int c _AND
size_t n)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
char *s = (char *) m;

while (n-- != 0)
{
*s++ = (char) c;
}

return m;
#else
char *s = (char *) m;
int i;
unsigned long buffer;
unsigned long *aligned_addr;

if (!TOO_SMALL (n) && !UNALIGNED (m
{
/* If we get this far, we know that n is large and m is word-aligned. */

aligned_addr = (unsigned long*)m;

/* Store C into each char sized location in BUFFER so that
we can set large blocks quickly. */
c &= 0xff;
if (LBLOCKSIZE == 4)
{
buffer = (c << 8) | c;
buffer |= (buffer << 16);
}
else
{
buffer = 0;
for (i = 0; i < LBLOCKSIZE; i++)
buffer = (buffer << 8) | c;
}

while (n >= LBLOCKSIZE*4)
{
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
n -= 4*LBLOCKSIZE;
}

while (n >= LBLOCKSIZE)
{
*aligned_addr++ = buffer;
n -= LBLOCKSIZE;
}
/* Pick up the remainder with a bytewise loop. */
s = (char*)aligned_addr;
}

while (n--)
{
*s++ = (char)c;
}

return m;
#endif /* not PREFER_SIZE_OVER_SPEED */
}

Только хвостик чарами добивает...

kokoc88

Чего чего я не понял?
Вопроса.

Ivan8209

> Чего чего я не понял?
> Ну, приведи пример кода, инициализирующий массив int
> значениями 257 с помощью memset.
Вопрос не про то, как это сделать при помощи memset,
а про реализацию.
> Автору, кстати, можешь воспользоваться fill из STL
Если человек спрашивает про memset, то это не случайно.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

caffeine

Вопрос не про то, как это сделать при помощи memset,
а про реализацию.
Ну тебя я не понимаю, а первоначальный вопрос я понял. Автор спрашивал можно ли заполнить память 64 битными "неделимыми" структурами, а вы мне, как я понимаю, говорите про то, что memset сдвигается по 32 (64) бита по памяти, но при этом заполняет все эти 32 (64) бита одним значением (unsigned char) c, используя побитовый сдвиг. Что есть скорее оптимизация "8-битной записи", нежели "не 8-битность записи".
Если человек спрашивает про memset, то это не случайно.
Не случайно конечно, но я видел многих, кто просто-напросто ничего не знает об stl алгоритмах, а они порой полезны.

dimi61

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

Maurog

void *memset(void *s, int c, size_t n);
объясните мне, почему второй аргумент имеет тип int, а не unsigned char ?

Ivan8209

> объясните мне, почему второй аргумент имеет тип int, а не unsigned char?
Hysterical raisins?
---
SINT VT SVNT AVT NON SINT

Maurog

при чем тут изюм?:)
так исторически сложилось? то есть когда ввели эту функцию она была корректной (копировала целыми интами а со временем стала некорректной (стали по-байтно копировать?) или изначально наглючили в стандарте Си?
update: судя по стандарту Си глюк был внедрен при рождении языка :grin:

tokuchu

update: судя по стандарту Си глюк был внедрен при рождении языка :grin:
Это не глюк, а "фича" C. Передача парамера char не отличается от передачи параметра int.

Maurog

вот спасибо, зеленый
а если не отличается, то почему не сделали char? =) чтобы не было неявной конверсии в int ? типа у нас супер оптимизация в сях

tokuchu

вот спасибо, зеленый
а если не отличается, то почему не сделали char? =) чтобы не было неявной конверсии в int ? типа у нас супер оптимизация в сях
Так конверсия в любом случае будет. Параметры char передаются в функцию int-ами.

tokuchu

Ну и до кучи скажу, что это любого типа меньше int касается. Получишь в результате int.
И так же float нет смысла делать, т.к. он "проапгрейдится" до double.

Vlad77

И так же float нет смысла делать, т.к. он "проапгрейдится" до double.
на powerpc тоже проапгрейдится?

procenkotanya

на powerpc другой стандарт Си должен быть?
если не заглядывать в букву стандарта, на апгрейд float->double неиллюзорно намекает отсутствие отдельного спецификатора для float'ов в printf

Andbar

На первом курсе у нас на информатике использовался ваткомовский компилятор и так если писать %f вместо %lf, вывод будет неправильный. Но скорее всего просто компилятор работал не по стандарту.

caffeine

какбе реализация компилятора очень сильно зависит от процессора.
А все эти *фичи языка Си* скорее зависят от процессора.

Vlad77

когда берёшь параметры из памяти, там уже самому следить надо, процессор не помогает
(да, float-double в контексте va_args припоминается)

Serab

на powerpc тоже проапгрейдится?
только что вот проапгрейдилось.

test1.c:
void f(float);

int main
{
     const float y = 28.18;
     f(y);
     return 0;
}

test2.c:
#include <stdio.h>
void f(double x)
{
     printf( "%f\n", x );
}

Maurog

ух магия
отчего слинковалось?
разве float и double не разные типы?

Ivan8209

> разве float и double не разные типы?
А они обязаны быть разными?
---
...Я работаю антинаучным аферистом...

Serab

ботаем C vs C++ mangling

Serab

Разными — обязаны. Иметь разный размер или внутреннее представление — нет. Т.е. в C++ это линковаться не должно.

Maurog

в стандарте нет манглинга
ссылочку хочу на стандарт =)

Serab

Короче, магнлинг С — это вещь очень определенная, потому что появлялась параллельно с фортраном и задача там ставилась только не пересекаться с фортраном.
Ну и еще в С нет перегрузки, поэтому ему продвинутый манглинг не нужен. Там для __cdecl достаточно совпадения имен функций, для __stdcall и __fastcall — имен и суммарного размера параметров (с учетом округлений и выравниваний, короче полное смещение ESP за счет параметров, еще точнее: что написать в ADD ESP,<X> вместо <X> после возврата из функции).

Maurog

благодарю
вот поковырял драфтик:
3.3.2.2 Function calls
....
If the expression that denotes the called function has a type that
does not include a prototype, the integral promotions are performed on
each argument and arguments that have type float are promoted to
double. These are called the default argument promotions. If the
number of arguments does not agree with the number of parameters, the
behavior is undefined. If the function is defined with a type that
does not include a prototype, and the types of the arguments after
promotion are not compatible with those of the parameters after
promotion, the behavior is undefined. If the function is defined with
a type that includes a prototype, and the types of the arguments after
promotion are not compatible with the types of the parameters, or if
the prototype ends with an ellipsis ( ", ..." the behavior is
undefined.
If the expression that denotes the called function has a type that
includes a prototype, the arguments are implicitly converted, as if by
assignment, to the types of the corresponding parameters. The
ellipsis notation in a function prototype declarator causes argument
type conversion to stop after the last declared parameter. The
default argument promotions are performed on trailing arguments. If
the function is defined with a type that is not compatible with the
type (of the expression) pointed to by the expression that denotes the
called function, the behavior is undefined.
но не очень понял, это то или не то :grin:

Serab

А, ты про это стандарт спрашивал. Ну, я не гуру стандарта, надо будет распечатать в толчок как-нибудь :)

procenkotanya

потому что появлялась параллельно с фортраном и задача там ставилась только не пересекаться с фортраном.
откуда это известно? и в чём польза от "не пересечения"?

Serab

и в чём польза от "не пересечения"?
Ну как, по-моему очевидно: чтобы можно было не заморачиваясь использовать оба языка. Например, чтобы не добавлять требования к программам на фортране не иметь функций с именем "main".
откуда это известно?
Читал в статье про устройство и эволюцию линкеров. К сожалению, ссылку найти не могу.

Maurog

и в чём польза от "не пересечения"?
начали бы влинковывать фортан в сишные проги и в новом стандарте c++0x пришлось бы его поддерживать (слава обратной совместимости) :grin:

procenkotanya

Ну как, по-моему очевидно: чтобы можно было не заморачиваясь использовать оба языка. Например, чтобы не добавлять требования к программам на фортране не иметь функций с именем "main".
а в чём проблема в программах на фортране иметь main? Сишные библиотеки, которые захочется линковать в этот фортран, не будут иметь main, очевидно.
> не заморачиваясь
не-не-не. сейчас, чтобы нормально линковать c-fortran, нужно в Си добавлять подчёркивания к прототипам. Разве это "не заморачиваясь"? А был бы одинаковый мэнглинг, извращаться бы не пришлось.

Serab

а в чём проблема в программах на фортране иметь main? Сишные библиотеки, которые захочется линковать в этот фортран, не будут иметь main, очевидно.
Ну а если наоборот, линковать фортрановские либы в C?
> не заморачиваясь
не-не-не. сейчас, чтобы нормально линковать c-fortran, нужно в Си добавлять подчёркивания к прототипам. Разве это "не заморачиваясь"? А был бы одинаковый мэнглинг, извращаться бы не пришлось.
Изначально не было мэнглинга, его потом добавили как раз, чтобы пространства имен C и фортрана не пересекались. И как раз имена с подчеркиваниями были зарезервированы для фортрана.
А "тогда" еще было и очень весомое ограничение на длину декорированного имени (что-то около 8ми символов что позволяло в итоге звать из фортрана фактически только функции не длинее 6 символов в имени.
А так можно по идее ожидать чего-нибудь типа extern "FORTRAN" :)

Serab

Вот, кстати, нашел одну из статеек.
http://www.iecc.com/linker/linker05.html
Параграф "Name Mangling".

procenkotanya

> Ну а если наоборот, линковать фортрановские либы в C?
С функцией main? Тогда нужно библиотеки после объектных файлов указывать (что соответствует обычной практике) и main из сишного объектного файла победит.
Я не вижу тут ничего фортран-специфичного. Точно так же в программе на Си не должно быть externally-visible функций с именами, совпадающими с именами функций из линкующихся написанных на Си библиотек.
В слинкованной статье я сходу не вижу рационального объяснения появления такого_ фортрановского мэнглинга. Вот это:
As a particularly egregious example, this Fortran program would for quite a few years crash an OS/360 system:
CALL MAIN
END
вообще абзац. Вместо того, чтобы пофиксить ОС и дать программистам стрелять себе в ногу, изобрели какое-то извращение. Точно так же в наши дни вызов __libc_start_main из пользовательского кода вызывает бесконечную рекурсию, и кого это парит?

Serab

Ну ты сравнил, конечно,
MAIN
и
__libc_start_main
Второе вообще как-то боязно вызвать по определению.

Serab

Я не вижу тут ничего фортран-специфичного. Точно так же в программе на Си не должно быть externally-visible функций с именами, совпадающими с именами функций из линкующихся написанных на Си библиотек.
Ну в статье было написано именно про стандартные библиотеки языков, которые разрабатывались изначально некоторое время независимо, и похоже, что у людей все-таки были причины сократить эффективную длину имени функции до 7 и 6ти для C и фортрана соответственно.

Serab

Ну и еще там написано про альтернативный подход.

procenkotanya

Ну ты сравнил, конечно,
Ну и переделали бы рантайм фортрана, чтобы он начинался не с MAIN, а с __libfortran_start_main, было бы одинаково боязно =)

Serab

MAIN — функция из C, ее в рантайме фортрана и так нет.

procenkotanya

Если в те далёкие времена MAIN не было частью рантайма фортрана, программа
CALL MAIN
END
вообще не линковалась бы. Или я неправильно понял тот пассаж?

Serab

А, блин. Это я виноват, давно не читал статью. Там написано, что она как раз была фортрановская. Извиняюсь за запутывание. Сейчас перечитаю вопрос :)

Serab

Ну и переделали бы рантайм фортрана, чтобы он начинался не с MAIN, а с __libfortran_start_main, было бы одинаково боязно =)
Ну типа так и сделали, просто замутили декорирование. Вроде я понял, там декорировали именно из-за специальных функций фортрана, которые лежали в библиотечках и явно в коде на фортране нигде не встречались. Их приходилось обходить как фортрановцам, так и сишникам.

evolet


void *memset(void *s, int c, size_t n);
объясните мне, почему второй аргумент имеет тип int, а не unsigned char ?
насколько я понимаю, это фишка libc, связанная с EOF etc, т.е. идеей возврата кода ошибки, для возврата которой надо больше битов (а остальных местах сделали для единообразия , что имхо правильно)

Serab

ну и какую ошибку ты хочешь вернуть memset'у?

vall

EMORON

yolki

для флоатов - %f, для даблов - %lf.
gcc 4.3, watcom 1.8

salamander

gcc-4.3.2 + libc-2.7 на Debian дают следующее:
    printf("%f\n", x);
        movss   -4(%rbp %xmm0
cvtps2pd %xmm0, %xmm0
movl $.LC1, %edi
movl $1, %eax
call printf

Компилировал с -O0 для пущей наглядности.
Сие намекает нам на то, что %f все же соответствует типу double (как и написано в пункте 8 параграфа 7.19.6.1 стандарта ISO9899-C99; то же самое написано и в манах по printf а ты сделал ошибочное утверждение.

yolki

сие намекает, что вещественная арифметика, проводимая на MMX регистрах или регистрах сопроцессора, приводится к размерам регистров, которые 80 бит = long double.
сие также намекает, что float несоответствует double, ибо в соответствие его приводит инструкция cvtps2pd

salamander

сие намекает, что вещественная арифметика, проводимая на MMX регистрах или регистрах сопроцессора, приводится к размерам регистров, которые 80 бит = long double.
xmm0 - SSE регистр
xmm0 - 128-битный регистр
xmm0 может хранить как несколько 32-битных, так и несколько 64 битных вещественных чисел, но не 80- и не 128-битные.
сие также намекает, что float несоответствует double, ибо в соответствие его приводит инструкция cvtps2pd

Конечно, не соответствует.
Конечно, приводится инструкцией cvtps2pd
Потому что в функцию-то он передается как double.
О чем и говорили выше, но ты начал это опровергать, делая неверные утверждения в процессе.

Vlad77

на MMX регистрах ... приводится к размерам регистров, которые 80 бит
Айси на тебя нет. MMX регистры конечно занимают младшие 64 бита регистров сопроцессора, но преобразования до 80 бит не происходит iirc

yolki

размер регистров относился ко второй части предложения, а именно - к FPU.

rosali

> то готовится UINT значение и шлепается по 4\8 байт
UINT то шлепается, но у него все байты одинаковые, а нужно чтобы были разные.

geja_03

UINT то шлепается, но у него все байты одинаковые, а нужно чтобы были разные.
Ну суть примерно в том, что мемсет в любом случае сделает тот же цикл, что и у топикстартера )
Оставить комментарий
Имя или ник:
Комментарий: