Есть ли функция (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;
}
Спасибо всем за ответы. Я проверил решение 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, и добавьте для этого соответствующую обработку.
Я бы сделал это следующим образом (прошу прощения за его принадлежность к 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;
}
}
Я хочу спросить: Вы точно профилировали std::fill
и показали, что это узкое место в производительности? Я бы предположил, что он реализован довольно эффективно, так, что компилятор может автоматически генерировать соответствующие инструкции (например, -march
на gcc).
Если это узкое место, то все еще возможно получить больше пользы от алгоритмического редизайна (если это возможно), чтобы избежать установки такого количества памяти (очевидно, снова и снова), что уже не имеет значения, какой механизм заполнения вы используете.
Предполагая, что у вас ограниченное количество значений в фоновом параметре (или, что еще лучше, только включено), возможно, вам стоит попытаться выделить статический вектор и просто использовать 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;
}
Это может быть немного непереносимым, но вы можете использовать перекрывающуюся память копировать. Заполните первые четыре байта желаемым шаблоном и используйте memcpy ().
int32* p = (int32*) malloc( size );
*p = 1234;
memcpy( p + 4, p, size - 4 );
не думаю, что можно стать намного быстрее
Рассматривали ли вы использование
vector<int32_t> myVector;
myVector.reserve( sizeIWant );
, а затем std :: fill? Или, возможно, конструктор std :: vector
, который принимает в качестве аргумента количество удерживаемых элементов и значение для их инициализации?
Не совсем уверен, как вы установили 4 байта подряд, но если вы хотите заполнять память только одним байтом снова и снова, вы можете использовать memset
.
void * memset ( void * ptr, int value, size_t num );
Заполнение блока памяти
Устанавливает первые num байт блока памяти, на который указывает
ptr
, в указанное значение (интерпретируется какunsigned char
).
Я только что протестировал 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 байт за раз.