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

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

AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
AABBCC00AABBCC00AABBCC00AABBCC00AABBCC00
...

Я мог использовать std::fill или простой для цикла, но это не достаточно быстро.


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

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

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;
}
11
задан Kirill V. Lyadvinsky 9 July 2010 в 14:25
поделиться

8 ответов

Спасибо всем за ответы. Я проверил решение 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, и добавьте для этого соответствующую обработку.

4
ответ дан 3 December 2019 в 05:56
поделиться

Я бы сделал это следующим образом (прошу прощения за его принадлежность к 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;
    }
}
9
ответ дан 3 December 2019 в 05:56
поделиться

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

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

6
ответ дан 3 December 2019 в 05:56
поделиться

Предполагая, что у вас ограниченное количество значений в фоновом параметре (или, что еще лучше, только включено), возможно, вам стоит попытаться выделить статический вектор и просто использовать 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;
    }
0
ответ дан 3 December 2019 в 05:56
поделиться

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

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

не думаю, что можно стать намного быстрее

-2
ответ дан 3 December 2019 в 05:56
поделиться

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

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

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

3
ответ дан 3 December 2019 в 05:56
поделиться

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

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

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

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

0
ответ дан 3 December 2019 в 05:56
поделиться

Я только что протестировал std::fill with 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 байт за раз.

0
ответ дан 3 December 2019 в 05:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: