Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 05.06.2015, 13:13   #1
Sibedir
Тот ещё
Старожил
 
Аватар для Sibedir
 
Регистрация: 14.11.2007
Сообщений: 2,242
По умолчанию процедура Exchange на BASM

Вот оно ДЕТИЩЕ
Код:
// Меняет значения между Value1 и Value2
procedure Exchange (var Value1, Value2; const Size: Integer);
asm
        PUSH    ESI
@@begin:
        CMP     ECX, 4
        JB      @@3
        CMP     ECX, 8
        JB      @@4
@@8:
        MOV     ESI, [EAX]
        MOV     EBX, [EDX]
        MOV     [EAX], EBX
        MOV     [EDX], ESI
        ADD     EAX, 4
        ADD     EDX, 4
        SUB     ECX, 4
@@4:
        MOV     ESI, [EAX]
        MOV     EBX, [EDX]
        MOV     [EAX], EBX
        MOV     [EDX], ESI
        ADD     EAX, 4
        ADD     EDX, 4
        SUB     ECX, 4
        JMP     @@begin
@@3:
        JCXZ    @@end
        CMP     CL, 2
        JB      @@1
@@2:
        MOV     BX, [EAX]
        SHL     EBX, 8*2
        MOV     BX, [EDX]
        MOV     [EAX], BX
        SHR     EBX, 8*2
        MOV     [EDX], BX
        ADD     EAX, 2
        ADD     EDX, 2
        SUB     CL, 2
        JMP     @@3
@@1:
        MOV     BL, [EAX]
        MOV     BH, [EDX]
        MOV     [EAX], BH
        MOV     [EDX], BL
@@end:
        POP     ESI
end;
Sibedir вне форума Ответить с цитированием
Старый 05.06.2015, 13:13   #2
Sibedir
Тот ещё
Старожил
 
Аватар для Sibedir
 
Регистрация: 14.11.2007
Сообщений: 2,242
По умолчанию

Это еще чё. Было вот так
Код:
procedure Exchange_ (var Value1, Value2; const Size: Integer);
asm
@@begin:                            {счетчик = ?}
        CMP     ECX, $04            // счетчик COMPARE 4
        JNZ     @@N4                // IF <> THEN @@N4

@@E4:                               {счетчик = 4}
        MOV     EBX, [EAX]          // \
        MOV     ECX, [EDX]          //  \ обмен данных
        MOV     [EAX], ECX          //  / размером 4 байта
        MOV     [EDX], EBX          // /
        RET                         // выход

@@N4:                               {счетчик <> 4}
        JA      @@A4                // IF > THEN @@A4

@@B4:                               {счетчик < 4}
        CMP     ECX, $02            // счетчик COMPARE 2
        JNZ     @@N2                // IF <> THEN @@N2

@@E2:                               {счетчик = 2}
        MOV     BX, WORD PTR [EAX]  // \
        MOV     CX, WORD PTR [EDX]  //  \ обмен данных
        MOV     [EAX], CX           //  / размером 2 байта
        MOV     [EDX], BX           // /
        RET                         // выход

@@N2:                               {счетчик <> 2   [0, 1, 3]}
        JCXZ    @@E0                // IF счетчик = 0 THEN @@E0
        JA      @@E3                // ELSE IF > THEN @@E3

@@E1:                               {счетчик = 1}
        MOV     BL, BYTE PTR [EAX]  // \
        MOV     CL, BYTE PTR [EDX]  //  \ обмен данных
        MOV     [EAX], CL           //  / размером 1 байт
        MOV     [EDX], BL           // /

@@E0:                               {счетчик = 0}
        RET                         // выход

@@E3:                               {счетчик = 3 (собсем уж редкость)}
        MOV     BL, BYTE PTR [EAX]  // \
        MOV     CL, BYTE PTR [EDX]  //  \ обмен данных
        MOV     [EAX], CL           //  / размером 1 байт
        MOV     [EDX], BL           // /
        INC     EAX                 // \ переход на
        INC     EDX                 // / следующий адрес
        MOV     BX, WORD PTR [EAX]  // \
        MOV     CX, WORD PTR [EDX]  //  \ обмен данных
        MOV     [EAX], CX           //  / размером 2 байта
        MOV     [EDX], BX           // /
        RET                         // выход

@@A4:                               {счетчик > 4}
        PUSH    ESI
        MOV     BX,  CX             // хвост = счетчик
        AND     BX,  $0003          //         MOD 4
        SHR     ECX, $02            // счетчик обмена = счетчик DIV 4
        CMP     BX, 0               // хвост COMPARE 0
        JNZ     @@tail              // if <> THEN замена с хвостом

// ОБМЕН БЕЗ ХВОСТА ------------------------------------------------------------
@@cycle:                            // ЦИКЛ ОБМЕНОВ ----------------------------
        MOV      ESI,  [EAX]        // | \
        MOV      EBX,  [EDX]        // |  \ обмен данных
        MOV     [EAX],  EBX         // |  / размером 4 байта
        MOV     [EDX],  ESI         // | /
        ADD      EAX, 4             // | \  переход на
        ADD      EDX, 4             // | /  следующий адрес
        LOOP    @@cycle             // |----------------------------------------
        POP      ESI
        RET                         // выход

// ОБМЕН С ХВОСТОМ -------------------------------------------------------------
@@tail:
        PUSH    BX                  // запоминаем хвост
@@cycle2:                           // ЦИКЛ ОБМЕНОВ ----------------------------
        MOV      ESI,  [EAX]        // | \
        MOV      EBX,  [EDX]        // |  \ обмен данных
        MOV     [EAX],  EBX         // |  / размером 4 байта
        MOV     [EDX],  ESI         // | /
        ADD      EAX, 4             // | \  переход на
        ADD      EDX, 4             // | /  следующий адрес
        LOOP    @@cycle2            // |----------------------------------------
        POP      CX                 // вспоминаем хвост
        POP      ESI
        JMP     @@B4                // обрабатываем хвост
@@end:
end;
Sibedir вне форума Ответить с цитированием
Старый 08.09.2015, 15:21   #3
Dmitro25
 
Регистрация: 19.11.2010
Сообщений: 7
По умолчанию

По хорошему, надо бы ещё EBX в стеке сохранять
Dmitro25 вне форума Ответить с цитированием
Старый 08.09.2015, 17:10   #4
Vapaamies
Ваш К. О.
Участник клуба
 
Аватар для Vapaamies
 
Регистрация: 26.12.2012
Сообщений: 1,770
По умолчанию

Что это за фигня? Почему нет ни одной инструкции XCHG?
Vapaamies вне форума Ответить с цитированием
Старый 09.09.2015, 12:10   #5
Sibedir
Тот ещё
Старожил
 
Аватар для Sibedir
 
Регистрация: 14.11.2007
Сообщений: 2,242
По умолчанию

Цитата:
Сообщение от Dmitro25 Посмотреть сообщение
По хорошему, надо бы ещё EBX в стеке сохранять
Насколько я понял, если компилятор чтит паскалевские соглашения, то не нужно. EBX не будет после отработки этой подпрограммы использоваться как указатель. На тестах всё было нормально.
Но я могу ошибаться.
Я на счет ESI тоже до конца не понял, собственно поэтому и сохраняю его в стек.

Цитата:
Сообщение от Vapaamies Посмотреть сообщение
Что это за фигня? Почему нет ни одной инструкции XCHG?
Обработка предполагалась однопотоковая + вот это: http://www.programmersforum.ru/showthread.php?t=279011

Последний раз редактировалось Sibedir; 09.09.2015 в 12:13.
Sibedir вне форума Ответить с цитированием
Старый 09.09.2015, 13:45   #6
Dmitro25
 
Регистрация: 19.11.2010
Сообщений: 7
По умолчанию

Цитата:
Сообщение от Vapaamies Посмотреть сообщение
Что это за фигня? Почему нет ни одной инструкции XCHG?
С XCHG значительно медленнее.

Предложу свою модификацию:
Код:
procedure MyExchange2(var Value1, Value2; const Size: Integer);
asm
        PUSH    ESI
        push    EBX
@@beg8:
        sub     ecx, 8
        jc      @@beg4
        MOV     ESI, [EAX+ecx+4]
        MOV     EBX, [EDX+ecx+4]
        MOV     [EDX+ecx+4], ESI
        MOV     [EAX+ecx+4], EBX
        MOV     ESI, [EAX+ecx]
        MOV     EBX, [EDX+ecx]
        MOV     [EDX+ecx], ESI
        MOV     [EAX+ecx], EBX
        jnz     @@beg8
        jmp     @@end
@@beg4: add     ecx, 4
        jnc     @@beg2
        MOV     ESI, [EAX+ecx]
        MOV     EBX, [EDX+ecx]
        MOV     [EDX+ecx], ESI
        MOV     [EAX+ecx], EBX
        jz      @@end
        sub     ecx, 4
@@beg2: add     ecx, 2
        jnc     @@beg1
        mov     SI, word ptr [EAX+ecx]
        mov     BX, word ptr [EDX+ecx]
        MOV     word ptr [EDX+ecx], SI
        MOV     word ptr [EAX+ecx], BX
        jz      @@end
        sub     ecx, 2
@@beg1: inc     ecx
        jnz     @@end
        mov     bh, byte ptr [EAX+ecx]
        mov     bl, byte ptr [EDX+ecx]
        MOV     byte ptr [EDX+ecx], bh
        MOV     byte ptr [EAX+ecx], bl
@@end:  pop     EBX
        POP     ESI
        ret
end;
Она выполняется немного быстрее

2Sibedir
Практика показывает, что EBX тоже нужно сохранять.

Последний раз редактировалось Dmitro25; 09.09.2015 в 13:51.
Dmitro25 вне форума Ответить с цитированием
Старый 09.09.2015, 14:28   #7
Sibedir
Тот ещё
Старожил
 
Аватар для Sibedir
 
Регистрация: 14.11.2007
Сообщений: 2,242
По умолчанию

Dmitro25, да, есть и такое. Многое зависит от объема перемещаемой партии, частоты вызовов, выравнивания данных, модели процессора и пр.
Я уже 1000 вариантов перепробовал. От простых типа:
Код:
procedure Exchange (var Value1, Value2; const Size: Integer);
asm
        CMP     ECX , 4
        JB    @@shot
        PUSH    ESI
@@cycle:
        MOV     ESI ,[EAX]
        MOV     EBX ,[EDX]
        MOV    [EAX], EBX
        MOV    [EDX], ESI
        LEA     EAX ,[EAX+4]
        LEA     EDX ,[EDX+4]
        LEA     ECX ,[ECX-4]
        CMP     ECX , 4
        JNB   @@cycle
        POP     ESI
@@shot:
        JCXZ  @@end
        CMP     CL, 2
        JB    @@byte
        MOV     BX  ,[EAX]
        SHL     EBX , 8*2
        MOV     BX  ,[EDX]
        MOV    [EAX], BX
        SHR     EBX , 8*2
        MOV    [EDX], BX
        LEA     EAX ,[EAX + 2]
        LEA     EDX ,[EDX + 2]
        LEA     ECX ,[ECX - 2]
        JCXZ  @@end
@@byte:
        MOV     BL  ,[EAX]
        MOV     BH  ,[EDX]
        MOV    [EAX], BH
        MOV    [EDX], BL
@@end:
end;
До кода и на 5 экранов, и с таблицами переходов, и с относительными короткими переходами на вычисляемое значение, и с использованием регистров сопроцессора, и с проверками на необходимость обмена, и с выравниванием по данными.
Последний монстр отложен на неопределённый срок в виду того, что основная цель (образовательная) достигнута.
Sibedir вне форума Ответить с цитированием
Старый 09.09.2015, 15:37   #8
Dmitro25
 
Регистрация: 19.11.2010
Сообщений: 7
По умолчанию

Sibedir
Свой вариант тестировал только на Core i3 с выравниванием 4 и размером данных от 57 до 64 байт. Наблюдал устойчивый прирост.
А так да, в современных процессорах наличие хитрого конвейера и кэша сделало задачу оптимизации весьма нетривиальной. Вот пример: недавно в библиотеке mORMot наткнулся на процедуру вычисления CRC-32, которая на поверку оказалась в 5 (пять!) раз быстрее, чем моя самопальная процедура на ассемблере.

Спасибо, что поделились свои кодом, заодно и меня на небольшое творчество сподвигли. Я тут пишу свою реализацию списка, который будет хранить не указатели на записи, а сами записи (по типу TDIVector из DIContainers, только попроще, но и побыстрее). Понадобилось для процедуры сортировки сделать Exchange области памяти, стал искать, через google нашёл ваше решение.
Dmitro25 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Почта Exchange Nemesis9 Работа с сетью в Delphi 1 18.12.2013 10:54
Dynamic Data Exchange tolikman Microsoft Office Excel 2 12.08.2011 20:34
Странное поведение BASM Sibedir Общие вопросы Delphi 8 31.01.2011 12:02
Оптимизация sin() на BASM InternetStranger Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM 5 11.02.2010 00:56