Выравнивание данных под работу с SSE
Модератор: Модераторы
Выравнивание данных под работу с SSE
Для повышения скорости программы пробую использовать SSE инструкции, но для эффективной работы с этими инструкциями необходимо выравнивать данные на границу 16 (для movaps в частности), иначе смысл этих инструкций в плане скорости теряется. Собственно вопрос: как нужно выравнивать данные в ФП на адрес кратный 16 (например адрес массива)?
-
MageSlayer
- постоялец
- Сообщения: 216
- Зарегистрирован: 07.09.2006 12:30:44
Не вижу особой проблемы.
Выделять память с запасом в n-1 байт и размещать массив по адресу (указатель+n-1) and 16.
Выделять память с запасом в n-1 байт и размещать массив по адресу (указатель+n-1) and 16.
Огромнеееееейшеееее спасибо!
Т.е. получается вот такая процедура для выделения блока памяти:
Procedure New16(Var P:Pointer;S:Longint);
Begin
GetMem(P,S+16);
S:=Longint(P);
P:=Pointer(S shr 4 shl 4+16);
End;
Как я сам не додумался
Ну а если на уровне компилятора выравнивать? а то в этом методе придется хранить начальный адрес и выравненный адрес для каждого блока, а этих блоков может быть достаточно много,и вариант обьединить все блоки в один блок тоже не подходит, так как количество блоков меняется динамически в большом диапозоне.
Т.е. получается вот такая процедура для выделения блока памяти:
Procedure New16(Var P:Pointer;S:Longint);
Begin
GetMem(P,S+16);
S:=Longint(P);
P:=Pointer(S shr 4 shl 4+16);
End;
Как я сам не додумался
Ну а если на уровне компилятора выравнивать? а то в этом методе придется хранить начальный адрес и выравненный адрес для каждого блока, а этих блоков может быть достаточно много,и вариант обьединить все блоки в один блок тоже не подходит, так как количество блоков меняется динамически в большом диапозоне.
- bw
- постоялец
- Сообщения: 359
- Зарегистрирован: 01.12.2005 10:36:23
- Откуда: Усть-Илимск
- Контактная информация:
Стандартный менеджер кучи веделяет мозг с выравниванием до 8 байт. Хотя никто не может гарантировать, что это правило не изменится в следующих версиях FPC. Можно написать свой менеджер, опираясь на код существующего.
В случае с New16 ты гарантированно не сможешь освободить память FreeMem (смещение указателя происходит даже, если он находится уже на границе 16 байт). Так было бы лучше:
С учетом того что у тебя GetMem в 50% уже вернет "выравненный указатель" получается экономия в этих случаях по 32 байта.
Но лучше не выравнивать указатель при выделении памяти (так придется хранить где то еще один, оригинальный, что бы её можно было освободить), а просто выполнять размещение данных в выделенном буфере, с учетом выравнивания. Т.е. данные должны быть размещены со смещением от начала буфера от 0 до 15.
p.s. Походу я ошибся, стандартный менеджер (в версии FPC 2.2.0 под Linux) выполняет выравнивание не до 8 байт, а до 16 :-). Так что ничего больше делать не нужно.
..bw
В случае с New16 ты гарантированно не сможешь освободить память FreeMem (смещение указателя происходит даже, если он находится уже на границе 16 байт). Так было бы лучше:
Код: Выделить всё
Ptr := GetMem(Size + 15);
if DWord(Ptr) and $F > 0 then
Ptr := Pointer((DWord(Ptr) and $FFFFFFF0) + 16);
С учетом того что у тебя GetMem в 50% уже вернет "выравненный указатель" получается экономия в этих случаях по 32 байта.
Но лучше не выравнивать указатель при выделении памяти (так придется хранить где то еще один, оригинальный, что бы её можно было освободить), а просто выполнять размещение данных в выделенном буфере, с учетом выравнивания. Т.е. данные должны быть размещены со смещением от начала буфера от 0 до 15.
p.s. Походу я ошибся, стандартный менеджер (в версии FPC 2.2.0 под Linux) выполняет выравнивание не до 8 байт, а до 16 :-). Так что ничего больше делать не нужно.
..bw
А с аллокатором для динамических массивов как получается? Ведь там первые 8 байт - служебная информация. Или он так аллоцирует, чтобы нолевой элемент массива начинался с границы в 16 байт?
bw
Прежде всего, спасибо за ответ
)
Про то, что придется хранить два указателя на блок и про идею с общим блок и одним смещением для него, я писал - не вариант, хотя если больше не будет других возможностей, заюзаю это способ.
А то что в ФП 2.2.0 под ГО32 тоже идет выравнивание на 16 байт как и под линуху я не уверен, так как если указатель не выравнивать, ассемблерная вставка с командой movaps гарантировано вызовет ошибку (#GP если я не ошибаюсь). Если же выравнивать - все работает без ошибок.
Еще раз спасибо за код, опробую и переведу на ассемблер, а то уж больно мне это переопределение типов не нравится
Прежде всего, спасибо за ответ
Про то, что придется хранить два указателя на блок и про идею с общим блок и одним смещением для него, я писал - не вариант, хотя если больше не будет других возможностей, заюзаю это способ.
А то что в ФП 2.2.0 под ГО32 тоже идет выравнивание на 16 байт как и под линуху я не уверен, так как если указатель не выравнивать, ассемблерная вставка с командой movaps гарантировано вызовет ошибку (#GP если я не ошибаюсь). Если же выравнивать - все работает без ошибок.
Еще раз спасибо за код, опробую и переведу на ассемблер, а то уж больно мне это переопределение типов не нравится
Врядли в выделяемой области есть служебная информация,
А именно так все массивы и устроены. Только сам массив в итоге - указатель не на сам выделенный блок, а на его 8-й байт. Таким образом, служебная информация оказывается по отрицательному смещению. Если попробуешь привести массив к указателю и освободить его... БУУУМ!
- bw
- постоялец
- Сообщения: 359
- Зарегистрирован: 01.12.2005 10:36:23
- Откуда: Усть-Илимск
- Контактная информация:
> А то что в ФП 2.2.0 под ГО32 тоже идет выравнивание на 16 байт как и под линуху я не уверен
А я уверен :-). Проверь, это не сложно:
На какой границе находятся выделенные блоки? 16?
> movaps гарантировано вызовет ошибку
Решай проблемы по мере их поступления :-). А вдруг никогда не возникнет такой ошибки, а ты уже несколько дней не можешь начать работу только из-за потенциальной возможности. Можешь, например использовать свои GetMem/FreeMem, вот самая простая реализация GetMem, которая не выполняет выравнивание, а только проверяет, что бы блоки были выравнены:
Если этого окажется не достаточно, перепишешь MyGetMem и MyFreeMem так, что бы они гарантировали выравнивание.
> БУУУМ!
>> Я не изучал код стандартного менеджера кучи.
..bw
А я уверен :-). Проверь, это не сложно:
Код: Выделить всё
begin
WriteLn(HexStr(DWord(GetMem(1)), 8));
WriteLn(HexStr(DWord(GetMem(1)), 8));
WriteLn(HexStr(DWord(GetMem(1)), 8));
WriteLn(HexStr(DWord(GetMem(1)), 8));
end.
На какой границе находятся выделенные блоки? 16?
> movaps гарантировано вызовет ошибку
Решай проблемы по мере их поступления :-). А вдруг никогда не возникнет такой ошибки, а ты уже несколько дней не можешь начать работу только из-за потенциальной возможности. Можешь, например использовать свои GetMem/FreeMem, вот самая простая реализация GetMem, которая не выполняет выравнивание, а только проверяет, что бы блоки были выравнены:
Код: Выделить всё
function MyGetMem(Size: DWord): Pointer;
begin
Result := GetMem(Size);
if DWord(Result) and $F > 0 then
begin
FreeMem(Result);
raise Exception.Create('Жопа');
end;
end;
Если этого окажется не достаточно, перепишешь MyGetMem и MyFreeMem так, что бы они гарантировали выравнивание.
> БУУУМ!
>> Я не изучал код стандартного менеджера кучи.
..bw
bw
)) Проверил я твоим кодом(хотя до этого также и своим проверял, иначе тему вообще бы не создавал),результаты:
0010D9E8 (1104360 / 16 = 69022,5)
0010D9F8 (1104376 / 16 = 69023,5) и т.д.
" А вдруг никогда не возникнет такой ошибки" - я проверял след. кодом, и сразу же получил ошибку(что естественно так как данные не выравнены на 16):
Может у тебя директива включена какая нить для выравнивания? И еще: Я работаю под Дос(расширитель GO32).
P/S. Exception "жопа" прикальнула %)
)) Проверил я твоим кодом(хотя до этого также и своим проверял, иначе тему вообще бы не создавал),результаты:
0010D9E8 (1104360 / 16 = 69022,5)
0010D9F8 (1104376 / 16 = 69023,5) и т.д.
" А вдруг никогда не возникнет такой ошибки" - я проверял след. кодом, и сразу же получил ошибку(что естественно так как данные не выравнены на 16):
Код: Выделить всё
Type
PVector= ^TVector;
TVector=Record X,Y,Z,W:Single;End;
Var V:PVector;
Begin
New(V);
Asm
Mov eax,V
Movaps xmm0,[eax]
End;
Dispose(V);
End.Может у тебя директива включена какая нить для выравнивания? И еще: Я работаю под Дос(расширитель GO32).
P/S. Exception "жопа" прикальнула %)
Последний раз редактировалось Aloner 28.12.2008 00:40:12, всего редактировалось 1 раз.
Есть директива {$ALIGN
в документации написано следующее:
... Почему бы не расширить данную директиву на другие платформы?
в документации написано следующее:
The {$ALIGN directive can be used to select the data alignment strategy of the compiler for the Mac
OS. Only valid in MACPAS mode, it can have the following values:
... Почему бы не расширить данную директиву на другие платформы?
Mr.Smart
+1
Присоединяюсь к просьбе!
Я еще бы лучше ввести оператор, который бы указывал что например именно этот массив или этот указатель нужно выравнять на указанную границу...
+1
Присоединяюсь к просьбе!
Я еще бы лучше ввести оператор, который бы указывал что например именно этот массив или этот указатель нужно выравнять на указанную границу...
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Я очень далек от этой темы, но вообще-то компилятор умеет генерить SSE код, а стало быть, и самостоятельно выравнивать все что нужно. Для этого, наверное, нужно разрешить ему этот код генерить (опция -Cfsse2), и объявлять типы переменных наподобие Tvector = array[0..3] of single (хотя, может он настолько умный, что и record x,y,z,w: single end; поймет).
