Подтвердить что ты не робот

Есть memset(), который принимает целые числа, превышающие char?

Есть ли версия memset(), которая устанавливает значение, превышающее 1 байт (char)? Например, предположим, что у нас есть функция memset32(), поэтому, используя ее, мы можем сделать следующее:

int32_t array[10];
memset32(array, 0xDEADBEEF, sizeof(array));

Это установит значение 0xDEADBEEF во всех элементах массива. В настоящее время мне кажется, что это можно сделать только с помощью цикла.

В частности, меня интересует 64-битная версия memset(). Знаете что-нибудь подобное?

4b9b3361

Ответ 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.