Как быстро заполнить память с помощью значения `int32_t`?

Есть ли функция (SSEx intrinsics в порядке), которая заполнит память указанным значением int32_t? Например, когда это значение равно 0xAABBCC00, память результатов должна выглядеть так:

AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
...

Я мог бы использовать std::fill или простой for-loop, но он недостаточно быстр.


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

Упрощенный код:

struct X
{
  typedef std::vector<int32_t> int_vec_t;
  int_vec_t buffer;

  X() : buffer( 5000000 ) { /* some more action */ }
  ~X() { /* some code here */ }

  // the following function is called 25 times per second
  const int_vec_t& process( int32_t background, const SOME_DATA& data );
};

const X::int_vec_t& X::process( int32_t background, const SOME_DATA& data )
{
    // the following one string takes 30% of total time of #process function
    std::fill( buffer.begin(), buffer.end(), background );

    // some processing
    // ...

    return buffer;
}

Ответ 1

Спасибо всем за ваши ответы. Я проверил решение wj32, но он показывает очень похожее время, как std::fill. Мое текущее решение работает в 4 раза быстрее (в Visual Studio 2008), чем std::fill с помощью функции memcpy:

 // fill the first quarter by the usual way
 std::fill(buffer.begin(), buffer.begin() + buffer.size()/4, background);
 // copy the first quarter to the second (very fast)
 memcpy(&buffer[buffer.size()/4], &buffer[0], buffer.size()/4*sizeof(background));
 // copy the first half to the second (very fast)
 memcpy(&buffer[buffer.size()/2], &buffer[0], buffer.size()/2*sizeof(background));

В производственном коде нужно добавить проверку, если buffer.size() делится на 4 и добавить для этого соответствующую обработку.

Ответ 2

Вот как бы я это сделал (пожалуйста, извините за Microsoft):

VOID FillInt32(__out PLONG M, __in LONG Fill, __in ULONG Count)
{
    __m128i f;

    // Fix mis-alignment.
    if ((ULONG_PTR)M & 0xf)
    {
        switch ((ULONG_PTR)M & 0xf)
        {
            case 0x4: if (Count >= 1) { *M++ = Fill; Count--; }
            case 0x8: if (Count >= 1) { *M++ = Fill; Count--; }
            case 0xc: if (Count >= 1) { *M++ = Fill; Count--; }
        }
    }

    f.m128i_i32[0] = Fill;
    f.m128i_i32[1] = Fill;
    f.m128i_i32[2] = Fill;
    f.m128i_i32[3] = Fill;

    while (Count >= 4)
    {
        _mm_store_si128((__m128i *)M, f);
        M += 4;
        Count -= 4;
    }

    // Fill remaining LONGs.
    switch (Count & 0x3)
    {
        case 0x3: *M++ = Fill;
        case 0x2: *M++ = Fill;
        case 0x1: *M++ = Fill;
    }
}

Ответ 3

Я должен спросить: вы определенно профилировали std::fill и показали, что это узкое место в производительности? Я предполагаю, что это будет реализовано довольно эффективно, так что компилятор может автоматически сгенерировать соответствующие инструкции (например, -march on gcc).

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

Ответ 4

Рассматривали ли вы использование

vector<int32_t> myVector;
myVector.reserve( sizeIWant );

а затем используйте std:: fill? Или, возможно, конструктор a std::vector, который принимает в качестве аргумента количество сохраненных элементов и значение для их инициализации в?

Ответ 5

Не совсем уверен, как вы устанавливаете 4 байта в строке, но если вы хотите снова заполнить память одним байтом заново, вы можете использовать memset.

void * memset ( void * ptr, int value, size_t num );

Заполнить блок памяти

Устанавливает первые num байты блока памяти, на которые указывает ptr, на указанное значение (интерпретируется как unsigned char).

Ответ 6

Предполагая, что у вас есть ограниченное количество значений в вашем фоновом параметре (или даже лучше, только на), возможно, вам стоит попытаться выделить статический вектор и просто использовать memcpy.

const int32_t sBackground = 1234;
static vector <int32_t> sInitalizedBuffer(n, sBackground);

    const X::int_vec_t& X::process( const SOME_DATA& data )
    {
        // the following one string takes 30% of total time of #process function
        std::memcpy( (void*) data[0], (void*) sInitalizedBuffer[0], n * sizeof(sBackground));

        // some processing
        // ...

        return buffer;
    }

Ответ 7

Я только что протестировал std:: fill с g++ с полной оптимизацией (SSE и т.д. включен):

#include <algorithm>
#include <inttypes.h>

int32_t a[5000000];

int main(int argc,char *argv[])
{
    std::fill(a,a+5000000,0xAABBCC00);
    return a[3];
}

и внутренний цикл выглядел так:

L2:
    movdqa  %xmm0, -16(%eax)
    addl    $16, %eax
    cmpl    %edx, %eax
    jne L2

Похоже, 0xAABBCC00 x 4 загрузился в xmm0 и перемещается по 16 байт за раз.

Ответ 8

vs2013 и vs2015 могут оптимизировать простой для цикла цикл в инструкцию rep stos. Это самый быстрый способ заполнения буфера. Вы можете указать std::fill для своего типа следующим образом:

namespace std {
    inline void fill(vector<int>::iterator first, vector<int>::iterator last, int value){
        for (size_t i = 0; i < last - first; i++)
            first[i] = value;
    }
}

BTW. Чтобы компилятор выполнял оптимизацию, к буферу должен обращаться оператор подстроки.

Это не будет работать на gcc и clang. Они оба скомпилируют код в условный цикл перехода. Он работает так медленно, как исходный std::fill. И хотя wchar_t является 32-битным, wmemset не имеет сборки, как нравится memset. Поэтому вам нужно написать код сборки для оптимизации.

Ответ 9

Это может быть немного не переносимым, но вы можете использовать дублирующуюся копию памяти. Заполните первые четыре байта с помощью шаблона, который вы хотите, и используйте memcpy().

int32* p = (int32*) malloc( size );
*p = 1234;
memcpy( p + 4, p, size - 4 );

Не думайте, что вы можете ускориться быстрее