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

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

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

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

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

Ответы на вопрос(7)

вы можете попробовать что-то вроде (пример VC ++):

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;
}

Если вы сделаете это сложным, скорее всего, это будет медленнее, чем проще, оптимизировать код, не говоря уже о том, что его сложнее поддерживать.

ас, как кто-то другой предложил. В большинстве случаев этот цикл будет незначительным.

Но если это какая-то особая ситуация, и вы не против того, чтобы зависеть от платформы, и действительно хотите избавиться от цикла, вы можете сделать это в сборочном блоке.

//pseudo code
asm
{
    rep stosq ...
}

Вы можете узнать команду сборки Google Stosq для уточнения. Это не должно быть больше, чем несколько строк кода.

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 () s и memset ( ) с более подходящим кодом, поэтому он выглядит не так ужасно, но если вы знаете достаточно, чтобы гарантировать, что назначение всегда будет работать, и ваш профилировщик скажет вам, что это быстрее, вы можете заменить memcpy на назначение. Второе для ( ) цикл присутствует, если объем заполняемой памяти не кратен 64 битам. Если вы знаете, что так будет всегда, вы можете просто удалить этот цикл.)

wmemset(3) - это широкая (16-битная) версия memset. Я думаю, что это самое близкое, что вы получите в C, без цикла.

если вы пишете переносимый код, вы смотрите на цикл.

Если вы пишете непереносимый код, проверьте документацию по компилятору / платформе, но не задерживайте дыхание, потому что здесь редко можно получить большую помощь. Может быть, кто-то еще добавит примеры платформ, которые что-то предоставляют.

То, как вы будете писать свои собственные, зависит от того, можете ли вы определить в API, что вызывающий объект гарантирует, что указатель dst будет достаточно выровнен для 64-битной записи на вашей платформе (или платформах, если они переносимы). На любой платформе с 64-битным целочисленным типом, по крайней мере, malloc будет возвращать соответствующим образом выровненные указатели.

Если вам нужно справиться с неприсоединением, вам нужно что-то вроде ответа лунной тени. Компилятор может встроить / развернуть этот memcpy с размером 8 (и использовать 32- или 64-битные операции выравнивания без выравнивания, если они существуют), поэтому код должен быть довольно ловким, но я предполагаю, что он, вероятно, не будет особенным случаем вся функция для пункта назначения выравнивается. Я бы с радостью поправился, но боюсь, что не буду.

Так что, если вы знаете, что вызывающая сторона всегда выдаст вам dst с достаточным выравниванием для вашей архитектуры и длиной, кратной 8 байтам, тогда выполните простой цикл, записывающий uint64_t (или что-то еще в 64-битном int). ваш компилятор), и вы, вероятно, (без обещаний) в конечном итоге с более быстрым кодом. У вас наверняка будет более короткий код.

В любом случае, если вам небезразлична производительность, тогда профилируйте ее. Если это не достаточно быстро, попробуйте еще раз с большей оптимизацией. Если это все еще не достаточно быстро, задайте вопрос о версии asm для процессоров, на которых это не достаточно быстро. memcpy / memset может значительно повысить производительность благодаря оптимизации для каждой платформы.

Только для записи, следующее используетmemcpy(..) в следующем порядке. Предположим, мы хотим заполнить массив 20 целыми числами:

--------------------

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(..) реализация не использует это.

а затем подумайте только об использовании цикла.

Компилятор, вероятно, знает больше об оптимизации доступа к памяти в любой конкретной архитектуре, чем вы, поэтому позвольте ему делать свою работу.

Оберните его как библиотеку и скомпилируйте со всеми оптимизациями, улучшающими скорость, которые позволяет компилятор.

ВАШ ОТВЕТ НА ВОПРОС