2011-10-26 1 views
2

У меня есть простая операция с плавающей запятой, которая всегда выполняется дважды. Поэтому я попытался перевести его на SSE, но он просто терпит неудачу. Язык высокого уровня - Delphi, так как он не поддерживает функции Intrinsics, я должен написать все это. В основном я просто параметр загрузки/выгрузки и некоторые умножений и addditions .:Нелинейная сборка неэффективной процедуры в Delphi с использованием SSE2

Procedure TLP1Poly2.Process(Const _a1, _b1, _OldIn1, _OldIn2, _OldOut1, _OldOut2:  Double; Var Sample1, Sample2: Double); 
Asm 
    MOVLPD XMM4, _a1 
    MOVHPD XMM4, _a1 
    MOVLPD XMM3, _b1 
    MOVHPD XMM3, _b1 
    // 
    MOVLPD XMM0, [Sample1] 
    MOVHPD XMM0, [Sample2] 
    MULPD XMM0, XMM4 
    // 
    MOVLPD XMM1, _OldIn1 
    MOVHPD XMM1, _OldIn2 
    MULPD XMM1, XMM4 
    // 
    MOVLPD XMM2, _OldOut1 
    MOVHPD XMM2, _OldOut2 
    MULPD XMM2, XMM3 
    // 
    ADDPD XMM0, XMM1 
    ADDPD XMM0, XMM2 
    // 
    MOVLPD [Sample1], XMM0 
    MOVHPD [Sample2], XMM0 
    // 
    // which stands for twice this: 
    // Sample:= Sample*a1 + oldinp*a1 + oldout*b1; 
    // 
End; 

, но эта процедура не работает, если я NOP "все между Sample1/Sample2 загрузки/сохранения, это нормально, но в остальном мой фильтр молчит. Какова основная вещь, которую я не получаю от SSE в этом?

Addenum:

старый класс класс:

constructor TLP1.create; 
begin 
    oldfreq := -1 ; 
end; 
procedure TLp1.process(inp,Frq,SR :single); 
begin 
    if Frq<>oldfreq then 
    begin 
     a := 2* SR; 
     t := Frq * _ppi; 
     n := 1/ (a+t) ; 
     b1:= (a - t) * n; 
    a1:= t * n; 
    oldfreq := frq; 
    end; 
    outlp := (inp+_kd)*a1 + oldinp*a1 + oldout*b1; 
    oldout := outlp ; 
    oldinp := inp; 
end; 

Новый класс:

Procedure TLP2Poly2.SetSamplerate(Const Value: Single); 
Begin 
    If Value = FSamplerate Then Exit; 
    FSamplerate := Value; 
    UpdateCoefficients; 
End; 

Procedure TLP2Poly2.SetFrequency(Const Value: Single); 
Begin 
If Value = FFrequency Then Exit; 
    FFrequency := Value; 
    UpdateCoefficients; 
End; 

Procedure TLP2Poly2.UpdateCoefficients; 
Var 
    a,t,n: Single; 
Begin 
    a := 2 * FSamplerate ; 
    t := FFrequency * 2 * pi; 
    n := 1/ (a+t) ; 
    b1:= (a - t) * n; 
    a1:= t * n; 
End; 

Procedure TLP2Poly2.Process(Var Sample1, Sample2: Double); 
Var 
    o1, o2: Double; 
Begin 
    o1 := Sample1; 
    o2 := Sample2; 
    IntProcess(a1, b1, OldIn1, OldIn2, OldOut1, OldOut2, Sample1, Sample2); 
    OldOut1 := Sample1; 
    OldOut2 := Sample2; 
    OldIn1 := o1; 
    OldIn2 := o2; 
End; 

Procedure TLP2Poly2.IntProcess(Const _a1, _b1, _OldIn1, _OldIn2, _OldOut1, _OldOut2: Double; Var Sample1, Sample2: Double); 
Asm 
    MOVLPD XMM4, _a1 
    MOVHPD XMM4, _a1 
    MOVLPD XMM3, _b1 
    MOVHPD XMM3, _b1 
    // 
    MOVLPD XMM0, [Sample1] 
    MOVHPD XMM0, [Sample2] 
    MULPD XMM0, XMM4 
    // 
    MOVLPD XMM1, _OldIn1 
    MOVHPD XMM1, _OldIn2 
    MULPD XMM1, XMM4 
    // 
    MOVLPD XMM2, _OldOut1 
    MOVHPD XMM2, _OldOut2 
    MULPD XMM2, XMM3 
    // 
    ADDPD XMM0, XMM1 
    ADDPD XMM0, XMM2 
    // 
    MOVLPD [Sample1], XMM0 
    MOVHPD [Sample2], XMM0 
End; 
+1

"Это не работает" не означает много. Что это за код? Что он должен делать, и что он делает вместо этого? Немного более подробно было бы очень полезно. –

+0

это фильтр LowPass, 1 полюс. исходный код помещается как комментарий в конец выделенного кода. a1 и b1 - коэффициенты, которые совпадают с исходной версией фильтра. – az01

+1

Какая именно процедура Delphi вы хотите перевести? Ваши замечания по коду - недостаточность. –

ответ

2

В Delphi есть отладчик панели ("FPU"), который показывает SSE регистров. Поэтому, если вы подаете свой фильтр на некоторые ненулевые значения, вы должны найти, откуда приходит тихий выход.

6

При написании ассемблера для Delphi, особенно в режиме 64 бит, вы всегда должны знать, как передаются параметры. Я никогда не использую имена первых 4 параметров, так как они все равно находятся в реестрах. Я использую эти регистры напрямую.

Обратите внимание, что _a1, _b1, _oldIn1 и _oldIn2 передаются в XMM0 - XMM3 соответственно, так что первая часть кода перезаписывает некоторые из этих регистров. Например, загрузка XMM3 с _b1 будет перезаписываться _oldIn2. То же самое происходит с XMM2, который содержит _oldIn1.

Было бы разумно изменить порядок использования вашего регистра, поэтому вам не нужно использовать память как промежуточную.

IOW, попробовать что-то вроде (непроверенные):

asm 
     MOVDDUP XMM0,XMM0 
     MOVDDUP XMM1,XMM1 

     MOVLPD XMM4,[Sample1] 
     MOVHPD XMM4,[Sample2] 
     MULPD XMM4,XMM0 

     // etc... 
+0

спасибо за совет. И я не был уверен, что если MOVDUP будет в порядке. Большая проблема заключается в том, что я использую некоторые старые автономные документы для изучения SSE (http://softpixel.com/~cwright/programming/simd/). – az01

+0

Попробуйте официальные документы от AMD или Intel. Их можно скачать бесплатно, как несколько .pdfs. –

+0

Чтобы загрузить в две половины, начните с 'movsd' вместо' movlpd'.Используйте «movlpd», если вы * хотите * слиться со старым значением и иметь зависимость от него. Кроме того, верхняя половина также может быть 'movlps' для сохранения одного байта размера кода. –