|
Ассемблерная оптимизация маленького куска (порядка 10-15 инструкций) |
|
|
|
Feb 26 2016, 14:46
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Здравствуйте, товарищи.
Имеется старенький arm926, на нём потребовалось запилить определённый алгоритм.
Основная тяжеловесная часть данного алгоритма делают следующее:
На входе - 32-битное слово (лежит в регистре R0), в памяти (время доступа - 1 такт) лежат 4 таблицы по 256 однобайтовых элементов каждая.
-Выковыриваем из входного слова отдельно все 4 байта -Из таблицы 1 дёргаем байт, позиция которого определяется первым выдернутым байтом входного слова -Из таблицы 2 дёргаем байт, позиция которого определяется вторым выдернутым байтом входного слова -Из таблицы 3 дёргаем байт, позиция которого определяется третьим выдернутым байтом входного слова -Из таблицы 4 дёргаем байт, позиция которого определяется четвёртым выдернутым байтом входного слова -Из четырёх надёрганных байтов формируем выходное 32-битное слово
Реализация в лоб занимает у меня 11 команд (4 логических И с маской, затем 4 загрузки из таблиц в регистры, затем 3 сложения, чтобы получить выходное слово. Сдвиги не учитываю, так как они прилеплены к другим командам и даются "бесплатно")
Есть ли предложения по сокращению количества команд, если алгоритмическая оптимизация недоступна (то есть только грамотным подбором ассемблерных инструкций) ?
Очень надеюсь, что есть опытные сограждане.
|
|
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 34)
|
Feb 26 2016, 15:22
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Himmler @ Feb 26 2016, 20:46)  На входе - 32-битное слово (лежит в регистре R0), в памяти (время доступа - 1 такт) лежат 4 таблицы по 256 однобайтовых элементов каждая.
-Выковыриваем из входного слова отдельно все 4 байта -Из таблицы 1 дёргаем байт, позиция которого определяется первым выдернутым байтом входного слова -Из таблицы 2 дёргаем байт, позиция которого определяется вторым выдернутым байтом входного слова -Из таблицы 3 дёргаем байт, позиция которого определяется третьим выдернутым байтом входного слова -Из таблицы 4 дёргаем байт, позиция которого определяется четвёртым выдернутым байтом входного слова -Из четырёх надёрганных байтов формируем выходное 32-битное слово Просто поразительно как Ваша задача напоминает решение задачи изложенной только что, практически одновременно с Вами в этом посте: http://electronix.ru/forum/index.php?showt...p;#entry1407211Правда там нужны таблицы не байтовые, а состоящий из 32-битных слов, и ядро другое - M0. Но всё-же, всё-же..... очень подозрительно.....  Касательно сути проблемы: Вы не озвучили требования по памяти. Тупо в лоб оптимизация за счёт объёма памяти: разбивать входное 32-бит слово не на байты, а на куски большей разрядности - например 11/11/10 бит; ну или 16/16бит. Памяти израсходуете больше, но тактов - меньше. PS: Только что в голову пришёл самый быстрый метод - в зависимости от функционирования GPIO в Вашем МК, этот метод вообще может занимать всего пару тактов Выделяете 2-а 32-битных порта GPIO. Соединяете вых. линии первого порта с вх. линиями второго в соответствии с таблицей перестановки бит, выводите слово в первый порт, считываете со второго. На ядре M3 этот алгоритм может занимать всего два такта, если GPIO сидит на быстрой шине.
|
|
|
|
|
Feb 26 2016, 16:28
|
self made
   
Группа: Свой
Сообщений: 855
Регистрация: 7-03-09
Из: Toronto, Canada
Пользователь №: 45 795

|
Цитата(jcxz @ Feb 26 2016, 10:22)  PS: Только что в голову пришёл самый быстрый метод - в зависимости от функционирования GPIO в Вашем МК, этот метод вообще может занимать всего пару тактов Выделяете 2-а 32-битных порта GPIO. Соединяете вых. линии первого порта с вх. линиями второго в соответствии с таблицей перестановки бит, выводите слово в первый порт, считываете со второго. На ядре M3 этот алгоритм может занимать всего два такта, если GPIO сидит на быстрой шине. Тут не перестановка битов, а выборка из массива. Смотря что у вас там за доступы, может помочь кэширование результатов.
|
|
|
|
|
Feb 26 2016, 16:58
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Дело в том, что расширить таблицы мне уже не удастся, так как если сделать их не 1-байтовыми, а 2-байтовыми, то они будут занимать уже не 1 килобайт (4*16*16*1), а 256 килобайт (2*256*256*2). А быстрой однотактовой памяти у меня всего 32 килобайта. Есть огромная ддр-ка, но она дико медленная, обращение к ней съест весь выигрыш. И прекешировать нужное не получится, потому как индексы прыгают хаотически согласно входному слову.
Насчёт GPIO идеи не понял - как можно соединить входы с выходами так, чтобы такое соединение охватывало 2^32 состояний ?
Я пока копаю в сторону именно удачно подходящих инструкций, например чтобы не выделять отдельно 4 байта четыремя командами, или например избавиться от тройного сложения и как-то иначе собрать выходное 32-битное слово из однобайтовых кусков.
|
|
|
|
|
Feb 26 2016, 18:56
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Himmler @ Feb 26 2016, 22:58)  Дело в том, что расширить таблицы мне уже не удастся, так как если сделать их не 1-байтовыми, а 2-байтовыми, то они будут занимать уже не 1 килобайт (4*16*16*1), а 256 килобайт (2*256*256*2). А быстрой однотактовой памяти у меня всего 32 килобайта. Есть огромная ддр-ка, но она дико медленная, обращение к ней съест весь выигрыш. И прекешировать нужное не получится, потому как индексы прыгают хаотически согласно входному слову. Но 11/11/10 потребует только (2^11*2+2^10)*2 = 10240 байт. Хотя (не помню точно систему команд) возможно потребуется доп. сдвиг для преобразования индекса в смещение. Цитата(Himmler @ Feb 26 2016, 22:58)  Насчёт GPIO идеи не понял - как можно соединить входы с выходами так, чтобы такое соединение охватывало 2^32 состояний ? Забудьте. Я думал у Вас такая-же задача, как у товарища переставляющего биты.
|
|
|
|
|
Feb 26 2016, 19:32
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
В принципе да, я могу уместиться в память, если переделаю 4 таблицы по 8 бит в 3 таблицы по 12/12/8 бит.
Тогда алгоритм получится следующий:
-Выковыриваем из входного слова отдельно 12 байт, потом ещё 12, и последние 8. -Из таблицы 1 дёргаем 12 бит, позиция которых определяется первым выдернутым значением из входного слова -Из таблицы 2 дёргаем 12 бит, позиция которых определяется вторым выдернутым значением из входного слова -Из таблицы 3 дёргаем 8 бит, позиция которого определяется третьим выдернутым значением из входного слова -Из трёх надёрганных кусков формируем выходное 32-битное слово
Итого: 3 логических И с маской, 3 загрузки из памяти в регистры, 2 суммирования. Экономия, несомненно есть.
Хотелось бы услышать ещё предложения, возможно есть инструкции, позволяющие сложить три значения за такт, или опять же какая-либо хитрость с масками.
P.S. Вот я тут подумал, при разбиении входного слова на куски можно ещё одну инструкцию сэкономить - не брать маску для старших бит, а передать входное слово с нужным сдвигом, получив тем самым нужное количество старших бит в качестве индекса внутри массива в третьей таблице.
|
|
|
|
|
Feb 26 2016, 20:09
|
Местный
  
Группа: Участник
Сообщений: 356
Регистрация: 24-02-09
Пользователь №: 45 309

|
Можно в 9 команд уложиться. В R0 исходное слово 32 бит В R1 адрес первой таблицы(table_0), в процессе выполнения не меняется, Таблицы идут последовательно: table_0, table_1, table_2, table_3 На выходе в R6 - значения таблиц по условию: [t3],[t2],[t1],[t0].
Эскизы прикрепленных изображений
|
|
|
|
|
Feb 26 2016, 20:29
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Действительно, я наверное некорректно в постановке вопроса употребил слово "команда". В моём случае все инструкции, кроме загрузки из памяти выполняются за 1 такт, а латентность, вызываемая загрузкой из памяти маскируется во-первых конвейерной загрузкой, а во-вторых использованием загруженных данных через 2 инструкции после команды загрузки. А борьба идёт, естественно, за количество тактов. И предложенное выше укрупнение таблиц + экономия одной операции с маской позволяет выполнить алгоритм за 7 тактов. На мой взгляд, гипотетически сэкономить можно на сложении трёх значений и на операциях с маской. Но как именно - пока не знаю. Цитата(Ga_ry @ Feb 27 2016, 00:24)  Если организовать результирующее слово как структуру из указателей? А именно ?
|
|
|
|
|
Feb 26 2016, 21:15
|
Местный
  
Группа: Участник
Сообщений: 356
Регистрация: 24-02-09
Пользователь №: 45 309

|
Цитата(Himmler @ Feb 26 2016, 23:29)  А борьба идёт, естественно, за количество тактов. Понятно. А как оказались четыре 8-битных числа собранными в один регистр R0? Зачем их туда сложили? Если эти 4 байта лежали рядом в какой-то другой таблице, то их можно было считать побайтно в 4 отдельных регистра и не пользоваться для их разделения масками и сдвигами вообще. Можно ещё сэкономить время на сдвигах (безотносительно к вышенаписанному), если сделать 4 таблицы (каждая по 256*32битных слова) с заранее смещёнными в них 8-битными данными. В таблице 0 все данные вида 0х000000 NN, в таблице 1 данные вида 0х0000 NN00, и так далее. Тогда таблицы займут 4кБ всего. А данные берутся из памяти за 1 такт, и ещё за 1 складываются в общий регистр без сдвига (который тоже вроде бы ест такты). Вынуть все 4 байта из 4 таблиц, и сложить в один регистр займёт 7 тактов:
Сообщение отредактировал controller_m30 - Feb 26 2016, 21:38
Эскизы прикрепленных изображений
|
|
|
|
|
Feb 26 2016, 21:51
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Цитата(controller_m30 @ Feb 27 2016, 01:15)  Понятно. А как оказались четыре 8-битных числа собранными в один регистр R0? Зачем их туда сложили? Если эти 4 байта лежали рядом в какой-то другой таблице, то их можно было считать побайтно в 4 отдельных регистра и не пользоваться для их разделения масками и сдвигами вообще.
Можно ещё сэкономить время на сдвигах (безотносительно к вышенаписанному), если сделать 4 таблицы (каждая по 256*32битных слова) с заранее смещёнными в них 8-битными данными. В таблице 0 все данные вида 0х000000NN, в таблице 1 данные вида 0х0000NN00, и так далее. Тогда таблицы займут 4кБ всего. А данные берутся из памяти за 1 такт, и ещё за 1 складываются в общий регистр без сдвига (который тоже вроде бы ест такты). Вынуть все 4 байта из 4 таблиц, и сложить в один регистр займёт 7 тактов: Это не 4 8-битных числа, разбиение условное, можно побить как 12/12/8. Входной вектор - 32-битный, выходной - тоже 32-битный. Идеальный вариант однотактового преобразователя - таблица на 4 миллиарда состояний, по 32 бита в каждом. Но ввиду очевидной невозможности такого безумия приходится разбивать преобразование на куски. Можно преобразовывать по 4 бита 8 раз, а можно по 8 бит 4 раза, а можно по 16 бит 2 раза (но на это памяти уже не хватит). По доступной памяти уложусь максимум в 12/12/8.
|
|
|
|
|
Feb 26 2016, 23:36
|
Частый гость
 
Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894

|
Не понимаю как у вас получается 9 команд, и 18 в уме. У меня получилось 20 команд, и 35 системных тиков. Код // Cortex-M3 // r0 вход-выход push {r1, r2, r3, r4, r5, lr} ldr r0, =входное ldr r1, =адрес таблиц (1+2+3+4) mov r2, #0xFF // маска and r3, r0, r2 ldrb r4, [r1, r3] // первый байт and r3, r2, r0, lsr #8 add r0, r0, #0x100 ldrb r5, [r0, r3] // второй байт add r0, r0, #0x100 and r3, r2, r0, lsr #16 add r4, r4, r5, lsl #8 ldrb r5, [r0, r3] // третий байт add r0, r0, #0x100 lsr r0, r0, #24 add r4, r4, r5, lsl #16 ldrb r5, [r0, r3] // последний байт add r0, r4, r5, lsl #24 pop {r1, r2, r3, r4, r5, lr} Можно ещё читать байт прямо в разряды регистра, но без выравнивания таблиц - может получиться кака.
|
|
|
|
|
Feb 27 2016, 07:41
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Цитата(AVI-crak @ Feb 27 2016, 03:36)  . Дело в том, что такие операции, как загрузка адресов таблиц, сохранение регистров в стек, загрузка входного и выгрузка выходного слов выполняются по одному разу, а вот основная часть крутится многократно. Примерно получается (если считать, что входное слово, 12-битная маска, адреса таблиц уже загружены,регистры сохранены, и таблиц всего 3 по 12/12/8 бит): Код and r1, r0, r7; получаем указатель в первой таблице (r7 = 0x00000fff) and r2, r0, r7, LSL #12; получаем указатель во второй таблице
ldr r1, [r4, r1]; загружаем младшие 12 бит (r4 был предварительно загружен и будет ещё многократно использоваться) ldr r2, [r5, r2, LSR #12]; загружаем средние 12 бит (r5 был предварительно загружен и будет ещё многократно использоваться) ldr r3, [r6, r0, LSR #24]; загружаем старшие 8 бит (r6 был предварительно загружен и будет ещё многократно использоваться)
add r1, r1, r2, LSL #12; собираем 3 куска в одно 32-битное слово add r1, r3, LSL #24; собрали
Сообщение отредактировал IgorKossak - Feb 27 2016, 15:39
Причина редактирования: бездумное цитирование
|
|
|
|
|
Feb 27 2016, 07:57
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Himmler @ Feb 27 2016, 13:41)  Примерно получается (если считать, что входное слово, 12-битная маска, адреса таблиц уже загружены,регистры сохранены, и таблиц всего 3 по 12/12/8 бит): Не понимаю - почему Вы выбрали 12/12/8, а не 11/11/10? Ведь первый вариант требует 16896 байт таблиц, в то время как 2-ой - только 10240.  Цитата(Himmler @ Feb 27 2016, 13:41)  ldr r1, [r4, r1]; загружаем младшие 12 бит (r4 был предварительно загружен и будет ещё многократно использоваться) ldr r2, [r5, r2, LSR #12]; загружаем средние 12 бит (r5 был предварительно загружен и будет ещё многократно использоваться) ldr r3, [r6, r0, LSR #24]; загружаем старшие 8 бит (r6 был предварительно загружен и будет ещё многократно использоваться) Забываете, что читаете не байты, а слова. Надо сдвинуть все индексы влево на 1 бит. Так что кол-во команд будет на 1 больше. И команды не LDR, а LDRH.
|
|
|
|
|
Feb 27 2016, 08:20
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
В моём случае не принципиально 10 килобайт или 16, а дискреты в 4 бита мне удобнее для пересчёта таблиц из их первоначального вида (где было 8 таблиц по 16 4-битных значений в каждой) Насчёт не байты, а слова - да, лопухнулся, написал как было раньше, когда было 4 однобайтовых таблицы. Сдвигать нужно на 1 бит индексы у двух таблиц, третья осталась однобайтовой. Только вот не понял, откуда ещё 1 команда, вроде же достаточно поменять на код вида Код ldr r1, [r4, r1, LSL#1]; загружаем младшие 12 бит (r4 был предварительно загружен и будет ещё многократно использоваться) ldr r2, [r5, r2, LSR #11]; загружаем средние 12 бит (r5 был предварительно загружен и будет ещё многократно использоваться) ldr r3, [r6, r0, LSR #24]; загружаем старшие 8 бит (r6 был предварительно загружен и будет ещё многократно использоваться)
Сообщение отредактировал Himmler - Feb 27 2016, 14:31
|
|
|
|
|
Feb 27 2016, 08:25
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Вы упорно пишете LDR, хотя Вам надо грузить 16-битные слова Доп. команда добавляется если использовать 11/11/10 рассечение (нужно маскировать и сдвинуть старшие 10бит, а это не получится сделать сразу в LDRH). Ок, тогда оставим 12/12/8. Тогда получим: Код ;исх. данные: ;R0 - аргумент; ;R7 = 0x1FFE AND R1, R7, R0, LSL #1;получаем индекс*2 в первой таблице AND R2, R7, R0, LSR #12-1;получаем индекс*2 во второй таблице LDRH R1, [R4, R1];загружаем младшие 12 бит (r4 был предварительно загружен и будет ещё многократно использоваться) LDRH R2, [R5, R2];загружаем средние 12 бит (r5 был предварительно загружен и будет ещё многократно использоваться) LDRB R3, [R6, R0, LSR #24];загружаем старшие 8 бит (r6 был предварительно загружен и будет ещё многократно использоваться) ADD R1, R1, R2, LSL #12;собираем 3 куска в одно 32-битное слово ADD R1, R3, LSL #24
|
|
|
|
|
Feb 27 2016, 08:32
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Опять же согласен, вгружать 32-битные слова неправильно.
То есть на данный момент 7 тактов. Больше нигде и ничего не срезать ?
|
|
|
|
|
Feb 28 2016, 00:15
|
Частый гость
 
Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894

|
LDRB R3, [R6, R0, LSR #24] - это наверное из другой вселенной. В скобках адрес, при этом модификации подвергается последний регистор в записи, и двигать (умножать) его можно исключительно в лево, и число для модификации от единицы до трёх, и чтение байта с такой модификацией - теряет смысл, ибо глюканёт на несоответствии объявленных границ выравнивания переменных и зарегистрированной командой. В целом трешь и угар, выдыхайте  .
Сообщение отредактировал AVI-crak - Feb 28 2016, 00:16
|
|
|
|
|
Feb 28 2016, 11:33
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
К сожалению прямо сейчас у меня железяки и полноценного ide под рукой нету, смогу только завтра проверить.
Но ARMSim#.191, onlinedisassembler.com и ARM instruction evaluator прекрасно переварили такой код, взаимно понимая друг друга и отлично ассемблируя/дизассемблируя результаты друг друга. В частности LDRB R3, [R6, R0, LSR #24] (машинный код E7D63C20) делает именно то, что задумывалось.
|
|
|
|
|
Mar 2 2016, 18:16
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Извиняюсь за задержку, сегодня проверил код, погонял на разных входных данных - работает именно так, как нужно Код ;R0 - используется другим куском кода ;R1 - используется другим куском кода ;R2 - здесь храним кусок (12 бит) :R3 - здесь храним второй кусок (12 бит) ;R4 = in_data ;R5 = table_0_pointer = 0x4000 ;R6 = table_1_pointer = 0x6000 ;R7 = table_2_pointer = 0x3F00 ;R8 = mask (0x1FFE) ;R9-R14 - используется другим куском кода
AND R2, R8, R4, LSL #1 AND R3, R8, R4, LSR #11 LDRH R2, [R5, R2] LDRH R3, [R6, R3] LDRB R4, [R7, R4, LSR #24] ADD R2, R2, R3, LSL #12 ADD R2, R2, R4, LSL #24 Поглядел на весь код в целом - было бы очень хорошо сэкономить два регистра. Ну или хотя бы один. Предположительно, это можно как-нибудь сделать, не храня все три адреса в R5, R6 и R7, а храня один их них, а два других -вычисляя на лету. Но тут я столкнулся с тем, что LDRH не хочет делать сдвиги и суммирования, как это делают LDR и LDRB. Можно ли тут как-нибудь извернуться ? Разумеется, не увеличивая число инструкций.
|
|
|
|
|
Mar 3 2016, 16:38
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Сегодня начал мерить производительность кода и познакомился с кучей неприятных сюрпризов архитектуры ARM.
Во-первых блокировка при загрузке байт/слов составляет не один такт, а два. Во-вторых все сдвиги, прилепленные к командам, совсем не бесплатные, и тоже добавляют блокировку на использование сдвигаемого регистра на один такт.
Возможно, есть что-то ещё, и в результате приведённый мной выше код работает правильно, но в два раза дольше, чем я ожидал.
Единственное, что сразу пришло на ум и чуть-чуть ускорило выполнение - перенос одной команды загрузки слова на пораньше. Но особой погоды это не сделало.
Поэтому актуален следующий вопрос:
Как можно изменить код (в том числе с добавлением команд и расходованием доп. регистров), чтобы уменьшить именно количество тактов при выполнении ?
|
|
|
|
|
Mar 4 2016, 09:37
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(Himmler @ Mar 3 2016, 19:38)  Сегодня начал мерить производительность кода и познакомился с кучей неприятных сюрпризов архитектуры ARM.
Во-первых блокировка при загрузке байт/слов составляет не один такт, а два. Во-вторых все сдвиги, прилепленные к командам, совсем не бесплатные, и тоже добавляют блокировку на использование сдвигаемого регистра на один такт.
Возможно, есть что-то ещё, и в результате приведённый мной выше код работает правильно, но в два раза дольше, чем я ожидал.
Единственное, что сразу пришло на ум и чуть-чуть ускорило выполнение - перенос одной команды загрузки слова на пораньше. Но особой погоды это не сделало.
Поэтому актуален следующий вопрос:
Как можно изменить код (в том числе с добавлением команд и расходованием доп. регистров), чтобы уменьшить именно количество тактов при выполнении ? а развернуть цикл, и конвертировать паралельно 2 числа, может както уменьшить простои?
|
|
|
|
|
Mar 4 2016, 14:41
|
Частый гость
 
Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894

|
Цитата(Himmler @ Mar 3 2016, 23:38)  Сегодня начал мерить производительность кода и познакомился с кучей неприятных сюрпризов архитектуры ARM. Это наверное ваша команда чтения из памяти виновата LDRB R3, [R6, R0, LSR #24] У старенького arm926 - ядро ARM7TDMI, и его команды имеют чёткое описание http://www.gaw.ru/html.cgi/txt/doc/micros/...tmi/insruct.htmФормат чтения памяти : [Rn, +/-Rm, LSR #5bit_shift_imm] , но в реальности LSR-LSL - это пятый бит смещения, а нулевой бит просто не участвует в операции - и того 3 бита смещения. То-есть как у всех ARM чипов.
|
|
|
|
|
Mar 4 2016, 14:51
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(AVI-crak @ Mar 4 2016, 17:41)  У старенького arm926 - ядро ARM7TDMI Это что-то новенькое Цитата(AVI-crak @ Mar 4 2016, 17:41)  и его команды имеют чёткое описание Да. См. тут.
|
|
|
|
|
Mar 4 2016, 18:06
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Дело в том, что у меня и так линейный код, без ветвлений, циклов и прочего. Только арифметические операции и загрузка из памяти. Параллелить его не получится, поскольку он идеологически последовательный. Весь параллелизм - это сложить что-нибудь, пока что-то другое загружается из памяти. Просто судя по времени выполнения, в приведённом мной коде в среднем к каждой команде добавляется ещё и штрафной NOP. Откуда берутся такие задержки - пока не знаю.
|
|
|
|
|
Mar 7 2016, 07:45
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Himmler @ Mar 3 2016, 00:16)  Поглядел на весь код в целом - было бы очень хорошо сэкономить два регистра. Ну или хотя бы один. Предположительно, это можно как-нибудь сделать, не храня все три адреса в R5, R6 и R7, а храня один их них, а два других -вычисляя на лету. Но тут я столкнулся с тем, что LDRH не хочет делать сдвиги и суммирования, как это делают LDR и LDRB. Но, судя по системе команд, команда LDRH регистр смещения может как прибавлять к базовому регистру, так и вычитать из базового (если я правильно понимаю описание - не уверен, надо проверить на железе, но не на чем в данный момент). Так что если в коде Код AND R2, R8, R4, LSL #1 AND R3, R8, R4, LSR #11 LDRH R2, [R5, R2] LDRH R3, [R6, R3] LDRB R4, [R7, R4, LSR #24] ADD R2, R2, R3, LSL #12 ADD R2, R2, R4, LSL #24 нулевой индекс таблиц R5 и R6 содержит одинаковое значение, то можно их расположить одна за другой (объединив 0-й индекс) в командах LDRH использовать один базовый регистр, установленный на середину этой объединённой таблицы, и в одной LDRH использовать смещение в '+', а в другой - в '-'. Тогда один регистр сэкономится. Цитата(Himmler @ Mar 5 2016, 00:06)  Дело в том, что у меня и так линейный код, без ветвлений, циклов и прочего. Только арифметические операции и загрузка из памяти. Параллелить его не получится, поскольку он идеологически последовательный. Весь параллелизм - это сложить что-нибудь, пока что-то другое загружается из памяти. Просто судя по времени выполнения, в приведённом мной коде в среднем к каждой команде добавляется ещё и штрафной NOP. Откуда берутся такие задержки - пока не знаю. Я думаю предлагавший имел в виду разбавить данный кусок кода командами из смежного кода, расположив их в местах предполагаемых "NOP"-ов. Вам вначале нужно выяснить где именно появляются штрафные такты (эти самые NOPы), чтобы можно было с ними бороться перестановкой и перераспределением инструкций. Если штрафы накладываются слишком близким расположением точки вычисления адреса/смещения к команде в которой этот адрес используется (это пары AND R2, R8, R4, LSL #1 / LDRH R3, [R6, R3] и AND R3, R8, R4, LSR #11 / LDRH R3, [R6, R3]) и если штраф - всего один такт, то можно команду LDRB R4, [R7, R4, LSR #24]сместить на две строчки выше - это уберёт штрафы. Если штрафы больше 1-го такта, то надо этот кусок разбавлять командами из смежного кода.
|
|
|
|
|
Mar 7 2016, 07:58
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Ну про перенос команды загрузки я ранее уже писал, это маленько прибавило скорости. А вот про хранение одного адреса на 2 таблицы - не удалось, потому как размер таблиц - 0x2000, а такое значение не влезет в код команды. Изначально у меня была идея разместить таблицы так: 0x3F00 (маленькая таблица на 256 байт) 0x4000 0x6000
При этом хранить только 0x4000, а из него получать сдвигом 0x6000 и вычитанием 0x3F00. Вот только LDRH не позволяет сдвинуть, в отличие от LDR.
|
|
|
|
|
Mar 8 2016, 09:37
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Вы не поняли.... Вот описание команды LDRH ядра ARM7TDMI: http://electronix.ru/redirect.php?http://w...arm/lds_str.htmперемотайте до Рис.25 Нас интересует смещение не в виде константы (рис.26), а в виде регистра (рис.25). Обратите внимание на бит U. Так вот: если таблицу, адрес которой находится в R6 записать в обратном порядке и совместить нулевые индексы таблиц R5 и R6 в одном слове, то должно помочь. Т.е. - при размере таблиц == N элементов будет: (N-1)-ый индекс таблицы R6 (N-2)-ый индекс таблицы R6 ... (1)-ый индекс таблицы R6 (0)-ой индекс таблиц R5 и R6 (единый) (1)-ый индекс таблицы R5 ... (N-2)-ый индекс таблицы R5 (N-1)-ый индекс таблицы R5
устанавливаете регистр R5 на адрес (0)-ой индекс таблиц R5 и R6 (единый) и адресуете обе части таблицы командами: LDRH R2, [R5, R2] LDRH R3, [R5, -R3]Освобождается регистр R6.
|
|
|
|
|
Mar 8 2016, 09:51
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-06-11
Пользователь №: 65 868

|
Точно, неплохая идея. Даже один сэкономленный регистр будет уже в плюс.
Сообщение отредактировал Himmler - Mar 8 2016, 10:01
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|