SUBPD xmm, mem = SIGSEGV[Решено]

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

SUBPD xmm, mem = SIGSEGV[Решено]

Сообщение Maxizar » 27.04.2011 19:30:14

Есть такой тип данных:
Код: Выделить всё
    Type
      PComplex =^TComplex;
      TComplex = record
         Re,
         Im:Double;
    end;// TComplex = record


Написал реализацию БПФ на Языки Free Pascal, на встроенном ассемблере (FPU), теперь хочу написать применив SSE. В данном случае SSE2.
Вот кусочек кода:
Код: Выделить всё
    MOVuPD  xmm0,  [EAX]                 //ST(0) :=  D[StartIndex]
    MOVuPD  xmm1,  xmm0
    MOVuPD  xmm2,  [EBX]
    ADDPD   xmm0,  xmm2
    SUBPD   xmm1,  xmm2
    MOVuPD  [EAX], xmm0
    MOVuPD  [EBX], xmm1
    pop   EDX
    jmp   @Exit


Все работает на ура. Но в большинстве случаев встречал такую запись (особенно в книгах) да и в спецификации команд указано, что так можно делать. А именно:
Код: Выделить всё
SUBPD   xmm1,  указатель на память


Переделываем:
Код: Выделить всё
    MOVuPD  xmm0,  [EAX]                 //ST(0) :=  D[StartIndex]
    MOVuPD  xmm1,  xmm0
    MOVuPD  xmm2,  [EBX]
    ADDPD   xmm0,  xmm2
    SUBPD   xmm1,  TComplex [EBX] --- Тут ошибка
    MOVuPD  [EAX], xmm0
    MOVuPD  [EBX], xmm1
    pop   EDX
    jmp   @Exit


Пробывал вот так:
Код: Выделить всё
SUBPD   xmm1,  TComplex [EBX]
или
SUBPD   xmm1,  dqword [EBX]
или 
SUBPD   xmm1,  [EBX]


Но у меня вылетает ошибка: SIGSEGV почему, что я делаю не так?.
И да чуть не забыл, если кто подскажет, наверное сможет мне ответить, как лучше делать? непосредственно обращаться к памяти? вот так:
Код: Выделить всё
SUBPD   xmm1,  [EBX]

Или всеже загрузить в регистр а потом отнять? разница по времени ощутимая будет?

PS. Работаю с SSE впервые.
Последний раз редактировалось Maxizar 11.05.2011 21:41:26, всего редактировалось 1 раз.
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Sergei I. Gorelkin » 27.04.2011 23:51:20

Для почти всех команд SSE, кроме некоторых вроде movupd, адреса памяти должны быть кратны 16. Попробуй заменить movupd на movapd, если начнет падать на ней, значит дело в выравнивании. После того, как выровняешь, оставь movapd, она быстрее чем movupd.

БОльшая проблема состоит в том, что почти все SSE инструкции с аргументом типа "память" валятся в FPC c Internal Error. С регистрами не валятся. Третий день пытаюсь исправить (нужно переколбасить полвину x86ins.dat), но, боюсь, раньше окончания праздников это начинание не удастся завершить.

Maxizar писал(а):И да чуть не забыл, если кто подскажет, наверное сможет мне ответить, как лучше делать? непосредственно обращаться к памяти? вот так:

Тут не все так просто. Существуют понятия зависимости от предыдущего значения, латентности и производительности инструкций. Например, латентность может быть 0.5 (инструкция выполняется пол-цикла), а производительность 2 (следующая такая же инструкция может начать выполняться только через 2 цикла после предыдущей, зато между ними теоретически можно вставить еще 3 команды с латентностью 0.5 без потери производительности).
О зависимостях, если пишем так:
Код: Выделить всё
   movapd  xmm0, [eax]     (1)
   movapd  xmm1, [ebx]     (2)
   subpd    xmm0, xmm1     (3)

то команда (3) зависит от значения xmm1, присваиваемого командой (2), и должна дожидаться ее завешения. Но можно между ними вставить еще одну команду, не использующую xmm1, и она сможет выполняться параллельно с (2) или (3). Зато команда (2) не зависит от (1).
А если так
Код: Выделить всё
   movapd xmm0,[eax]
   subpd xmm0,[ebx]

то будет ни разу не быстрее, опять же из-за того, что второй команде нужен результат первой.
В общем, ручная оптимизация на таком уровне - это очень неблагодарное занятие. Особенно с учетом того, что каждое семейство процессоров ведет себя по-своему.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Maxizar » 28.04.2011 11:21:50

Поиграл с выравниванием, а если быть точным с выравниванием Record-а но, потом до меня дошло, что ведь SizeOf(Double) = 8, а SizeOf(TComplex) = 16 и тут все просто хорошо, именно так как нам и нужно.

Прочитал ваше сообщение еще раз и увидел, что адрес должен быть кратен 16. То есть наш массив или переменная должна лежать, начиная с адреса скажем FF так как: FF/16 = 16 а если будут лежать скажем с FE то будет ошибка, я правильно понял?

Почитал руководство по FPC а именно про директиву {$ALIGN x} Это ее нужно было использовать? Если да, то все равно ошибка при любом Х (1,2,4,8,16,32) команда MOVuPD отрабатывает хорошо, А вот команда MOVaPD вылетает. (проставлял директиву во всех модулях). Так же из-за этой проблемы (выравнивания) вылетает и арифметическая команда при обращении к памяти….

Кстати спасибо за объяснение по поводу зависимости скорости выполнения команды от выполнения предыдущей.. Это скорее всего сейчас справедливо и для простых команд процессора, потому что там вроде тоже стали применять конвейер. Эх, как я жалею, что у меня не было хорошего учителя как Вы в универе… :evil:
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Sergei I. Gorelkin » 28.04.2011 12:35:06

Maxizar писал(а):Прочитал ваше сообщение еще раз и увидел, что адрес должен быть кратен 16. То есть наш массив или переменная должна лежать, начиная с адреса скажем FF так как: FF/16 = 16 а если будут лежать скажем с FE то будет ошибка, я правильно понял?


Последняя цифра адреса в шестнадцатеричном представлении должна быть 0.
Если блок выделяется динамически, то картину может портить менеджер памяти, вставляющий в начало блока служебную информацию, причем {$align } при этом не влияет. Со статическими блоками {$align } вроде бы должна влиять.

Maxizar писал(а):Кстати спасибо за объяснение по поводу зависимости скорости выполнения команды от выполнения предыдущей.. Это скорее всего сейчас справедливо и для простых команд процессора, потому что там вроде тоже стали применять конвейер. Эх, как я жалею, что у меня не было хорошего учителя как Вы в универе…

Конвеер применяли начиная с первых пентиумов, а может даже и раньше (точно не помню), так что да, справедливо.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Maxizar » 28.04.2011 12:43:52

Sergei I. Gorelkin писал(а):Последняя цифра адреса в шестнадцатеричном представлении должна быть 0.

Ой FF = 255 а не 256 :oops: Бывает :oops: :oops: ...

Sergei I. Gorelkin писал(а):Если блок выделяется динамически, то картину может портить менеджер памяти, вставляющий в начало блока служебную информацию, причем {$align } при этом не влияет. Со статическими блоками {$align } вроде бы должна влиять.


Хм. значит в этом то и проблема, я обрабатываю динамический массив Комплексных чисел. У меня по адресу [EAX] первый элемент массива D то есть D[0].

Подтверждаю что, со статическим массивом все работает. При этом я даже убрал все директивы ALIGN, и все равно все работает… Проблема действительно только с динамическими массивами.
При этом работают как операции:
Код: Выделить всё
MOVuPD  xmm0,  [EAX]

так и операции:
Код: Выделить всё
MOVaPD  xmm0,  [EAX]


Так же со статическим массивом работает такая операция как:
Код: Выделить всё
ADDPD   xmm0,  [EBX]

и ей подобные.

Так что будем ждать исправлений для динамических массивов.

Добавлено спустя 4 часа 12 минут 51 секунду:
Кстати как записать в xmm регистр число, зная указатель я вроде понял:
Код: Выделить всё
MOVaPD  xmm0,  [EAX]

Все Ок...
А как записать в регистр xmm локальную переменную?
скажем вот так:
Код: Выделить всё
var
  CopyStartIndex :Integer;
  TempBn         :TComplex;
  DPointer       :Integer;
{$ASMMODE intel}
  asm
    push  EAX
    push  EBX
// Много кода :)

    MOVaPD  xmm0,  [EAX]
    MOVaPD  xmm1,   TempBn  <--- Ошибка

//Еще код

При этом Компилятор не ругается и компилирует все это дело, но при работе программа вылетает именно на этой строчке. а ошибка всё та же SIGSEGV :evil:
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Sergei I. Gorelkin » 28.04.2011 20:12:14

Я боюсь, что вот так взять и исправить динамические массивы не слишком-то получится. В них служебная информация от самого массива плюс от менеджера памяти. Та часть, что от менеджера, зависит от самого менеджера (который типа можно подменять), а для того менеджера, который по умолчанию - зависит от размера блока. Вдобавок все размеры отличаются для 32 и 64-битных платформ. И еще эта совместимость... с тем, что с simd никогда и не работало.

С другой стороны, компилятор вроде как умеет опознавать векторизуемые типы, но для этого нужно использовать массивы: type tcomplex=array[0..1] of double.

Со стеком тоже та еще песня, т.к. в win32 и linux32 нет требований к выравниванию стека на входе в процедуру (в darwin и x64 - есть), а локальные переменные отсчитываются от исходного значения esp в сторону уменьшения, то получить гарантированно выровненный адрес локальной переменной на уровне Паскаля не удастся никакими ухищрениями. Только объявлять процедуру как nostackframe, ручками выравнивать esp, переменные отсчитывать опять же ручками от выровненного значения в сторону увеличения...
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Maxizar » 11.05.2011 20:07:27

Вот, после праздников решил заняться программированием :)
По поводу команд MOVaPD и ей подобных, которые требуют выравнивания на 16 байт… долго ломал голову, но изучая и пырясь на Асм листинги, которые выдает команда:
Код: Выделить всё
-Amasm -al –s


Увидел строчку:
Код: Выделить всё
Align 16


Я долго чесал затылок, и не мог понять почему тогда ничего не работает… но мой кот «Грызлик» смотря на меня в упор требуя колбасы, привел меня на мысль об Отладчике… и о боже отключаем отладчик и блин все заработало…. (В тот день Грызлик слопал все запасы колбасных изделий...)
При этом его можно отключать не на глухо, а просто поставив оптимизацию выше O1. я ставил O2 или О3. И все хорошо компилилось и работало...
При этом работает как со статическими массивами и переменными, так и с Динамическими массивами, так что ничего переписывать не нужно...

При этом я так и не понял, каким Макаром, а именно какой директивой выставлять выравнивание? То что стоит по умолчанию 16 и оно как раз нужно для выравнивания в SSE2, конечно хорошо, а вдруг мне нужно скажем выравнивание на 8 байт или вообще 4…
Пробовал вот эти директивы:
Код: Выделить всё
{$A-}
{$A4}
{$ALIGN 4}
{$CODEALIGN 8}


Выставлял как в главном модуле так и в других… в асм Листинге все равно стоят строчки типа: Align 16
И при этом работает команда MOVaPD и ей подобные… (Значит и в правду 16 байт)

Проблема вроде как решена, но все же если не сложно подскажите на счет выравнивания на будущее.
Maxizar
постоялец
 
Сообщения: 385
Зарегистрирован: 20.03.2010 19:48:14

Re: SUBPD xmm, mem = SIGSEGV

Сообщение Sergei I. Gorelkin » 11.05.2011 21:24:48

Если честно, не знаю, какой директивой задавать выравнивание. {$codealign } скорее всего ни при чем, т.к. задает выравнивание меток кода, а не данных. Возможно также, что компилятор опознает тип как векторизуемый и выравнивает на 16 байт, игнорируя директивы.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1406
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

Рейтинг@Mail.ru