Project

General

Profile

Программирование на ассемблере. Пара вопросов о производительности

Added by qewerty over 10 years ago

Вот допустим есть функция перемножения матриц 4 на 4 (указатели на стек и фрейм не меняются, т.к. локальные переменные на стеке не нужны). Матрицы хранятся по столбцам (как в OpenGL).

.global qf_mat44_mul
qf_mat44_mul:
    .alias qf_mat44_mul.a #SP,4
    .alias qf_mat44_mul.b #SP,8
    .alias qf_mat44_mul.c #SP,12

    jmp qf_mat44_mul.P0
    ; сохранение адресов аргументов в регистрах
    rdl qf_mat44_mul.a
    setl #32, @1
    rdl qf_mat44_mul.b
    setl #33, @1
    rdl qf_mat44_mul.c
    setl #34, @1
    complete

.local qf_mat44_mul.P0
qf_mat44_mul.P0:
    rdl #SP
    jmp @1
    ; чтение матрицы b
    va01 := rdq #32
    va23 := rdq #32,8
    va45 := rdq #32,16
    va67 := rdq #32,24
    va89 := rdq #32,32
    vaAB := rdq #32,40
    vaCD := rdq #32,48
    vaEF := rdq #32,56
    ; вспомогательные значения, для упаковки матрицы a в строки
    va54 := pack @va45, @va45
    vaDC := pack @vaCD, @vaCD
    va76 := pack @va67, @va67
    vaFE := pack @vaEF, @vaEF
    ; упаковка строк матрицы a
    va04 := patch @va45, @va01
    va8C := patch @vaCD, @va89
    va15 := pack @va54, @va01
    va9D := pack @vaDC, @va89
    va26 := patch @va67, @va23
    vaAE := patch @vaEF, @vaAB
    va37 := pack @va76, @va23
    vaBF := pack @vaFE, @vaAB
    ; чтение матрицы b
    vb01 := rdq #33
    vb23 := rdq #33,8
    vb45 := rdq #33,16
    vb67 := rdq #33,24
    vb89 := rdq #33,32
    vbAB := rdq #33,40
    vbCD := rdq #33,48
    vbEF := rdq #33,56
    ; скалярные произведения строк матрицы a со стобцами матрицы b
    madd @va04, @vb01
    madd @va8C, @vb23
    vc_0 := addf @1, @2
    madd @va15, @vb01
    madd @va9D, @vb23
    vc_1 := addf @1, @2
    madd @va26, @vb01
    madd @vaAE, @vb23
    vc_2 := addf @1, @2
    madd @va37, @vb01
    madd @vaBF, @vb23
    vc_3 := addf @1, @2
    ; --
    madd @va04, @vb45
    madd @va8C, @vb67
    vc_4 := addf @1, @2
    madd @va15, @vb45
    madd @va9D, @vb67
    vc_5 := addf @1, @2
    madd @va26, @vb45
    madd @vaAE, @vb67
    vc_6 := addf @1, @2
    madd @va37, @vb45
    madd @vaBF, @vb67
    vc_7 := addf @1, @2
    ; --
    madd @va04, @vb89
    madd @va8C, @vbAB
    vc_8 := addf @1, @2
    madd @va15, @vb89
    madd @va9D, @vbAB
    vc_9 := addf @1, @2
    madd @va26, @vb89
    madd @vaAE, @vbAB
    vc_A := addf @1, @2
    madd @va37, @vb89
    madd @vaBF, @vbAB
    vc_B := addf @1, @2
    ; --
    madd @va04, @vbCD
    madd @va8C, @vbEF
    vc_C := addf @1, @2
    madd @va15, @vbCD
    madd @va9D, @vbEF
    vc_D := addf @1, @2
    madd @va26, @vbCD
    madd @vaAE, @vbEF
    vc_E := addf @1, @2
    madd @va37, @vbCD
    madd @vaBF, @vbEF
    vc_F := addf @1, @2
    ; упаковка результатов
    vc01 := patch @vc_1, @vc_0
    vc23 := patch @vc_3, @vc_2
    vc45 := patch @vc_5, @vc_4
    vc67 := patch @vc_7, @vc_6
    vc89 := patch @vc_9, @vc_8
    vcAB := patch @vc_B, @vc_A
    vcCD := patch @vc_D, @vc_C
    vcEF := patch @vc_F, @vc_E
    ; запись результатов
    wrq @vc01, #34
    wrq @vc23, #34,8
    wrq @vc45, #34,16
    wrq @vc67, #34,24
    wrq @vc89, #34,32
    wrq @vcAB, #34,40
    wrq @vcCD, #34,48
    wrq @vcEF, #34,56
    complete

Есть кой-какие вопросы о том, как сделать её максимально быстрой:

1.) Имеет ли смысл сохранять адреса аргументов в индексных регистрах для дальнейшего использования?

Т.е. сделав:

    rdl qf_mat44_mul.a
    setl #32, @1

Далее можно загрузить матрицу так:

    va01 := rdq #32
    va23 := rdq #32,8
    va45 := rdq #32,16
    va67 := rdq #32,24
    va89 := rdq #32,32
    vaAB := rdq #32,40
    vaCD := rdq #32,48
    vaEF := rdq #32,56

Вместо того, чтобы читать адрес в коммутатор и вычислять адреса вот так:

    pa01 := rdl qf_mat44_mul.a
    pa23 := addl @pa01, 8
    pa45 := addl @pa01, 16
    pa67 := addl @pa01, 24
    pa89 := addl @pa01, 32
    paAB := addl @pa01, 40
    paCD := addl @pa01, 48
    paEF := addl @pa01, 56
    va01 := rdq @pa01
    va23 := rdq @pa23
    va45 := rdq @pa45
    va67 := rdq @pa67
    va89 := rdq @pa89
    vaAB := rdq @paAB
    vaCD := rdq @paCD
    vaEF := rdq @paEF

Но последнее позволяет реализовать функцию одним параграфом. Т.е. меня интересует есть ли смысл избегать переключения параграфов, а также скорость выполнения чтения из адреса в регистре со смещением и из вычисленного адреса в коммутаторе. Возможно есть какие-то подводные камни.

2.) Имеет ли смысл минимизировать кол-во операций записи в память?

Например, тут у меня сделано так:

    ; упаковка результатов
    vc01 := patch @vc_1, @vc_0
    vc23 := patch @vc_3, @vc_2
    vc45 := patch @vc_5, @vc_4
    vc67 := patch @vc_7, @vc_6
    vc89 := patch @vc_9, @vc_8
    vcAB := patch @vc_B, @vc_A
    vcCD := patch @vc_D, @vc_C
    vcEF := patch @vc_F, @vc_E
    ; запись результатов
    wrq @vc01, #34
    wrq @vc23, #34,8
    wrq @vc45, #34,16
    wrq @vc67, #34,24
    wrq @vc89, #34,32
    wrq @vcAB, #34,40
    wrq @vcCD, #34,48
    wrq @vcEF, #34,56

Но можно сделать и так:

    wrl @vc_0, #34
    wrl @vc_1, #34,4
    wrl @vc_2, #34,8
    ...
    wrl @vc_E, #34,56
    wrl @vc_F, #34,60

Что делает 16 операций записи в память, а не 8, но все 16 операций независимы, тогда как в первом случае операции записи зависят от упаковки значений.


Replies (82)

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

интересная программа, спасибо! сделаю пару замечаний, если речь идет о перемножении матриц трансформации (наверное, это предположение я сделал из-за упоминания в тексте OpenGL).
Матрицы трансформации перемещают, меняют объект в трехмерном пространстве и имеют следующий вид:

[ R_xx, R_xy, R_xz, T_x ]
[ R_yx, R_yy, R_yz, T_y ]
[ R_zx, R_zy, R_zz, T_z ]
[   0,   0,    0,    1  ]

Матрица трансформации действует(умножается) на вектор, описывающий точку тела и имеющий координаты
[ X ]
[ Y ]
[ Z ]
[ 1 ]

Вектор трехмерный, хотя и описывается четырьмя координатами, четвертая координата у вектора всегда равна 1. Для каждой матрицы трансформации четвертая строка всегда имеет вид
[ 0, 0, 0, 1] 

Числа Tx,Ty,Tz отвечают за перемещение вектора в пространстве, а матрица 3х3, состоящая из букв R_ij - это матрица поворота, растяжения/сжатия.

В этом случае, мне кажется, в программе нужно использовать свойства матрицы: Последняя строка имеет всегда один и тот же вид, и соответственно, ее не нужно умножать - ответ известен.
четвертый член в скалярном произведении строки матрицы a на столбец b тоже известен и равен нулю для первых 3х строк матрицы a и единицы для последней.
Т.е. вообще говоря для задания матриц трансформации, достаточно задания 3х4 значений.

Кроме того у матрицы трансформации есть антисимметрия относительно диагонали. Я не знаю как его использовать в 3х мерном случае, однако, в моей задаче, которая сводится к поворотам на двумерной плоскости, я сначала решил применить матрицу трансформации

[  cos(a) sin(a) 0 ]
[ -sin(a) cos(a) 0 ]
[   0       0    1 ]

но затем заменил ее на умножение на комплексное число.
В самом деле:
[  cos(a) -sin(a) 0 ]   [ X ]   [ X cos(a) - Y sin(a) ]
[  sin(a)  cos(a) 0 ] x [ Y ] = [ Y cos(a) + X sin(a) ]
[   0        0    1 ]   [ 1 ]   [          1          ]

что соответствует умножению комплексных чисел
(A, B) x (C, D) = (AC - BD, AD + BD)

где (A,B) есть (X,Y), a (C,D) eсть (cos(a), sin(a)) и это умножение выполняется встроенной командой процессора mulc.

PS: Наконец сам себе сформулировал (для своей двумерной задачи) что двигаюсь правильно.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

Да, речь идёт именно о матрицах трансформации. Понятно, что нижняя строка у таких матриц всегда (0, 0, 0, 1), однако в данном случае ничего от этого факта выиграть не получится, т.к. кол-во операций для скалярного произведения 3-х и 4-х компонентных векторов одинаково.

    madd @va04, @vb01
    madd @va8C, @vb23
    vc_0 := addf @1, @2
    ; против
    madd @va04, @vb01
    addf @va_8, @vb_2
    vc_0 := addf @1, @2

А раз так, то пусть уж честно считает. =) Собственно, мои вопросы касаются архитектурно-специфических оптимизаций, а не алгоритмических.

Вектор трехмерный, хотя и описывается четырьмя координатами, четвертая координата у вектора всегда равна 1.

У вектора (как направления) четвёртый компонент всегда 0, это у точки 1, что, при умножении на матрицу, позволяет ей принимать перемещение, а вектору это не нужно.

Повороты в 3-х мерном пространстве гораздо сложнее и могут быть заданы разными методами (углы Эйлера для всех случаев XYZ, XZY, YXZ и т.д, кватернионы).

А это:

    x = X cos(a) - Y sin(a)
    y = Y cos(a) + X sin(a)

классический способ поворота двумерной точки (http://ru.wikipedia.org/wiki/Поворот). Если у Вас нет сложной иерархии объектов, то его лучше и использовать. Матрицы полезны, чтобы собирать сложные иерархические трансформации в одну сущность.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

Однако, произведение последней строки a [0,0,0,1] на столбцы b можно не считать.
это не нужно:

    madd @va37, @vb01
    madd @vaBF, @vb23

    madd @va37, @vb45
    madd @vaBF, @vb67

    madd @va37, @vb89
    madd @vaBF, @vbAB

    madd @va37, @vbCD
    madd @vaBF, @vbEF

и даже не инициализировать

va37 := pack @va76, @va23
vaBF := pack @vaFE, @vaAB

хотя, я понимаю, что основной вопрос в оптимизации. но, смотрите, сейчас в программе около 90 строк, а нужно для параграфа 64. Десять строк я уже убрал.

Еще более радикально, представьте что матрица трансформации инициализирована в порядке

[ 0  3  6  9 ]
[ 1  4  7  A ]
[ 2  5  8  B ]

это сильно сократит программу.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 10 years ago

qewerty, в вашем коде для достижения большего быстродействия можно поставить все команды madd подряд, а затем после них сделать сложение.
Запись программы в один параграф, на мой взгляд, в данном случае не должна принести увеличения скорости, но это лучше проверить с помощью таймера.
С записью в память по 64 или 32 бита тоже можно поэкспериментировать и посмотреть результат по таймеру.
У вас отладочная плата имеется своя или используете удалённый доступ?

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

krufter_multiclet,

в вашем коде для достижения большего быстродействия можно поставить все команды madd подряд, а затем после них сделать сложение

Да, у меня были такие мысли, но с учётом того, что команды исполняются по готовности потребителя, я сомневался. Спасибо!

У вас отладочная плата имеется своя или используете удалённый доступ?

Я пока только на модели запускал. Плату заказал недавно, сегодня из курьерской службы звонили, сказали завтра привезут. На днях смогу потестировать.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

подозрительно быстро на удаленном доступе выполнилось. таймер я не использовал. сделал 65542 умножений матриц 4х4, ответа долго ждать не пришлось, меньше секунды. если есть демонстрация использования таймера, сделаю замер(замеры) с таймером.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

trott, ну если допустить, что каждая команда выполняется за один такт, то эта функция на 4-х клеточном процессоре в идеале должна занять около 25 тактов, что при 80 МГц должно дать 3.2 мильёна умножений в секунду. =)

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

qewerty, это вопрос к производителю, сколько тактов у него занимает каждая операция. кстати, очень хотелось бы узнать, cколько?
я вчера оценивал результат. у меня получилось, в предположении что умножение/сложение занимают 10 тактов. 100х100х10^3х10=10^7 или 10 миллионов. поделим на 4 клетки = 2.5 мильёна

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 10 years ago

Примеры работы с таймером имеются конечно. Могу прикрепить сюда пример.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

да, пожалуйста, если можно пример ... и есть ли данные, сколько тактов занимает madd, pack, rdl, и прочие.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 10 years ago

С помощью таймера периферии:

.include "HDL50001_pcf.inc" 
.alias ron1 1
.alias ron3 3

.text

initTimer0:
    jmp StartTimer0
    getl 0xFFFFFFFF
    wrl @1, TIM0_CNT ;период
    getl 0x00000001
    wrl @1, TIM0_PSC ;значение предделителя
complete

StartTimer0:
    jmp Get_start_time
    getl 0x00000005
    wrl @1, TIM0_CR
complete

Get_start_time:
    rdl TIM0_CNTVAL
    setl #ron1, @1
    jmp code
complete

code:
    ;code here
    jmp Get_finish_time
complete

Get_finish_time:
    rdl TIM0_CNTVAL
    getl #ron1
    subl @1, @2
    setl #ron3, @1
    jmp initUART0
complete

В текущей версии у таймера периферии необходимо период умножить на два, чтобы получить количество тактов(у системного таймера делать это не нужно).

В потоке на каждом такте выдаётся результат выполнения команды, например madd, rdl, pack... Точное время выполнения команд по отдельности может быть вычислено только с помощью пользовательской потактовой модели(или мы можем проанализировать параграф на своей модели и сказать сколько и что выполняется). Можем только составить чистое время выполнения команд в ALU без учёта выходного диспетчера.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

На LDM-MCp0411100101-Q208 Evolution получилось выжать около мильёна умножений в секунду на 80 МГц. Функция вот такая: http://dumpz.org/605010/. Выполняется около 80 тактов, точнее сказать сложно из-за расходов на организацию цикла, кстати, тоже на асме, т.к. на Си слишком большие накладные расходы были, практически равные умножению.

А вот можно посмотреть аналогичный тест на Raspberry Pi: http://thinkingeek.com/2013/05/12/arm-assembler-raspberry-pi-chapter-14/. Лучший результат (асм+неон) смог умножить 2 в 21 степени матриц (2097152) за 1.51 сек. И это на 700 МГц (до 1 ГГц в турбо режиме, но будем брать 700). Для 80 МГц получается 158725 умножений в сек, что в 6 раз меньше чем у Мультиклета на МГц.

Хочу ещё попробовать сделать более жизненные тесты и сравнить с Raspberry Pi, которая у меня тоже имеется.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 10 years ago

Спасибо за приятную информацию и проведённые тесты.
А не покажете как цикл организовали? Или весь исходник? Т.е. вызов функции был из Си?

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

krufter_multiclet, цикл, как выше писал, на асме тоже. Как-то так (воспроизвожу по памяти, т.к. сейчас не могу посмотреть исходники):

; Си-прототип (возвращает замеренное таймером значение):
;     int qf_test_mat44_mul(const float* a, const float* b, float* c);

.global qf_test_mat44_mul
qf_test_mat44_mul
    jmp start_timer
    getl 0xFFFFFFFF
    wrl @1, TIM0_CNT
    getl 0x00000001
    wrl @1, TIM0_PSC
complete

.local start_timer
start_timer
    jmp get_start_time
    getl 0x00000005
    wrl @1, TIM0_CR
complete

.local get_start_time
get_start_time
    rdl TIM0_CNTVAL
    setl #1, @1
    jmp qf_loop_mat44_mul
complete

.local check_timer_and_quit
check_timer_and_quit:
    rdl TIM0_CNTVAL
    getl #1
    subl @1, @2
    rdl #SP
    jmp @1
    wrl @3, #SP,4
complete

.global qf_loop_mat44_mul
qf_test_mat44_mul:
    jmp  qf_loop_mat44_mul.CHECK
    getl 1000000
    setl #0, @1
complete

.local qf_loop_mat44_mul.CHECK
qf_test_mat44_mul:
    c := getl #0
    je   @c, check_timer_and_quit ; как ваше Get_finish_time, вычисляет разницу таймера, но выходит по #SP
    jne  @c, qf_mat44_mul
    subl @c, 1
    setl #0, @1
complete

И в самой функции qf_mat44_mul, в параграфе P0, возврат делается на в qf_test_mat44_mul.CHECK, а не по #SP:

.global qf_mat44_mul
qf_mat44_mul:
    .alias qf_mat44_mul.a #SP,8
    .alias qf_mat44_mul.b #SP,12
    .alias qf_mat44_mul.c #SP,16

    jmp qf_mat44_mul.P0
    ; Сохранение адресов аргументов в регистрах
    rdl qf_mat44_mul.a
    rdl qf_mat44_mul.b
    rdl qf_mat44_mul.c
    setl #32, @3
    setl #33, @3
    setl #34, @3
complete

.local qf_mat44_mul.P0
qf_mat44_mul.P0:
    ; вот так:
    jmp qf_loop_mat44_mul.CHECK ; лучше было бы в регистре сохранить адрес возврата
    ; вместо:
    ; rdl #SP
    ; jmp @1
    ...

Всё это не по-сишному конечно, но в частных случаях вполне возможно, к тому же оптимизирующий компилятор также может не оформлять стековый кадр если функции он не нужен.

А qf_test_mat44_mul вызывалась уже из Си.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

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

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

qewerty, я протестировал свой микроконтроллер stm32f4-discovery(87мГц) на умножение матриц 4х4. результат: 5 секунд для 1 мильона умножений. код на C выложен на https://github.com/trot-t/stm32-matrix . плавающая точка включена.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by Zveruga over 10 years ago

Интригующие тесты. Во внутричиповой памяти Мультиклета памяти больше чем у Спектрума. Если можно будет подключить монитор, то возможно написать демку с 3D сценой, на подобии из тех, что были на спектруме.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by krufter_multiclet over 10 years ago

Да на VGA монитор выводили красный цвет, дальше времени не было.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

Прошлый тест совсем уж наивный был, выкладывать не стал. Кое-что доработал, исходники можно взять тут: https://bitbucket.org/qewerty/qf/overview. Функция умножения теперь сохраняет и восстанавливает значения регистров и судя по всему должна работать корректно. Да и цикл теперь более честный, с пробросом аргументов через стек.

Кому интересно может собрать, проверить и убедиться:

hg clone https://bitbucket.org/qewerty/qf
cd qf
make
mc-ploader qf_mat44_mul_TEST

Будет мигать диодами с интервалом в миллион матричных умножений. =)

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by Sergei.S over 10 years ago

Перемножение матриц - задача с явным параллелизмом, так что это не супер аргумент в пользу мультклета. Потому как гипотетический микроконтроллер с 4 обычными ядрами так же покажет пропорциональный рост производительности на этой задаче. И тут уже надо мерить не число умножений/мегагерц, а число умножений/площадь кристалла или число умножений/ватт. и так далее. Т.е. приводить задачу к объему вентелей, требуемых для ее решения.

Так же очень интересно, а что будет с перемножением матриц хотябы 100 х 100 например, что бы понять как работает подсистема памяти.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by qewerty over 10 years ago

Sergei.S,

Перемножение матриц - задача с явным параллелизмом

Да неужели? В том то и суть. Важно КАК оно распараллеливается. А задачи где параллелизма нет, нельзя распараллелить никак.

гипотетический микроконтроллер с 4 обычными ядрами так же покажет пропорциональный рост производительности на этой задаче.

Да, только на потоки код должен разбивать программист, а это гораздо сложней чем написать рядом несколько независимых инструкций. А если матрицы нужно умножать строго последовательно, в виду их зависимости, то никакого прироста не будет (в графе сцены, например, они частично зависимы). А если одно умножение на несколько потоков раздробить, то может быть даже ещё медленней из-за расходов на синхронизацию.

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by mouse over 10 years ago

Хочу добавить свои пять копеек. Пишу реализацию delay-функции на асме. Пробы пера. Первый вариант имеет оверхед с лишними вызовами двух команд в пределах одного параграфа:

.include "HDL50001_pcf.inc" 

.alias SP 39

.global MCp_delay

.text

MCp_delay:
    rdl #SP, 8
    setl #32, @1
    jmp loop
complete

loop:
    getl #32
    je @1, return
    jne @2, loop
    subl @3, 1
    setl #32, @1
complete

return:
    rdl #SP
    jmp @1
complete
Второй вариант с разбивкой на отдельные параграфы и с оверхедом на доп. вызов getl #32:
.include "HDL50001_pcf.inc" 

.alias SP 39

.global MCp_delay

.text

MCp_delay:
    rdl #SP, 8
    setl #32, @1
    jmp loop
complete

loop:
    getl #32
    je @1, return
    jne @2, decrement
complete

decrement:
    getl #32
    subl @1, 1
    setl #32, @1
    jmp loop
complete

return:
    rdl #SP
    jmp @1
complete
Первый вариант получается байтов на 14 короче. Вариант с авто-инкрементом индексных регистров не подходит. И сходу он у меня не полетел:
.include "HDL50001_pcf.inc" 

.alias SP 39

.global MCp_delay

.text

MCp_delay:
    rdl #SP, 8
    setl #32, @1
    setq #33, 0
    setl #MODR, 2  ; пробовал маску вплоть до 15
    jmp loop
complete

loop:
    jmp loop
    getl #32
    xor @1, #33    ; в модели регистр 0x21 принимал значение 0xf_0000_000f и не менялся от вызова к вызову параграфа
                   ; какой есть альтернативный способ сравнения двух значений?
    je @1, return
complete

return:
    rdl #SP
    jmp @1
complete
Что лучше — оверхед с выполнением лишних инструкций или лишние параграфы? И что предпочтительнее в случае:
    jmp loop
    […]
    je @1, return
вызывать jmp loop или после команды je @1,return вызывать jne @2, loop?

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

mouse, в вашем примере с автоинкрементом установка значения в #MODR разрешает инкрементировать индексный регистр #33, а маска, которая записывается в сам регистр #33, запрещает автоинремент.

MCp_delay:
    rdl #SP, 8
    setl #32, @1
    setq #33, 0  ; <= здесь выставили базу = 0, маску = 0, индекс = 0. если маска во всех разрядах равна 0, то инкремента не будет, 
; из-за того, что он запрещен маской, 0000000000000000 - маска запретила изменять все разряды у индекса.  
; 11111111111111 - такая маска разрешит менять все разряды индекса в регистре   
    setl #MODR, 2  ; пробовал маску вплоть до 15 <== здесь говорим, что будем автоинкрементировать единственный индексный регистр #32, это не маска, а резрешение инкремента
    jmp loop
complete

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by trott over 10 years ago

mouse, такая программа в модели 3 раза делает автоинкремент регистра #32

.include "HDL50001_pcf.inc" 
.text

.alias SP 39

.global MCp_delay

.text

MCp_delay:
;    rdl #SP, 8
    getl 2 ; сделаем 3 шага автоинкремента для проверки в модели
    mask := getl 65535        ; готовим маску 1111111111111111
    ind_aux := subl @mask, @2   
    ind := slll @ind_aux, 16  ; индекс  
    addl @ind, @mask ; заполняем индекс и маску
    patch @1, 0      ; склеиваем маску индекс и базу в 64 разряда
    setq #32, @1     ; записываем значение в индексный регистр #32
    setl #MODR, 1   ; разрешаем модификацию первого индексного регистра, он имеет #32
    jmp loop
complete

loop:
    exa #32
    je @1, return ; проверка на 0
    jne @2, loop
complete

return:
    getl 0x1000 ; остановимся, компилятор не любит пустых параграфов
;    rdl #SP
;    jmp @1
complete

RE: Программирование на ассемблере. Пара вопросов о производительности - Added by mouse over 10 years ago

trott wrote:

mouse, в вашем примере с автоинкрементом установка значения в #MODR разрешает инкрементировать индексный регистр #33, а маска, которая записывается в сам регистр #33, запрещает автоинремент.
[...]
setl #MODR, 2 ; пробовал маску вплоть до 15 <== здесь говорим, что будем автоинкрементировать единственный индексный регистр #32, это не маска, а резрешение инкремента

Если обратиться к документации , то выставленный бит разрешает инкремент. Что значит "маска / не маска"? Здесь я вас не понимаю. Про регистр #32, думаю, опечатка, т.к. 2-й бит отвечает за регистр #33.

Давайте вернёмся к моему начальному сообщению:

mouse, […]Вариант с авто-инкрементом индексных регистров не подходит[…]

Мы имеем 16-ти битный автоинкрементирующийся счётик . Как мне сделать точную паузу более 5 мс при частоте процессора 100MHz? Есть ещё один тонкий момент с автоинкрементом. В момент вызова MCp_delay из Си мне придётся сохранять значение как оригинального MODR, так и индексного регистра, дабы после завершения функции вернуть всё на место (я же не знаю, кем этот регистр может быть ещё использован). Т.е. функция откушает 8 (а то и все 12) байт на ровном месте + ненужные операции над памятью.

(1-25/82)