• dmk © (08.06.18 13:29) [0]
    Всем привет! Есть ли у кого возможность проверить AVX-опкоды?
    Взял опкоды из FreePascal и вставил в Delphi. Вроде все работает, но очень медленно. Может опкоды некорректные или Windows эмулятором их исполняет.
    На самом деле скорость должна быть очень высокая,
    т.к. XMM-версия этого кода работает раз в 20 быстрее.


     //vmovdqu ymm10, [R8]
     db $C4, $41, $7E, $6F, $10

     //vaddpd ymm10, ymm10, ymm13
     db $C4, $41, $2D, $58, $D5

     //vandpd ymm0, ymm10, ymm15
     db $C4, $C1, $2D, $54, $C7

     //vxorpd ymm0, ymm0, ymm15
     db $C4, $C1, $7D, $57, $C7

     //vptest ymm0, ymm0
     db $C4, $E2, $7D, $17, $C0
     jz @Inside
     dec X1
     jnz @X
     ret

    @Inside:
     //vmovdqa ymm1, ymm12
     db $C5, $7D, $7F, $E1

     //vmulpd ymm1, ymm1, ymm10
     db $C4, $C1, $75, $59, $CA

     //vmovdqa ymm4, ymm1
     db $C5, $FD, $7F, $CC

     //vmulpd ymm1, ymm1, ymm14
     db $C4, $C1, $75, $59, $CE

     //vextractf128 xmm2, ymm1, 01b
     db $C4, $E3, $7D, $19, $CA, $01

     addsd xmm2, xmm1
     psrldq xmm1, 8
     addsd xmm1, xmm2

     //Загружаем единицу
     movq xmm0, R13

     //интерполяция
     divsd xmm0, xmm1

     //Конвертируем в single
     cvtsd2ss xmm0, xmm0

     //fZ (тип Single)
     comiss xmm0, dword ptr [r10]
     jb @Below
     dec X1
     jnz @X
     ret

    @Below:
     movd dword ptr [r10], xmm0
     shufps xmm0, xmm0, 00b

     //vcvtpd2ps xmm4, ymm4
     db $C5, $FD, $5A, $E4

  • dmk © (08.06.18 15:07) [1]
    В общем проверил сам с помощью IDA Pro:
    https://hostingkartinok.com/show-image.php?id=e306db3aa4ea490e397dc50b818ced53

    IDA показывает правильный код.
    Даже не знаю куда теперь копать.

    Почему код может исполняется медленно?
  • Прохосый (10.06.18 03:23) [2]
    Так а можно полностью функцию-то? Обе версии? И пример входящих/результирующих данных?
    Тогда и запустим с радостью. :)
  • dmk © (10.06.18 11:03) [3]
    Процедура просто закрашивает полигон. Ничего интересного.
    Она вообще-то работает, но очень медленно.
    Такое ощущение, что код пропускается через эмулятор.


    procedure ScanLineVecD256(X0, X1: Integer; P: TVertexD);
    asm
     .NOFRAME

     sub X1, X0
     inc X1

     //Маска сборки ABGR-пиксела
     movq2dq xmm3, mm6

     //Множитель 255.0. Перед циклом загрузить в R12d
     movd xmm11, r12d
     shufps xmm11, xmm11, 00b

     //координаты точки
     //vmovdqu ymm10, [R8]
     db $C4, $41, $7E, $6F, $10

    @X:

     add r10, 4
     add r11, 4

     //vaddpd ymm10, ymm10, ymm13
     db $C4, $41, $2D, $58, $D5

     //vandpd ymm0, ymm10, ymm15
     db $C4, $C1, $2D, $54, $C7

     //vxorpd ymm0, ymm0, ymm15
     db $C4, $C1, $7D, $57, $C7

     //vptest ymm0, ymm0
     db $C4, $E2, $7D, $17, $C0

     jz @Inside
     dec X1
     jnz @X
     ret

    @Inside:

     //Копируем для умножения
     //vmovdqa ymm1, ymm12
     db $C5, $7D, $7F, $E1

     //vmulpd ymm1, ymm1, ymm10
     db $C4, $C1, $75, $59, $CA

     //vmovdqa ymm4, ymm1
     db $C5, $FD, $7F, $CC

     //vmulpd ymm1, ymm1, ymm14
     db $C4, $C1, $75, $59, $CE

     //Извлекаем (X+Y)
     //vextractf128 xmm2, ymm1, 01b
     db $C4, $E3, $7D, $19, $CA, $01
     //Складываем (X+Y)+Z
     addsd xmm2, xmm1
     psrldq xmm1, 8
     addsd xmm1, xmm2

     //Загружаем единицу
     movq xmm0, R13

     //интерполяция
     divsd xmm0, xmm1

     //Конвертируем в single
     cvtsd2ss xmm0, xmm0 //fZ = xmm0

     //На входе в XMM0 должен быть fZ (тип Single)
     comiss xmm0, dword ptr [r10] //Читаем Z-Buffer
     jb @Below
     dec X1
     jnz @X
     ret

    @Below:
     movd dword ptr [r10], xmm0 //Пишем fZ

     //Преобразуем цвет по вектору из Double в Single
     //vcvtpd2ps xmm4, ymm4
     db $C5, $FD, $5A, $E4

     cvtps2dq xmm4, xmm4 //Округляем сразу 4 значения
     pshufb xmm4, xmm3 //Собираем пиксел
     movd dword ptr [r11], xmm4 //Результат пишем по адресу пиксела

     dec X1
     jnz @X

    @Pass:
    end;
  • Pavia © (10.06.18 14:46) [4]

    > Такое ощущение, что код пропускается через эмулятор.

    А почему нет? Интел как раз таки выпускала эмулятор для AVX. Может по ошибки установили? А ваш проц точно поддерживает AVX?

    Вы бы код целиком выложили тогда потестировать можно было-бы.
  • dmk © (10.06.18 15:57) [5]
    У меня и AVX и AVX2 поддерживается. Проц i7-6950X.

    Вот тоже самое, только XMM с типом Single.
    AVX нужен, чтобы полностью помещался тип Double, т.е. 256 бит.


    procedure ScanLineVecS(X0, X1: Integer; P: TVertexD);
    asm
     .NOFRAME

     sub X1, X0
     inc X1

     cvtsd2ss xmm10, [P]
     cvtsd2ss xmm0, [P + 8]
     cvtsd2ss xmm1, [P + 16]

     insertps xmm10, xmm0, 00010000b
     insertps xmm10, xmm1, 00100000b

    @X:

     add r10, 4
     add r11, 4

     addps xmm10, xmm13
     movdqa xmm12, xmm10

     pand xmm12, xmm15
     pxor xmm12, xmm15
     ptest xmm12, xmm12
     jz @Inside
     dec X1
     jnz @X
     ret

    @Inside:

     movdqa xmm1, xmm11

     mulps xmm1, xmm10

     movdqa xmm4, xmm1

     mulps xmm1, xmm14
     haddps xmm1, xmm1
     haddps xmm1, xmm1

     movd xmm0, R13d //Single-Единица должна быть в R13
     divss xmm0, xmm1

     comiss xmm0, dword ptr [r10]
     jb @Below
     dec X1
     jnz @X
     ret

    @Below:
     movd dword ptr [r10], xmm0
     movd dword ptr [r11], xmm0

     dec X1
     jnz @X
    end;

  • dmk © (10.06.18 21:00) [6]
    В общем вот рабочий код. Только под 64 бита.

    У меня получаются такие значения:
    Test XMM: 2,36 сек. (4 239 084,36 в сек.)
    Test AVX: 149,95 сек. (66 687,56 в сек.)
    10 млн. сканлиний с очисткой буфера.
    В коде константа N.
    Похоже, что исполняется на эмуляторе, а не на железе.
    Код одинаковый. Разница лишь в типах данных: 128 бит и 256 бит.
  • dmk © (10.06.18 21:01) [7]

    program AVX_test;

    {$APPTYPE CONSOLE}

    {$R *.res}

    uses
     System.SysUtils, Winapi.Windows;

    type
     TVertex = packed record
       X, Y, Z, W: Single;
     end;

    type
     TVertexD = packed record
       X, Y, Z, W: Double;
     end;

    type
     TTripleVertex = packed record
       C0, C1, C2: TVertex;
     end;

    type
     XMM128D = array[0..3] of UInt32;

    type
     AVX256Q = array[0..3] of UInt64;

    type
     QWord = UInt64;

    const
     M255: Single = 255.0;
     GatherColors: UInt32 = $FF000408;
     SingleOne: Single = 1.0;
     DoubleOne: Double = 1.0;

     //-----------------------------------------------------

    procedure LoadDataS(CV, bStep, Z: Pointer; Area: Double);
    const
     InsideMask: XMM128D = ($80000000, $80000000, $80000000, $0);

    asm
     .NOFRAME

     movdqu xmm7, dqword ptr [CV]
     movdqu xmm8, dqword ptr [CV + 16]
     movdqu xmm9, dqword ptr [CV + 32]

     pxor xmm2, xmm2

     //bStep -> rdx
     cvtsd2ss xmm13, qword ptr [bStep]
     cvtsd2ss xmm0, qword ptr [bStep + 8]
     cvtsd2ss xmm1, qword ptr [bStep + 16]

     insertps xmm13, xmm0, 00010000b // Y
     insertps xmm13, xmm1, 00100000b // Z
     insertps xmm13, xmm2, 00110000b // Ноль

     //Z -> r8
     cvtsd2ss xmm14, qword ptr [Z]
     cvtsd2ss xmm0, qword ptr [Z + 8]
     cvtsd2ss xmm1, qword ptr [Z + 16]

     insertps xmm14, xmm0, 00010000b // Y
     insertps xmm14, xmm1, 00100000b // Z
     insertps xmm14, xmm2, 00110000b // Ноль

     mov R13, [DoubleOne]
     mov R12d, [M255]
     movd mm6, [GatherColors]
     movdqu xmm15, [InsideMask]

     movq xmm11, R13
     divsd xmm11, Area
     cvtsd2ss xmm11, xmm11
     shufps xmm11, xmm11, 00b
     insertps xmm11, xmm2, 00110000b // Ноль

     mov R13d, [SingleOne]
    end;

    //-----------------------------------------------------

    procedure LoadDataD256(CV, bStep, Z: Pointer; Area: Double);
    const
     InsideMask: AVX256Q = ($8000000000000000, $8000000000000000, $8000000000000000, $0000000000000000);

    asm
     .NOFRAME

     //vzeroupper
     //db $C5, $F8, $77

     mov R12d, [M255]
     movd mm6, [GatherColors]

     movdqu xmm7, dqword ptr [CV]
     movdqu xmm8, dqword ptr [CV + 16]
     movdqu xmm9, dqword ptr [CV + 32]

     //bStep находится в rdx
     //vmovdqu ymm13, [rdx][bStep]
     db $C5, $7E, $6F, $2A

     //InsideMask
     lea rax, qword ptr [InsideMask]
     //vmovdqu ymm15, [rax]
     db $C5, $7E, $6F, $38

     //Z находится в r8
     //vmovdqu ymm14, [r8]
     db $C4, $41, $7E, $6F, $30

     movsd xmm12, Area
     //vinsertf128 ymm12, ymm12, xmm12, 01b
     db $C4, $43, $1D, $18, $E4, $01
     shufpd xmm12, xmm12, 00b
    end;

    //-----------------------------------------------------

    procedure ScanLineVecS(X0, X1: Integer; P: TVertexD);
    asm
     .NOFRAME

     sub X1, X0
     inc X1

     cvtsd2ss xmm10, [P]
     cvtsd2ss xmm0, [P + 8]
     cvtsd2ss xmm1, [P + 16]

     insertps xmm10, xmm0, 00010000b
     insertps xmm10, xmm1, 00100000b

    @X:

     add r10, 4
     add r11, 4

     addps xmm10, xmm13
     movdqa xmm12, xmm10

     pand xmm12, xmm15
     pxor xmm12, xmm15
     ptest xmm12, xmm12
     jz @Inside

     dec X1
     jnz @X
     ret

    @Inside:

     movdqa xmm1, xmm11
     mulps xmm1, xmm10
     movdqa xmm4, xmm1

     mulps xmm1, xmm14
     movdqa xmm0, xmm1
     psrldq xmm0, 4
     addss xmm1, xmm0
     psrldq xmm0, 4
     addss xmm1, xmm0

     movd xmm0, R13d //Единица должна быть в R13
     divss xmm0, xmm1

     comiss xmm0, dword ptr [r10]
     jb @Below
     dec X1
     jnz @X
     ret

    @Below:
     movd dword ptr [r10], xmm0 //Пишем fZ
     movd dword ptr [r11], xmm0 //Результат пишем по адресу пиксела

     dec X1
     jnz @X
    end;

    //-----------------------------------------------------

    procedure ScanLineVecD256(X0, X1: Integer; P: TVertexD);
    asm
     .NOFRAME

     sub X1, X0
     inc X1

     //Маска сборки ABGR-пиксела
     movq2dq xmm3, mm6

     //Множитель 255.0
     movd xmm11, [M255]
     shufps xmm11, xmm11, 00b

     //Координаты точки
     //vmovdqu ymm10, (R8) P
     db $C4, $41, $7E, $6F, $10

    @X:

     add r10, 4
     add r11, 4

     //vaddpd ymm10, ymm10, ymm13
     db $C4, $41, $2D, $58, $D5

     //vandpd ymm0, ymm10, ymm15
     db $C4, $C1, $2D, $54, $C7
     //vxorpd ymm0, ymm0, ymm15
     db $C4, $C1, $7D, $57, $C7
     //vptest ymm0, ymm0
     db $C4, $E2, $7D, $17, $C0

     jz @Inside
     dec X1
     jnz @X
     ret

    @Inside:

     //vmovdqa ymm1, ymm12
     db $C5, $7D, $7F, $E1

     //vmulpd ymm1, ymm1, ymm10
     db $C4, $C1, $75, $59, $CA

     //vmovdqa ymm4, ymm1
     db $C5, $FD, $7F, $CC

     //vmulpd ymm1, ymm1, ymm14
     db $C4, $C1, $75, $59, $CE

     //vextractf128 xmm2, ymm1, 01b
     db $C4, $E3, $7D, $19, $CA, $01

     //Складываем (X+Y)+Z
     addsd xmm2, xmm1
     psrldq xmm1, 8
     addsd xmm1, xmm2

     //Загружаем единицу
     movq xmm0, R13 //Единица должна быть в R13

     //Перспективная Z-интерполяция
     //1.0 / (PL.X * Z.X + PL.Y * Z.Y + PL.Z * Z.Z)
     divsd xmm0, xmm1

     //Конвертируем в single
     cvtsd2ss xmm0, xmm0 //fZ = xmm0

     //На входе в XMM0 должен быть fZ (тип Single)
     comiss xmm0, dword ptr [r10] //Читаем Z-Buffer
     jb @Below
     dec X1
     jnz @X
     ret

    @Below:
     movd dword ptr [r10], xmm0 //Пишем fZ
     //vcvtpd2ps xmm4, ymm4
     db $C5, $FD, $5A, $E4
     movd dword ptr [r11], xmm4 //Результат пишем по адресу пиксела

     dec X1
     jnz @X

    @Pass:
    end;

    //-----------------------------------------------------

    procedure LoadAddr(A1, A2: QWord);
    asm
     .NOFRAME

     mov R10, A1
     mov R11, A2
    end;

    //-----------------------------------------------------

    procedure StoreQWords(dest, count: qword; q: qword);
    asm
     .NOFRAME
     mov r9, rdi

     mov rdi, dest
     mov rcx, count
     mov rax, q
     rep stosq

     mov rdi, r9
    end;

    //-----------------------------------------------------

    function SingleAsQWord(S: Single): UInt64;
    asm
     .NOFRAME

     movd eax, S
     mov r8d, eax
     shl rax, 32
     or rax, r8
    end;

    //-----------------------------------------------------
  • dmk © (10.06.18 21:01) [8]
    Вторая часть:


    const
     Len: Integer = 100;
     N: Integer = 10000000;

    var
     P1, P2: Pointer;
     Q1, Q2: QWord;
     QV1, QV2: QWord;
     i: Integer;
     P, Step, Z: TVertexD;
     CV: TTripleVertex;
     Area, tt: Double;
     ts, te: QWord;

    begin
     GetMem(P1, Len * SizeOf(Single));
     GetMem(P2, Len * SizeOf(Single));

     P.X := -25.0;
     P.Y := -121.0;
     P.Z := -21.0;
     P.W := 1.0;

     Step.X := -1.0;
     Step.Y := -1.0;
     Step.Z := -1.0;
     Step.W := 0.0;

     CV.C0.X := 1.0;
     CV.C0.Y := 1.0;
     CV.C0.Z := 1.0;
     CV.C0.W := 1.0;

     CV.C1.X := 0.0;
     CV.C1.Y := 0.0;
     CV.C1.Z := 0.0;
     CV.C1.W := 1.0;

     CV.C2.X := 1.0;
     CV.C2.Y := 1.0;
     CV.C2.Z := 1.0;
     CV.C2.W := 1.0;

     Z.X := 100.57;
     Z.Y := 98.15;
     Z.Z := 101.3;
     Z.W := 1.0;

     Area := -0.00340202034;

     Q1 := QWord(P1);
     Q2 := QWord(P2);

     QV1 := SingleAsQWord(5000.0);

     try
       // XMM-сканлиния ----------------------------------

       LoadDataS(@CV, @Step, @Z, Area);

       ts := GetTickCount;

       //Рисуем сканлинии
       for i := 0 to N do
       begin
         //Обновим адрес
         LoadAddr(Q1, Q2);
         //Рисуем сканлинию
         ScanLineVecS(0, Len - 1, P);
         //Очистка буфера
         StoreQWords(Q1, Len shr 1, QV1);
       end;

       te := GetTickCount;
       tt := (te - ts) / 1000.0;

       Writeln('Test XMM: ' + FloatToStrF(tt, ffNumber, 18, 2) + ' сек. (' + FloatToStrF(N / tt, ffNumber, 18, 2) + ' в сек.)');

       // AVX-сканлиния ----------------------------------

       LoadDataD256(@CV, @Step, @Z, Area);

       ts := GetTickCount;

       //Рисуем сканлинии
       for i := 0 to N do
       begin
         //Обновим адрес
         LoadAddr(Q1, Q2);
         //Рисуем сканлинию
         ScanLineVecD256(0, Len - 1, P);
         //Очистка буфера
         StoreQWords(Q1, Len shr 1, QV1);
       end;

       te := GetTickCount;
       tt := (te - ts) / 1000.0;

       Writeln('Test AVX: ' + FloatToStrF(tt, ffNumber, 18, 2) + ' сек. (' + FloatToStrF(N / tt, ffNumber, 18, 2) + ' в сек.)');
       Readln;

     except
     end;

     FreeMem(P1);
     FreeMem(P2);
    end.
  • dmk © (10.06.18 23:51) [9]
    Перенес во FreePascal - то же самое. XMM шустро, а AVX тормоза жуткие.
  • invis © (11.06.18 03:06) [10]
    Добавь vzeroupper после каждого перехода от AVX к SSE, и нормальную загрузку единицы (которая DoubleOne) в R13 для AVX.
    Вообще сомнительная практика - грузить регистры в одной функции, использовать в другой. Даже если "вроде работает".
    Ну и традиционное пожелание: не маяться дурью и переходить на Си.
  • dmk © (11.06.18 11:55) [11]
    И с vzeroupper пробовал и без - никакой разницы.
    Предзагрузка переменных в регистры - это обычная практика еще со времен DOS'а.
    Зачем мне счетчик в памяти, если у меня куча пустых регистров простаивает?
    ScanLine - это всего лишь часть большей процедуры. ~3Kb кода.

    >не маяться дурью и переходить на Си.
    Студия 2018 уже стоит. Дизассемблирование кода c++ показывает, что у c++ преимуществ перед Pascal нет.Только всякие вкусняшки на уровне языка. К ним еще привыкнуть надо.
  • Pavia © (11.06.18 13:29) [12]
    У меня тоже тормозит
    Test XMM: 9,50 сек. (1 052 631,58 в сек.)
    Test AVX: 427,84 сек. (23 373,01 в сек.)
  • dmk © (11.06.18 13:53) [13]
    >У меня тоже тормозит
    А у Вас OS какая?
  • dmk © (11.06.18 13:58) [14]
    Самое интересное, что если отключить AVX-вычисления и оставить просто загрузку в YMM-регистры, то скорость не падает. Т.е. работает железка, причем очень шустро.

    Просто оставил вот этот кусок кода:
     //vmovapd ymm10, [r8]
     db $C4, $41, $7D, $28, $10


    Никаких тормозов. Очень быстро. Только массив пришлось выравнивать на 32 байта.
    Рекомендация Intel.
  • invis © (11.06.18 17:50) [15]
    https://drive.google.com/open?id=1HcYPkg-b2WXtijVDDpWNrNuNsAQXy0GU


    > Дизассемблирование кода c++ показывает, что у c++ преимуществ
    > перед Pascal нет


    Надо уметь так писать, чтобы были преимущества. Например, (полу)автоматическая векторизация в Си хоть как-то работает, а в Дельфи её совсем нет.
  • dmk © (11.06.18 19:12) [16]
    >автоматическая векторизация в Си хоть как-то работает
    Это просто библиотеки. Они к языку никакого отношения не имеют.
    У меня своя векторная 2D/3D библиотека для Delphi, так что и Delphi теперь умеет :)
    Это дело наживное. Просто для C++ действительно много библиотек написано.
    А термин автоматическая векторизация для меня мало понятен.

    Если речь о таком (см. заголовки), то у меня есть немного.


    type
     PVertex = ^TVertex;
     TVertex = packed record
       X, Y, Z, W: Single;
     public
       class operator Add(A, B: TVertex): TVertex; overload;
       class operator Add(A: TVertex; N: Single): TVertex; overload;
       class operator Subtract(A, B: TVertex): TVertex; overload;
       class operator Subtract(A: TVertex; N: Single): TVertex; overload;
       class operator Negative(A: TVertex): TVertex;
       class operator Positive(A: TVertex): TVertex;
       class operator Inc(A: TVertex): TVertex; inline;
       class operator Dec(A: TVertex): TVertex; inline;
       class operator Trunc(A: TVertex): TVertex; inline;
       class operator Round(A: TVertex): TVertex; inline;
       class operator Multiply(A, B: TVertex): TVertex; overload;
       class operator Multiply(A: Single; B: TVertex): TVertex; overload;
       class operator Multiply(A: TVertex; B: Single): TVertex; overload;
       class operator Multiply(A: TVertex; M: TMatrix4): TVertex; overload;
       class operator Divide(A: TVertex; B: Single): TVertex; overload;
       class operator Divide(A: Single; B: TVertex): TVertex; overload;
       function Angle(A, B: TVertex): Single; inline; //Угол между двумя точками
       function Cross(A, B: TVertex): TVertex; inline; //Перпендикуляр к вектору
       function Distance(A, B: TVertex): Single; inline; //Расстояние между точками
       function Dot(A, B: TVertex): Single; inline; //Скалярное произведение векторов
       function Length(A: TVertex): Single; inline;
       function Middle(A, B: TVertex): TVertex; inline; //Середина вектора
       function Normalize(A: TVertex): TVertex; inline;
       procedure NormalizeAngles(A: TVertex); inline;
       function Scale(A, B: TVertex; F: Single): TVertex; inline; //Масштабирует вектор F = [0..1]
       procedure Split(var X, Y, Z: Single); inline;
     end;
  • dmk © (11.06.18 19:15) [17]
    >Надо уметь так писать, чтобы были преимущества.
    Asssembler = Assembler.
    Какие у ассемблера перед ассемблером могут быть преимущества?
  • invis © (11.06.18 22:27) [18]
    Преимущество в том, что пишем на Си (ну может с незначительными интринсиковыми вставками), а получаем ту же скорость, что и на ассемблере.
    Есть внешние библиотеки, есть встроенные в язык средства. Но даже внешние работают куда лучше чем в Дельфи по причине более эффективного inline.
  • invis © (11.06.18 23:19) [19]
    Про инлайн (FPC, но в Дельфи то же самое):
    http://www.sql.ru/forum/1281542-3/itogi-2017-goda?mid=21132202#21132202
    Автовекторизация:
    https://godbolt.org/g/Uv318K
    компилятору никто не говорил, что там можно применить SIMD (цикл vmovupd/vcmpltpd), но он сам догадался.
  • dmk © (11.06.18 23:32) [20]
    >компилятору никто не говорил, что там можно применить SIMD
    >(цикл vmovupd/vcmpltpd), но он сам догадался.

    Delphi тоже про SIMD знает. Вплоть до 4.2. AVX на стадии разработки.
    В планах Embarcadero вроде есть. Ручная оптимизация по любому лучше.
    Я все оптимизирую сам. Мне не лень.
  • invis © (12.06.18 00:46) [21]
    Сам компилятор Дельфи использует максимум SSE2 в скалярном режиме, сишные - до AVX-512 в векторном.
    Вся эта ветка наглядно показывает "прелести" ручной оптимизации, когда не хватает 3-х инструкций - и в 20 раз медленнее.
    Ну да ладно. Не лень - так не лень, что я буду уговаривать.
  • DevilDevil_ © (16.06.18 01:00) [22]
    Причину в итоге нашли?
  • dmk © (16.06.18 02:39) [23]
    >Причину в итоге нашли?
    Неа. Объяснение только одно - работает эмулятор, а не железо.
    У многих тормоза с авэиксом. Куча аналогичных вопросов на интеле.
    Я их спросил, но пока молчат.
  • invis © (16.06.18 04:02) [24]
    Я же выкладывал исправленную версию в (15). Правда, она все равно медленнее sse, но в разумных пределах медленнее.
  • dmk © (16.06.18 17:03) [25]
    invis ©   (16.06.18 04:02) [24]
    Эх Шаман Петрович! Счастья Вам! Учитывая, что объем данных в AVX больше в 2 раза, то получается AVX слегка быстрее читает из памяти, но в целом чуть медленнее XMM. Но думаю, что оптимизация это просто вопрос времени. В будущих поколениях CPU будет шустрее. Возможно ;)
  • dmk © (16.06.18 21:40) [26]
    В общем VZEROUPPER не очень помогает. Она что-то рушит, что нарушает вычисления.
    Без нее все считается правильно, а с ней быстро, но расчеты неправильные.
  • invis © (17.06.18 12:50) [27]
    Переделай всю процедуру на AVX-команды, VZEROUPPER только в конце.
    Сишные компиляторы так делают, даже если работают со 128-битными векторами (xmm регистры), команды всё равно с префиксом "v".
    https://godbolt.org/g/8267vi
  • dmk © (17.06.18 21:32) [28]
    Всю процедуру невозможно переделать на 256 бит к сожалению. Эта процедура часть большей процедуры. Цикл отрисовки 3D-треугольника. В XMM-регистрах данные не рушатся, а у меня куча переменных в регистрах. VZEROUPPER трет их, а это значит, что нужна новая загрузка из памяти. Получаются AVX-тормоза. Кроме того некоторых AVX-команд просто нет в «природе». Это дополнительная конверсия данных из 256 бит в 128. Получается жуть и очень медленно. Разница по скорости в разы в пользу XMM.

    Сейчас у меня есть новая XMM-версия с упакованными Double.
    По скорости приближается к Single.

    Сцена XMM-Single ~150 fps (но тут не хватает точности) Еще бы пару бит :)
    Сцена XMM-Double ~100 fps (точности хватает)
    Сцена AVX ~25 fps (точности хватает)

    В общем AVX пока отменяется.
  • invis © (18.06.18 12:55) [29]
    Я думаю, все нужные команды в AVX есть, компиляторы же как-то справляются.
    Если есть рисование треугольника на Дельфи, без ассемблера - выкладывай, посмотрю.
  • dmk © (18.06.18 17:58) [30]
    >все нужные команды в AVX есть
    К сожалению нет. Есть только в AVX+AVX-2. У меня нет AVX-2 компилятора, чтобы опкоды взять.

    >без ассемблера
    Без ассемблера в Delphi нельзя, т.к. в Delphi есть баг. Конверсия single-double автоматическая, что очень напрягает. Из-за этого расчеты неверные. Опция компилятора отключающая эту конверсию не работает. Все равно конвертит.

    >выкладывай, посмотрю
    В паблик ни за что.
  • invis © (18.06.18 23:15) [31]

    > У меня нет AVX-2 компилятора

    FPC же, с параметрами -O4 -Opcoreavx2 -Cfavx2 -Cpcoreavx2


    > Конверсия single-double автоматическая, что очень напрягает.
    >  Из-за этого расчеты неверные. Опция компилятора отключающая
    > эту конверсию не работает.

    Из single в double отключается. Или он ещё наоборот конвертит?
  • dmk © (18.06.18 23:50) [32]
    >Или он ещё наоборот конвертит?
    Он туда-сюда конвертит без спроса. Представление чисел получается разное.
    cvtsd2ss и cvtss2sd ставит везде без спроса.

    Есть такая директива, она на SIMD не распростроняется, только FPU.
    http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Floating_point_precision_control_(Delphi_for_x64)
  • dmk © (19.06.18 00:00) [33]
    Ан нет. Извиняюсь. Все таки удалось заставить его чисто в single считать.

    procedure TestSingle;
    var
     a, b, c: Single;

    begin
     {$EXCESSPRECISION OFF}
     a := 2.52355235;
     b := 3.57235224;
     c := (a + b);
     {$EXCESSPRECISION ON}
    end;


    Получается такой код:
    Wu.pas.1324: a := 2.52355235;
    00000000007858FD C74564E2812140   mov [rbp+$64],$402181e2
    Wu.pas.1325: b := 3.57235224;
    0000000000785904 C745606BA16440   mov [rbp+$60],$4064a16b
    Wu.pas.1326: c := (a + b);
    000000000078590B F30F104564       movss xmm0,dword ptr [rbp+$64]
    0000000000785910 F30F584560       addss xmm0,dword ptr [rbp+$60]
    0000000000785915 F30F11455C       movss dword ptr [rbp+$5c],xmm0


    А вот что без директивы:
    Wu.pas.1324: a := 2.52355235;
    00000000007858FD C74564E2812140   mov [rbp+$64],$402181e2
    Wu.pas.1325: b := 3.57235224;
    0000000000785904 C745606BA16440   mov [rbp+$60],$4064a16b
    Wu.pas.1326: c := (a + b);
    000000000078590B F3480F5A4564     cvtss2sd xmm0,qword ptr [rbp+$64]
    0000000000785911 F3480F5A4D60     cvtss2sd xmm1,qword ptr [rbp+$60]
    0000000000785917 F20F58C1         addsd xmm0,xmm1
    000000000078591B F2480F5AC0       cvtsd2ss xmm0,xmm0
    0000000000785920 F30F11455C       movss dword ptr [rbp+$5c],xmm0
Есть новые Нет новых   [118460   +13][b:0][p:0.002]