Как узнать вышел ли указатель за пределы массива

dimon72

Имеется код со вставкой на асме, где в качестве одного из опрерандов используется указатель (адрес).
Вот пример:
procedure TForm1.Button5Click(Sender: TObject);
var
V0: array of single;
V1: array of single;
Y2: array of single;
begin
Finalize(V0);
SetLength(V0,8);
Finalize(V1);
SetLength(V1,4);
Finalize(Y2);
SetLength(Y2,4);
V0[0]:=6;
V0[1]:=3;
V0[2]:=7;
V0[3]:=3;
//
V0[4]:=1;
V0[5]:=2;
V0[6]:=3;
V0[7]:=4;
V1[0]:=4;
V1[1]:=5;
V1[2]:=8;
V1[3]:=7;
Y2[0]:=0;
Y2[1]:=0;
Y2[2]:=0;
Y2[3]:=0;
asm
mov ebx, V0 //в Дельфи динамические массивы - есть указатели, поэтому, как я понимаю,
// в ebx здесь будет адрес первого элемента массива
mov edx, V1
mov eax, Y2
// загрузить векторы по указанным адресам в SSE регистры xmm0-1
movups xmm0,[ebx]
movups xmm1, [edx]
mulps xmm1, xmm0 //перемножить
// записать результат
movups [eax], xmm1 //он равен: Y2=(24,15,56,21)
end;
end;
Допустим, у меня есть вектор длиной Length1. И его нужно перемножить с другим вектором, длиной Length2. Как я понимаю, нужно установить указатель на нулевой элемент векторов, затем организовать цикл на асме, помещая по 4 числа в регистры SSE, перемещаясь по массивам "скачками" по 4 элемента.
Так, например, можно вызывать :
movups xmm0,[ebx+shift],
где shift - инкрементно возрастающий ряд: 16,32,48 и т.д.. Инкремировать до тех пор, пока не будет достигнут конец массива.
Вопросы: 1. как узнать, что достигнут конец массива? 2. как организовать проверку этого в Asm цикле?

Ivan8209

Для начала, цитируй так, чтобы было удобно читать,
такое впечатление, будто паскаль стал фортраном ---
один оператор на строке.
> Вопросы:
> 1. как узнать, что достигнут конец массива?
Никак. Длину ты должен знать заранее.
> 2. как организовать проверку этого в Asm цикле?
loop, или cmp+jz/jnz/jb, или как она там называется.
---
"...Исчезая в этих волнах."

SPARTAK3959

Длину динамического массива можно узнать обратившись толи по смещению -4, то ли по смещению -8. Вызови функцию length а потом дизассемблируй

dimon72

Понял. Спасибо, попробую.

Helga87

1. как узнать, что достигнут конец массива? 2. как организовать проверку этого в Asm цикле?
удобнее будет перед началом обработки на Object Pascal проверить длины массивов, а затем уже на асме знать, какие есть длины и не париться с их проверкой, тем более "хакерскими" методами, которые почти наверняка будут неправильно работать при смене платформы (например, с x86 на x64)

dimon72

Ну да, можно тот же SizeOf взять. Сейчас как раз экспериментирую.

dimon72

В общем, вот что у меня пока получилось.
type
TVectSingle = array of single;
// ф-ция сложения двух векторов (с исп. SSE-команд)
// Размерность векторов должна быть одинаковой
function SumVectSingle(const V1, V2: array of single): TVectSingle;
var
S: integer;
Vector: TVectSingle;
begin
S:=SizeOf(V1);//определим длину массива в байтах; необх.для опред-я кол-ва
//смещений по 16, кот. равно S/16
SetLength(Vector, High(V1)+1);
asm
mov ebx,0//инициализ-я переменной для задания инкремента смещения адреса
//он же контрольный объект цикла for i:=0 to...
mov edx, V1//в edx-адрес первого эл-та V1
mov ecx, V2 //в ecx-адрес первого эл-та V2
mov eax, Vector//здесь будет вых.вектор (результат суммы векторов)
@lb:
//****************************************************************************
//здесь расположим команды asm цикла (аналог for i:=.. to .. do ...)
// загрузить вх. векторы в регистры xmm0-1 по соотв.адресам со смещением по 16 байт
movups xmm0,[edx+ebx]
movups xmm1, [ecx+ebx]
addps xmm1, xmm0// сложить
// записать результат в Vector
movups [eax+ebx], xmm1
add ebx, 16//задаём очередное смещение адреса = 16 байт (размер XMM операнда)
//****************************************************************************
cmp ebx,S//...to [кол-во "шагов" по 16 байт, кот. нужно сделать] do
jnz @lb
mov ebx,0//контрольный объект - в исх. состояние
end;
Result := Vector;
end;
Вроде работает.
Если у вас есть замечания, уточнения - буду рад их услышать.

dimon72

Есть вопрос, на который я до сих пор не нашёл ответа:
как выравнивать данные по 16 байт в Дельфи?
Команды для выровненных входных данных работают быстрее невыровненных.
Пример команд: movaps вместо movups и т.д.

dimon72

Да, совсем забыл: нужно ведь учитывать, что размер входных массивов может быть не кратен 16 байт.
Попробовал переделать функцию с учётом этого замечания.
Проблема в том, что по недостающим элементам функция считает из неопределенного участка памяти и в конце вектора-результата, естественно, получается хрень.
Кто-нибудь знает как это исправить?
function SumVectSingle(var V1, V2: array of single): TVectSingle;
// Сложение двух векторов (с исп. SSE-команд)
var
S: integer;
Rmndr: integer;
Miss: integer;
Vector: TVectSingle;
begin
S:=SizeOf(V1);//определим длину массива в байтах; необх.для опред-я кол-ва смещений по 16 байт
Rmndr:=S mod 16;
If Rmndr<>0 then//если есть остаток от дел-я на 16, то...
begin
S:S div 16)*16)+16;//добавим 16 байт к имеющимуся кратному 16 кол-ву байт => делаем ещё один шаг в asm цикле)
Miss:=(16-Rmndr) div 4//считаем кол-во недостающих до кратности 4 значений
end;
SetLength(Vector, High(V1)+1+Miss);
asm
mov ebx,0//инициализ-я переменной для задания инкремента смещения адреса
//он же контрольный объект цикла for i:=0 to...
mov edx, V1//в edx-адрес первого эл-та V1
mov ecx, V2 //в ecx-адрес первого эл-та V2
mov eax, Vector//здесь будет вых.вектор (результат суммы векторов)
@lb:
//****************************************************************************
//здесь расположим команды asm цикла (аналог for i:=.. to .. do ...)
// загрузить вх. векторы в регистры xmm0-1 по соотв.адресам со смещением по 16 байт
movups xmm0,[edx+ebx]
movups xmm1, [ecx+ebx]
addps xmm1, xmm0// сложить
// записать результат в Vector
movups [eax+ebx], xmm1
add ebx, 10h//задаём очередное смещение адреса = 16 байт (размер XMM операнда)
//****************************************************************************
cmp ebx,S//...to [кол-во "шагов" по 16 байт, кот. нужно сделать] do
jnz @lb
mov ebx,0//контрольный объект - в исх. состояние
end;
Result := Vector;
end;

Ivan8209

> Сложение двух векторов
У меня возник такой вопрос: а что мешает использовать штатный BLAS?
---
"Аллах не ведёт людей неверных."

dimon72

Это просто для того, чтобы разобраться, хотя и быстрее должно быть, чем на Object Pascal.
Вообще, хочу сделать быструю FIR-фильтрацию с применением SSE инструкций (на Delphi).
А что такое BLAS?

Ivan8209

Слабо сделать поиск по GNU Software Directory или задействовать GAMS?
FAQ "Study".
---
"Аллах не ведёт людей неверных."

Helga87

Поддержу. В инете до фига математических библиотек, на разработку которых потрачены человекогоды. Т.е. стандартные функции вроде того же скалярного произведения там заоптимизированы по самое не могу.
Использовать SSE в Delphi для написания своих функций имеет смысл только для обучения. И то, не совсем ясно, куда потом полученные знания применять: в свое время я потратил 3 месяца на похожую программу на Delphi+SSE2, в то время как другие люди не связываясь с асмом, сделали то же самое (ну, возможно, на несколько процентов медленнее) за неделю. Да, я зарюхал тогда SSE, но ни разу после этого не применял эти знания. Вопрос: куда я потратил 3 месяца? Второй вопрос: стоит ли тратить время тебе?

dimon72

А слабо в двух словах ответить, не посылая в поиск?

dimon72

Да надо одну софтинку сделать. Главное, конечно, обучение.
в свое время я потратил 3 месяца на похожую программу на Delphi+SSE2
Ну получилось же?
А что делала твоя программа на Delphi+SSE2?

Ivan8209

Могу даже одним: RTFAQ.
---
"Аллах не ведёт людей неверных."
P.S. Специально для чукчей BLAS лежит в ${PKGSRCDIR}/math/blas,
и не найти его --- надо постараться.

Helga87

А что делала твоя программа на Delphi+SSE2?
Почти real-time ray tracer (на p-3 700, сейчас это уже было бы real-time, вероятно).

dimon72

А что делала твоя программа на Delphi+SSE2?
Почти real-time ray tracer (на p-3 700, сейчас это уже было бы real-time, вероятно).
Ну и молодец! Мало ли, что в Инете куча библиотек. Их тоже кто-то пишет. Да и в любом случае, главное здесь, что ты сам для себя разобрался, а не взял втупую чужой код.

Helga87

Да и в любом случае, главное здесь, что ты сам для себя разобрался, а не взял втупую чужой код.
Главное — это то, чего ты добился. Разобрался — это не достижение. Поэтому если удается взять чужой код и применить его для решения своей задачи — это прекрасно.
Но вообще, можно к этому всему относиться как к приятному времяпровождению, тогда желаю удачи в деле освоения SSE.

dimon72

Главное — это то, чего ты добился. Разобрался — это не достижение. Поэтому если удается взять чужой код и применить его для решения своей задачи — это прекрасно.
Да мне не для приятного времяпровождения, а надо реальную задачу решить.
Ну так как мне выравнивать массив по параграфу в Дельфи?
Может кто знает?

Helga87

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

dimon72

Есть Project->Options->Compiler->Record field alignment. Там есть следующие значения:
1,2,4,8. 16 нету.
Есть еще директива компилятора {$A+}. Из описания не видно, чтобы она помогала выравнивать по 16 байт.

Helga87

Тут уже помочь не смогу, последний раз с Дельфи работал больше трех лет назад.

Dasar

выделяй память в heap-е и выравнивай сам.

yolki

{$ALIGN 16} не работает?
в доках правда разрешено только 1,2,4,8
я не разбираюсь в SSE/2 и т.п.. нафига использовать сингл для арифметики? SSE использует 4-байтовый float?

dimon72

{$ALIGN 16}
Не работает.
[quote]SSE использует 4-байтовый float?[quote]
Да.
Преимущество в параллельности обработки данных. SSE позволяет производить одновременно операции над 4-мя парами чисел.
Реальная польза:

dimon72

выделяй память в heap-е и выравнивай сам.
Может примерчик, работающий в Delphi, подкинешь? Самый простой. Буду очень признателен.

Dasar

выделяем выравненные куски + прячем от остального кода работу по выравниванию (решение в лоб)

byte* AllocAlign16(int size)
{
byte * p = new byte[size + 17];
byte *p16 = p + 16 - int)p)%16; //выравниваем
(p16-1)^ = p16-p; //запоминаем сдвиг, чтобы потом можно было восстановить истинный указатель на блок
return p16;
}
void FreeAlign16(byte *p)
{
byte * oldP = p - (p-1)^;
delete oldP;
}

dimon72

...примерчик, работающий в Delphi

Dasar

что не получается, когда пытаешься сделать этот пример компилируемым?
ps
вместо % должна быть функция Mod
и выделение освобождение byte-ого массива может чуть по другому записывается.

dimon72

Спасибо, попробую.
Оставить комментарий
Имя или ник:
Комментарий: