que - puts c
¿Hay memset() que acepta números enteros más grandes que char? (8)
No hay una función de biblioteca estándar afaik. Entonces, si estás escribiendo código portátil, estás viendo un bucle.
Si está escribiendo un código no portátil, consulte la documentación de su compilador / plataforma, pero no contenga la respiración porque es raro obtener mucha ayuda aquí. Tal vez alguien más se involucrará con ejemplos de plataformas que proporcionan algo.
La forma en que escribiría la suya depende de si puede definir en la API que la persona que llama garantiza que el puntero dst estará lo suficientemente alineado para escrituras de 64 bits en su plataforma (o plataformas si es portátil). En cualquier plataforma que tenga un tipo entero de 64 bits, malloc al menos devolverá punteros adecuadamente alineados.
Si tiene que lidiar con la no alineación, entonces necesita algo como la respuesta de la sombra de luna. El compilador puede alinear / desenrollar esa memcpy con un tamaño de 8 (y usar operaciones de escritura no alineadas de 32 o 64 bits, si es que existen), por lo que el código debería ser bastante nippy, pero creo que probablemente no sea un caso especial toda la función para el destino está alineado. Me gustaría que me corrijan, pero temo que no lo seré.
Entonces, si sabe que la persona que llama siempre le dará un dst con una alineación suficiente para su arquitectura, y una longitud que es un múltiplo de 8 bytes, entonces haga un bucle simple escribiendo uint64_t (o lo que sea el int de 64 bits en su compilador) y probablemente (sin promesas) termine con un código más rápido. Seguramente tendrás un código más corto.
En cualquier caso, si le importa el rendimiento, perfilelo. Si no es lo suficientemente rápido, inténtelo de nuevo con más optimización. Si todavía no es lo suficientemente rápido, formule una pregunta sobre una versión de ASM para la (s) CPU (s) en las que no es lo suficientemente rápido. memcpy / memset puede obtener aumentos de rendimiento masivos de la optimización por plataforma.
¿Hay una versión de memset () que establece un valor que es más grande que 1 byte (char)? Por ejemplo, digamos que tenemos una función memset32 (), así que usándola podemos hacer lo siguiente:
int32_t array[10];
memset32(array, 0xDEADBEEF, sizeof(array));
Esto establecerá el valor 0xDEADBEEF en todos los elementos de la matriz. Actualmente me parece que esto solo se puede hacer con un bucle.
Específicamente, estoy interesado en una versión de 64 bits de memset (). ¿Sabes algo así?
Realmente debería dejar que el compilador optimice esto para usted como sugirió alguien más. En la mayoría de los casos, ese ciclo será insignificante.
Pero si esta es una situación especial y no le importa ser específico de la plataforma, y realmente necesita deshacerse del bucle, puede hacerlo en un bloque de ensamblaje.
//pseudo code
asm
{
rep stosq ...
}
Probablemente puedas google el comando de ensamblaje de stosq para los detalles. No debería ser más que unas pocas líneas de código.
Si solo está apuntando a un compilador x86, podría intentar algo como (ejemplo de VC ++):
inline void memset32(void *buf, uint32_t n, int32_t c)
{
__asm {
mov ecx, n
mov eax, c
mov edi, buf
rep stosd
}
}
De lo contrario, simplemente haga un bucle simple y confíe en el optimizador para saber qué está haciendo, algo así como:
for(uint32_t i = 0;i < n;i++)
{
((int_32 *)buf)[i] = c;
}
Si lo complica, es probable que resulte más lento que simple para optimizar el código, sin mencionar que es más difícil de mantener.
Solo para el registro, lo siguiente usa memcpy(..)
en el siguiente patrón. Supongamos que queremos llenar una matriz con 20 enteros:
--------------------
First copy one:
N-------------------
Then copy it to the neighbour:
NN------------------
Then copy them to make four:
NNNN----------------
And so on:
NNNNNNNN------------
NNNNNNNNNNNNNNNN----
Then copy enough to fill the array:
NNNNNNNNNNNNNNNNNNNN
Esto toma O (lg (num)) aplicaciones de memcpy(..)
.
int *memset_int(int *ptr, int value, size_t num) {
if (num < 1) return ptr;
memcpy(ptr, &value, sizeof(int));
size_t start = 1, step = 1;
for ( ; start + step <= num; start += step, step *= 2)
memcpy(ptr + start, ptr, sizeof(int) * step);
if (start < num)
memcpy(ptr + start, ptr, sizeof(int) * (num - start));
return ptr;
}
Pensé que podría ser más rápido que un bucle si memcpy(..)
se optimizó usando alguna funcionalidad de copia de memoria de bloque de hardware, pero resulta que un bucle simple es más rápido que el anterior con -O2 y -O3. (Al menos usando MinGW GCC en Windows con mi hardware particular). Sin el modificador -O, en una matriz de 400 MB el código anterior es aproximadamente el doble de rápido que un ciclo equivalente, y toma 417 ms en mi máquina, mientras que con la optimización ambos van a unos 300 ms. Lo que significa que tarda aproximadamente el mismo número de nanosegundos que los bytes, y un ciclo de reloj es de aproximadamente un nanosegundo. Entonces, o bien no hay funcionalidad de copia de memoria de bloque de hardware en mi máquina, o la memcpy(..)
no la aprovecha.
Verifique la documentación de su SO para una versión local, luego considere usar el bucle.
El compilador probablemente sepa más sobre optimizar el acceso a la memoria en cualquier arquitectura en particular que usted, así que deje que haga el trabajo.
Concluya como una biblioteca y compílelo con todas las optimizaciones de mejora de velocidad que permite el compilador.
escribe lo tuyo; es trivial incluso en asm.
wmemset(3)
es la versión ancha (16 bits) de memset. Creo que es lo más cercano que vas a obtener en C, sin un bucle.
void memset64( void * dest, uint64_t value, uintptr_t size )
{
uintptr_t i;
for( i = 0; i < (size & (~7)); i+=8 )
{
memcpy( ((char*)dest) + i, &value, 8 );
}
for( ; i < size; i++ )
{
((char*)dest)[i] = ((char*)&value)[i&7];
}
}
(Explicación, como se solicita en los comentarios: cuando se asigna a un puntero, el compilador supone que el puntero se alinea con la alineación natural del tipo; para uint64_t, es decir, 8 bytes. Memcpy () no hace tal suposición. En algún hardware no alineado los accesos son imposibles, por lo que la asignación no es una solución adecuada a menos que sepa que los accesos no alineados funcionan en el hardware con una penalización pequeña o nula, o saben que nunca ocurrirán, o ambos. El compilador reemplazará los pequeños memcpy () sy memset () s con un código más adecuado, por lo que no es tan horrible como parece, pero si sabe lo suficiente para garantizar la asignación siempre funcionará y su generador de perfiles le dice que es más rápido, puede reemplazar el memcpy con una tarea. El segundo para () El bucle está presente en caso de que la cantidad de memoria que se va a llenar no sea un múltiplo de 64 bits. Si sabe que siempre lo estará, simplemente puede soltar ese bucle).