Есть memset(), который принимает целые числа, превышающие char?
Есть ли версия memset(), которая устанавливает значение, превышающее 1 байт (char)? Например, предположим, что у нас есть функция memset32(), поэтому, используя ее, мы можем сделать следующее:
int32_t array[10];
memset32(array, 0xDEADBEEF, sizeof(array));
Это установит значение 0xDEADBEEF во всех элементах массива. В настоящее время мне кажется, что это можно сделать только с помощью цикла.
В частности, меня интересует 64-битная версия memset(). Знаете что-нибудь подобное?
Ответы
Ответ 1
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];
}
}
(Объяснение, как указано в комментариях: при назначении указателю компилятор предполагает, что указатель выровнен по отношению к естественному выравниванию типа; для uint64_t это 8 байтов. memcpy() не делает такого предположения. некоторые аппаратные несвязанные обращения невозможны, поэтому назначение не является подходящим решением, если вы не знаете, что неуправляемые обращения работают на аппаратном обеспечении с небольшим штрафом или без него, или знают, что они никогда не произойдут, или и то, и другое. Компилятор заменит небольшие memcpy() s и memset() s с более подходящим кодом, так что это не так страшно, так это выглядит, но если вы знаете достаточно, чтобы гарантировать, что назначение всегда будет работать, а ваш профилировщик говорит вам, что это быстрее, вы можете заменить memcpy на задание. for() присутствует в случае, если объем памяти, который должен быть заполнен, не является кратным 64 бит. Если вы знаете, что это всегда будет, вы можете просто отказаться от этого цикла.)
Ответ 2
Нет стандартной библиотечной функции afaik. Поэтому, если вы пишете переносимый код, вы смотрите на цикл.
Если вы пишете непереносимый код, то проверьте свою документацию по компилятору/платформе, но не задерживайте дыхание, потому что здесь редко можно получить большую помощь. Может быть, кто-то еще соберет примеры платформ, которые действительно что-то предоставляют.
Способ, которым вы должны писать самостоятельно, зависит от того, можете ли вы определить в API, что вызывающий агент гарантирует, что указатель dst будет достаточно выровнен для 64-разрядных операций записи на вашей платформе (или платформах, если они переносятся). На любой платформе, которая имеет 64-разрядный целочисленный тип вообще, malloc по крайней мере вернет подходящие указатели.
Если вам нужно справиться с несогласованностью, вам нужно что-то вроде ответа moonshadow. Компилятор может встроить/развернуть эту memcpy с размером 8 (и использовать 32-х или 64-разрядные нестандартные команды записи, если они существуют), поэтому код должен быть довольно неудобным, но я предполагаю, что это, вероятно, не будет особенным целая функция для назначения адресата. Я бы хотел, чтобы меня исправили, но я не буду бояться.
Итак, если вы знаете, что вызывающий абонент всегда даст вам dst с достаточным выравниванием для вашей архитектуры и длиной, кратной 8 байтам, затем выполните простой цикл, пишущий uint64_t (или любой другой 64-битный int находится в вашем компиляторе), и вы, вероятно, (no promises) получите быстрый код. У вас наверняка будет более короткий код.
В любом случае, если вы заботитесь о производительности, профайлируйте его. Если он не достаточно быстро, попробуйте еще раз с большей оптимизацией. Если он все еще не достаточно быстрый, задайте вопрос о версии asm для процессора (ов), на котором он не достаточно быстро. memcpy/memset может получить значительное увеличение производительности от оптимизации каждой платформы.
Ответ 3
Проверьте документацию своей операционной системы на локальную версию, а затем рассмотрите возможность использования цикла.
Компилятор, вероятно, знает больше об оптимизации доступа к памяти на какой-либо конкретной архитектуре, чем вы, поэтому давайте это сделаем.
Оберните его как библиотеку и скомпилируйте ее со всеми улучшениями, оптимизирующими скорость, которые позволяет компилятор.
Ответ 4
Только для записи следующее использование memcpy(..)
в следующем шаблоне. Предположим, мы хотим заполнить массив целыми целыми числами:
--------------------
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
Это принимает O (lg (num)) приложения 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;
}
Я думал, что это может быть быстрее, чем цикл, если memcpy(..)
был оптимизирован с использованием некоторой функции копирования памяти блока памяти, но оказывается, что простой цикл быстрее, чем выше, с -O2 и -O3. (По крайней мере, используя MinGW GCC для Windows с моим конкретным оборудованием.) Без переключателя -O на 400 МБ-массиве код выше примерно в два раза быстрее, чем эквивалентный цикл, и занимает 417 мс на моей машине, тогда как при оптимизации они оба идут примерно до 300 мс. Это означает, что он занимает примерно такое же количество наносекунд, как и байты, а тактовый цикл составляет около наносекунды. Таким образом, на моей машине нет функциональных возможностей памяти блока памяти, или реализация memcpy(..)
не использует ее.
Ответ 5
wmemset(3)
- это широкая (16-разрядная) версия memset. Я думаю, что самое близкое, что вы собираетесь получить на C, без цикла.
Ответ 6
Вы действительно должны позволить компилятору оптимизировать это для вас, как кто-то другой. В большинстве случаев этот цикл будет незначительным.
Но если это какая-то особая ситуация, и вы не против быть специфичной для платформы, и действительно нужно избавиться от цикла, вы можете сделать это в сборочном блоке.
//pseudo code
asm
{
rep stosq ...
}
Вы можете, вероятно, выполнить команду сборки goos stosq для специфики. Это не должно быть несколько строк кода.
Ответ 7
Если вы просто нацеливаете на компилятор x86, вы можете попробовать что-то вроде (пример VС++):
inline void memset32(void *buf, uint32_t n, int32_t c)
{
__asm {
mov ecx, n
mov eax, c
mov edi, buf
rep stosd
}
}
В противном случае просто создайте простой цикл и доверяйте оптимизатору знать, что он делает, просто что-то вроде:
for(uint32_t i = 0;i < n;i++)
{
((int_32 *)buf)[i] = c;
}
Если вы сделаете это сложными, возможно, это будет медленнее, чем проще для оптимизации кода, не говоря уже о более сложном обслуживании.
Ответ 8
напишите свой собственный; это тривиально даже в asm.