Rst7 5 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 4 minutes ago, jcxz said: Нужно было сделать осциллографирование (в реальном времени) содержимого некоторых переменных в ПО. Дайте я догадаюсь ;) Это же надо было исключительно на этапе отладки? Неужели настолько там все друг с другом запутано, что не получилось отладить по-модульно, например, а не собирать переменные из кучи мест? 5 minutes ago, jcxz said: Я Вам уже приводил пример - Вы так ничего и не ответили по существу: Например - данные поступают от АЦП. И как заставить АЦП присылать уже отсортированные данные, расскажите? В таком ключе на Ваш вопрос нет ответа. Надо подняться с уровня гайки М3 (поступающих данных от АЦП) и посмотреть на алгоритм работы целиком. Думаю, что все эти примеры показывают только одно - достаточно поверхностное отношение к системному проектированию. И главное, Вы не думайте, что я тут пропагандирую "никакого низкого уровня". Я сам большой любитель, например: Spoiler #pragma inline=forced static long long MULLS(REG32 a, REG32 b) { long long r; asm("SMULL %L0,%H0,%1,%2":"=Rp"(r):"r"(a),"r"(b)); return r; } //#define LL2FRACT_ROUND(VAR) (((VAR)&(1UL<<(FRACT_SHIFT-1))?(VAR)>>FRACT_SHIFT:((VAR)>>FRACT_SHIFT)+1)) //#define LL2FRACT_ROUND2(VAR) (((VAR)&(1UL<<(FRACT_SHIFT))?(VAR)>>(FRACT_SHIFT+1):((VAR)>>(FRACT_SHIFT+1))+1)) #pragma inline=forced static inline FRACT LL2FRACT_ROUND(long long r) { asm("LSRS %L0,%L0,%1":"+Rp"(r):"I"(FRACT_SHIFT):"cc"); //:); asm("ORR %L0,%L0,%H0, LSL %1":"+Rp"(r):"I"(32-FRACT_SHIFT)); asm("IT CS\n" "ADDCS %L0,%L0,#+1":"+Rp"(r)); return r; } #pragma inline=forced static inline FRACT LL2FRACT_ROUND2(long long r) { asm("LSRS %L0,%L0,%1":"+Rp"(r):"I"(FRACT_SHIFT+1):"cc"); //:); asm("ORR %L0,%L0,%H0, LSL %1":"+Rp"(r):"I"(32-(FRACT_SHIFT+1))); asm("IT CS\n" "ADDCS %L0,%L0,#+1":"+Rp"(r)); return r; } #pragma inline=forced static inline FRACT FMULL(long a, long b) { long long r = MULLS(a,b); return LL2FRACT_ROUND(r); } #pragma inline=forced static inline void Vector_Cross_Product(FRACT * vectorOut, FRACT const * v1, FRACT const * v2) { vectorOut[0]= FMULL(v1[1],v2[2]) - FMULL(v1[2],v2[1]); vectorOut[1]= FMULL(v1[2],v2[0]) - FMULL(v1[0],v2[2]); vectorOut[2]= FMULL(v1[0],v2[1]) - FMULL(v1[1],v2[0]); } static inline FRACT Vector_Dot_Product(FRACT const * vector1, FRACT const * vector2) { long long r; asm("SMULL %L0,%H0,%1,%2":"=Rp"(r):"r"(vector1[0]),"r"(vector2[0])); asm("SMLAL %L0,%H0,%1,%2":"+Rp"(r):"r"(vector1[1]),"r"(vector2[1])); asm("SMLAL %L0,%H0,%1,%2":"+Rp"(r):"r"(vector1[2]),"r"(vector2[2])); return LL2FRACT_ROUND(r); } #pragma inline=forced static inline FRACT Vector_Dot_Product_Half(FRACT const * vector1, FRACT const * vector2) { long long r; asm("SMULL %L0,%H0,%1,%2":"=Rp"(r):"r"(vector1[0]),"r"(vector2[0])); asm("SMLAL %L0,%H0,%1,%2":"+Rp"(r):"r"(vector1[1]),"r"(vector2[1])); asm("SMLAL %L0,%H0,%1,%2":"+Rp"(r):"r"(vector1[2]),"r"(vector2[2])); return LL2FRACT_ROUND2(r); } Обратите внимание даже не на SMULL/SMLAL, а на IT CS/ADD Или #define PRAMP() asm("ADD.N %0,%1":"+r"(sample):"r"(pwm_mask)); #define MRAMP() PRAMP() #define PROCESS() \ asm("ANDS.N %0,%1":"+r"(i1):"r"(pwm_mask)); \ asm("ADD.N %0,%1":"+r"(i1):"r"(sample)); \ *p++=i1; \ i=BLOCK_SIZE; do { sample=*input++; PROCESS(); PRAMP(); PROCESS(); PRAMP(); PROCESS(); PRAMP(); PROCESS(); PRAMP(); PROCESS(); PRAMP(); PROCESS(); PRAMP(); PROCESS(); PRAMP(); PROCESS(); MRAMP(); PROCESS(); MRAMP(); PROCESS(); MRAMP(); PROCESS(); MRAMP(); PROCESS(); MRAMP(); PROCESS(); MRAMP(); PROCESS(); MRAMP(); PROCESS(); } while(--i); В общем - любим, умеем, практикуем, но в первую очередь стараемся не плодить кучу такого кода. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 52 minutes ago, Arlleex said: Темы asm vs C, Windows vs Linux, AVR vs PIC, видимо, никогда не прекратятся на этом Форуме (да вообще нигде) 52 minutes ago, Arlleex said: Как будто насильно заставляет кто-то использовать этот несчастный ассемблер, ей-богу Тема немного о другом, если поглядите стартовое сообщение) 1 hour ago, xvr said: PUSH {R4 - R7, LR} POP {R4 - R7, PC} Я уже дал одно из объяснений, зачем нужны эти команды. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 2 минуты назад, Rst7 сказал: Дайте я догадаюсь ;) Это же надо было исключительно на этапе отладки? Неужели настолько там все друг с другом запутано, что не получилось отладить по-модульно, например, а не собирать переменные из кучи мест? Запутанно да - сильно, ибо проект сложный. И этап отладки - это не пара дней как можно подумать, а годы, пока живёт и развивается проект. И "отладка" здесь - это не только отладка ПО, но и железа, которое подключено - для этого оно даже нужнее. И железо разное и меняется и будет меняться. 2 минуты назад, Rst7 сказал: В таком ключе на Ваш вопрос нет ответа. Надо подняться с уровня гайки М3 (поступающих данных от АЦП) и посмотреть на алгоритм работы целиком. Так вот это осциллографирование как раз и позволяет оценить правильность работы всего алгоритма целиком. И искать пути улучшения. Вы же пользуетесь эмулятором для отладки? А зачем? Почему не подняться выше и не "посмотреть на алгоритм работы целиком"? И сразу аналитически найти все проблемы? Вот и здесь - то же. 2 минуты назад, Rst7 сказал: В общем - любим, умеем, практикуем, но в первую очередь стараемся не плодить кучу такого кода. Ну а кто- говорит за "кучу"? У меня ассемблера в во всём ПО - думаю менее 5%. Тут дело в том, что некоторые радикально настроенные товарищи уверяют что знание ассемблера вообще не нужно. Вот с этим и не соглашаюсь. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 18 minutes ago, jcxz said: Я приводил примеры кода на ассемблере, спрашивал как это сделать на си, чтобы после компиляции получалось хотя-бы не хуже Что значит 'не хуже'? Вас интересуют эстетические критерии, или количество строк? Лично меня интересует время исполнения этого кода, по сравнению с С вариантом. Этого нет. Значит и сравнивать нечего. Размер в строках не показатель. 22 minutes ago, jcxz said: Приводил код на си, спрашивал как получить оптимальное время выполнения того кода, не зная ассемблера - тоже слились. Я вам написал - нужно переделывать алгоритм. Могу даже написать как, но боюсь вы не поверите :( JIT который вы сделали тоже вариант, но я не уверен, что он будет лучше С варианта. И я не утверждал, что ассемблер вообще не нужен. Я утверждал, что его применение очень ограниченное, и уж точно не стоит бросаться все подозрительные места переписывать на ассемблере. 27 minutes ago, jcxz said: Если не разбираетесь в теме, зачем тогда вообще писать? Может все же послушаете человека, который разбирается в теме, прежде чем ярлыки навешивать? Или 10 лет работы в Intel (включая процессоры Nehalem и Haswel) для вас не показатель? 3 minutes ago, haker_fox said: Я уже дал одно из объяснений, зачем нужны эти команды. 'Зачем' нужны, это понятно. Я к тому, что урезав использование регистров внутри кода, можно было уменьшить набор сохраняемых регистров. 3 minutes ago, jcxz said: Тут дело в том, что некоторые радикально настроенные товарищи уверяют что знание ассемблера вообще не нужно. Покажите мне, где я говорил про 'совсем не нужно'? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 7 минут назад, xvr сказал: Я вам написал - нужно переделывать алгоритм. Могу даже написать как, но боюсь вы не поверите :( Так напишите. Не нужно бояться - я не страшный Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 1 minute ago, jcxz said: Вы же пользуетесь эмулятором для отладки? А зачем? Почему не подняться выше и не "посмотреть на алгоритм работы целиком"? И сразу аналитически найти все проблемы? Вы не поверите )))) Только отладочная печать и отладочное махание ножками. Математику отлаживаю на большом брате. Так что ни эмуляторы, ни железные отладчики я не использую. 6 minutes ago, jcxz said: Тут дело в том, что некоторые радикально настроенные товарищи уверяют что знание ассемблера вообще не нужно. Вот с этим и не соглашаюсь. Ну для формошлепов и любителей "нагуглить готовое решение" - не нужно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба И возвращаясь к передаче кучи переменных. Конечно, протокол обмена надо делать другим. Сначала складывать все long, потом все short. Итого код выглядит так: void Dump(long *d, long **s, unsigned int len32, unsigned int len16) { while(len32) { *d++=*(*s++); len32--; } unsigned short **_s; unsigned short *_d; _s=(unsigned short**)s; _d=(unsigned short*)d; while(len16) { *_d++=*(*_s++); len16--; } } Самое занятное - это результат компиляции такого тупого кода: Spoiler 4 void Dump(long *d, long **s, unsigned int len32, unsigned int len16) 5 { \ Dump: (+1) \ 0x0 0xB470 PUSH {R4-R6} \ 0x2 0x4604 MOV R4,R0 \ 0x4 0xB312 CBZ.N R2,??Dump_0 \ 0x6 0xF012 0x0003 ANDS R0,R2,#0x3 \ 0xA 0xD006 BEQ.N ??Dump_1 6 while(len32) 7 { 8 *d++=*(*s++); \ ??Dump_2: (+1) \ 0xC 0xF851 0x5B04 LDR R5,[R1], #+4 \ 0x10 0x682E LDR R6,[R5, #+0] \ 0x12 0xF844 0x6B04 STR R6,[R4], #+4 9 len32--; \ 0x16 0x1E40 SUBS R0,R0,#+1 \ 0x18 0xD1F8 BNE.N ??Dump_2 \ ??Dump_1: (+1) \ 0x1A 0x0892 LSRS R2,R2,#+2 \ 0x1C 0xF000 0x8016 BEQ.W ??Dump_0 \ ??Dump_3: (+1) \ 0x20 0xF851 0x0B04 LDR R0,[R1], #+4 \ 0x24 0x6800 LDR R0,[R0, #+0] \ 0x26 0xF844 0x0B04 STR R0,[R4], #+4 \ 0x2A 0x1E52 SUBS R2,R2,#+1 \ 0x2C 0xF851 0x5B04 LDR R5,[R1], #+4 \ 0x30 0x682D LDR R5,[R5, #+0] \ 0x32 0xF844 0x5B04 STR R5,[R4], #+4 \ 0x36 0xF851 0x0B04 LDR R0,[R1], #+4 \ 0x3A 0x6800 LDR R0,[R0, #+0] \ 0x3C 0xF844 0x0B04 STR R0,[R4], #+4 \ 0x40 0xF851 0x5B04 LDR R5,[R1], #+4 \ 0x44 0x682D LDR R5,[R5, #+0] \ 0x46 0xF844 0x5B04 STR R5,[R4], #+4 \ 0x4A 0xD1E9 BNE.N ??Dump_3 10 } 11 unsigned short **_s; 12 unsigned short *_d; 13 _s=(unsigned short**)s; 14 _d=(unsigned short*)d; \ ??Dump_0: (+1) \ 0x4C 0xB313 CBZ.N R3,??Dump_4 \ 0x4E 0xF013 0x0003 ANDS R0,R3,#0x3 \ 0x52 0xD006 BEQ.N ??Dump_5 15 while(len16) 16 { 17 *_d++=*(*_s++); \ ??Dump_6: (+1) \ 0x54 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x58 0x8815 LDRH R5,[R2, #+0] \ 0x5A 0xF824 0x5B02 STRH R5,[R4], #+2 18 len16--; \ 0x5E 0x1E40 SUBS R0,R0,#+1 \ 0x60 0xD1F8 BNE.N ??Dump_6 \ ??Dump_5: (+1) \ 0x62 0x089B LSRS R3,R3,#+2 \ 0x64 0xF000 0x8016 BEQ.W ??Dump_4 \ ??Dump_7: (+1) \ 0x68 0xF851 0x0B04 LDR R0,[R1], #+4 \ 0x6C 0x8800 LDRH R0,[R0, #+0] \ 0x6E 0xF824 0x0B02 STRH R0,[R4], #+2 \ 0x72 0x1E5B SUBS R3,R3,#+1 \ 0x74 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x78 0x8812 LDRH R2,[R2, #+0] \ 0x7A 0xF824 0x2B02 STRH R2,[R4], #+2 \ 0x7E 0xF851 0x0B04 LDR R0,[R1], #+4 \ 0x82 0x8800 LDRH R0,[R0, #+0] \ 0x84 0xF824 0x0B02 STRH R0,[R4], #+2 \ 0x88 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x8C 0x8812 LDRH R2,[R2, #+0] \ 0x8E 0xF824 0x2B02 STRH R2,[R4], #+2 \ 0x92 0xD1E9 BNE.N ??Dump_7 19 } 20 } \ ??Dump_4: (+1) \ 0x94 0xBC70 POP {R4-R6} \ 0x96 0x4770 BX LR ;; return Если разворота циклов, сделанных самим компилятором, недостаточно, то можно и самому выступить по написанию Duff Device, что несложно. Но и этот код, прямо скажем, хуже совсем на копейку, нежели JIT, но на порядок проще. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 56 minutes ago, jcxz said: Так напишите. Не нужно бояться - я не страшный Ну хорошо. Обрисовываю основную идею: Нужно дать возможность компилятору пооптимизировать чтение из переменных и присваивание в память. Для этого нужно сделать группы присваиваний без условных. Для этого все 64 бита делим на группы по 4 бита (можно больше), и для каждого возможного сочетания битов делаем блок с присваиваниями. Потом 16 switch'ей по 16 вариантов. как то так: switch(bm&15) { case 1: *(...)p = var0; p+=...; break; case 2: *(...)p = var1; p+=...; break; case 3: *(...)p = var0; *(...)(p+..) = var1; p+=... ; break; .... } switch(bm&0xF0) { case 0x10: ... ... } ... Разумеется всю эту простыню нужно генерировать скриптом снаружи Кстати, это ещё можно пооптимизировать, если сделать дублирование этого всего для разного выравнивания входного указателя Очевидно что JIT будет лучше, но сделать JIT, что бы он генерировал оптимальные присваивания для произвольного bm весьма непростая задача (и знания ассемблера тут недостаточно) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 7 minutes ago, xvr said: Ну хорошо. Обрисовываю основную идею: Идея, в общем, ничего так (если флеша вагон), но есть нюанс, касающийся именно ARM. Загрузка регистра из произвольной переменной стоит столько же, сколько *(*list++). На самом деле для ARM загрузка переменной в регистр - это *(*(pc+disp)). Потому никакого выигрыша не будет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 41 minutes ago, Rst7 said: Потому никакого выигрыша не будет. Уберётся инкремент указателя на каждую загрузку (останется 16 из 64 инкрементов), плюс невыровненные обращения компилятор [наверное] сможет разбросать. Если исходный буфер был выровненный, то некоторые запись в память можно отконвертировать из невыровненных в выровненные. (Не помню, в M4 есть запись невыровненных данных?) Ну и условных переходов будет меньше 45 minutes ago, Rst7 said: Загрузка регистра из произвольной переменной Тут мы пытаемся экономить запись в память Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 1 час назад, Rst7 сказал: Конечно, протокол обмена надо делать другим. Сначала складывать все long, потом все short. Итого код выглядит так: Ну во-первых: Я хотел располагать переменные именно в произвольном порядке перемешивая u32 и u16. Т.е. - они отсортированы по смысловой близости. Да - можно было конечно передавать и так, а на приёмной стороне (где быстродействие не важно) сделать перетасовку данных. Кроме того - что-то ещё меня менять порядок переменных, уже не помню. Цитата Если разворота циклов, сделанных самим компилятором, недостаточно, то можно и самому выступить по написанию Duff Device, что несложно. Но и этот код, прямо скажем, хуже совсем на копейку, нежели JIT, но на порядок проще. Во-вторых: Как раз "разворотов цикла" по логике работы алгоритма быть не должно. Какие могут быть развороты если входные данные - переменные? Вы просто неправильно задали условия компиляции. И вот в этом как раз и есть один из главных плюсов знания ассемблера, о котором я уже писал выше: По результату (по тому, что случился разворот цикла) человек, знающий ассемблер сразу поймёт, что где-то у него закралась ошибка. Всего лишь взглянув в окно дизасма. Даже не отлаживая - сразу видно. 1 час назад, xvr сказал: Потом 16 switch'ей по 16 вариантов. Проигрыш будет уже на этом этапе. Ибо 16 безусловных switch-ей - это как минимум 16 операций выделения группы бит и перехода по таблице. А у меня максимальное количество одновременно включённых каналов осциллографирования от 14 до 28. А тут только switch-ей - 16, не считая самих копирований. Ну и флеша ещё вагон потребуется. 1 час назад, xvr сказал: Очевидно что JIT будет лучше, но сделать JIT, что бы он генерировал оптимальные присваивания для произвольного bm весьма непростая задача (и знания ассемблера тут недостаточно) Архитектура - ARM (Cortex). Что там сложного в присваиваниях? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 25 минут назад, xvr сказал: Уберётся инкремент указателя на каждую загрузку (останется 16 из 64 инкрементов), А ARM есть автоинкрементная адресация (инкремент входит в саму команду). К тому же для загрузки адреса у меня инкремент указателя не используется. Мой полиморфный код формирует чтение адресов из таблицы адресов переменных с константными смещениями (в коде команды) внутри таблицы. 25 минут назад, xvr сказал: плюс невыровненные обращения компилятор [наверное] сможет разбросать. Невыровненные останутся невыровненными, с тратой лишнего такта. Единственный плюс - спаривание двух подряд идущих чтений или чтения+записи - в ARM насколько знаю это даёт уменьшение длительности выполнения этой пары на один такт. Но моя функция, генерящая код, это учитывает, и так же делает. 25 минут назад, xvr сказал: Если исходный буфер был выровненный, то некоторые запись в память можно отконвертировать из невыровненных в выровненные. (Не помню, в M4 есть запись невыровненных данных?) Тут - не понял... 25 минут назад, xvr сказал: Ну и условных переходов будет меньше Но они будут. Переходы. Как минимум - 32 штуки. До есть - дофига. В ARM - не важно - условные или нет - длительность одинакова, предсказаний вроде нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 34 minutes ago, xvr said: Уберётся инкремент указателя на каждую загрузку Это в ARM бесплатно. 20 minutes ago, jcxz said: Во-вторых: Как раз "разворотов цикла" по логике работы алгоритма быть не должно. Какие могут быть развороты если входные данные - переменные? Вы просто неправильно задали условия компиляции. Все я правильно задал. Циклы разворачиваются не на константное число. А все что можно, делается блоками по несколько итераций, а остальное - уже по одной штучке. На самом деле, если у Вас действительно ограничено число переменных, то Duff Device - это самое то в данном случае. 22 minutes ago, jcxz said: А у меня максимальное количество одновременно включённых каналов осциллографирования от 14 до 28. Ага, ну тогда - DD: void DumpDuff(long *d, long **s, int len32) { switch(len32) { default: case 28: *d++=*(*s++); case 27: *d++=*(*s++); case 26: *d++=*(*s++); case 25: *d++=*(*s++); case 24: *d++=*(*s++); case 23: *d++=*(*s++); case 22: *d++=*(*s++); case 21: *d++=*(*s++); case 20: *d++=*(*s++); case 19: *d++=*(*s++); case 18: *d++=*(*s++); case 17: *d++=*(*s++); case 16: *d++=*(*s++); case 15: *d++=*(*s++); case 14: *d++=*(*s++); case 13: *d++=*(*s++); case 12: *d++=*(*s++); case 11: *d++=*(*s++); case 10: *d++=*(*s++); case 9: *d++=*(*s++); case 8: *d++=*(*s++); case 7: *d++=*(*s++); case 6: *d++=*(*s++); case 5: *d++=*(*s++); case 4: *d++=*(*s++); case 3: *d++=*(*s++); case 2: *d++=*(*s++); case 1: *d++=*(*s++); case 0: ; } } И все, бинго: Spoiler 23 void DumpDuff(long *d, long **s, int len32) 24 { \ DumpDuff: (+1) \ 0x0 0xB410 PUSH {R4} 25 switch(len32) \ 0x2 0x2A1B CMP R2,#+27 \ 0x4 0xD80F BHI.N ??DumpDuff_1 \ 0x6 0xE8DF 0xF002 TBB [PC, R2] \ ??DumpDuff_0: \ 0xA 0x98 0x95 DC8 0x98,0x95,0x90,0x8B \ 0x90 0x8B \ 0xE 0x86 0x81 DC8 0x86,0x81,0x7C,0x77 \ 0x7C 0x77 \ 0x12 0x72 0x6D DC8 0x72,0x6D,0x68,0x63 \ 0x68 0x63 \ 0x16 0x5E 0x59 DC8 0x5E,0x59,0x54,0x4F \ 0x54 0x4F \ 0x1A 0x4A 0x45 DC8 0x4A,0x45,0x40,0x3B \ 0x40 0x3B \ 0x1E 0x36 0x31 DC8 0x36,0x31,0x2C,0x27 \ 0x2C 0x27 \ 0x22 0x22 0x1D DC8 0x22,0x1D,0x18,0x13 \ 0x18 0x13 26 { 27 default: 28 case 28: *d++=*(*s++); \ ??DumpDuff_1: (+1) \ 0x26 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x2A 0x6813 LDR R3,[R2, #+0] \ 0x2C 0xF840 0x3B04 STR R3,[R0], #+4 29 case 27: *d++=*(*s++); \ ??DumpDuff_2: (+1) \ 0x30 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0x34 0x6822 LDR R2,[R4, #+0] \ 0x36 0xF840 0x2B04 STR R2,[R0], #+4 30 case 26: *d++=*(*s++); \ ??DumpDuff_3: (+1) \ 0x3A 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x3E 0x681C LDR R4,[R3, #+0] \ 0x40 0xF840 0x4B04 STR R4,[R0], #+4 31 case 25: *d++=*(*s++); \ ??DumpDuff_4: (+1) \ 0x44 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x48 0x6813 LDR R3,[R2, #+0] \ 0x4A 0xF840 0x3B04 STR R3,[R0], #+4 32 case 24: *d++=*(*s++); \ ??DumpDuff_5: (+1) \ 0x4E 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0x52 0x6822 LDR R2,[R4, #+0] \ 0x54 0xF840 0x2B04 STR R2,[R0], #+4 33 case 23: *d++=*(*s++); \ ??DumpDuff_6: (+1) \ 0x58 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x5C 0x681C LDR R4,[R3, #+0] \ 0x5E 0xF840 0x4B04 STR R4,[R0], #+4 34 case 22: *d++=*(*s++); \ ??DumpDuff_7: (+1) \ 0x62 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x66 0x6813 LDR R3,[R2, #+0] \ 0x68 0xF840 0x3B04 STR R3,[R0], #+4 35 case 21: *d++=*(*s++); \ ??DumpDuff_8: (+1) \ 0x6C 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0x70 0x6822 LDR R2,[R4, #+0] \ 0x72 0xF840 0x2B04 STR R2,[R0], #+4 36 case 20: *d++=*(*s++); \ ??DumpDuff_9: (+1) \ 0x76 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x7A 0x681C LDR R4,[R3, #+0] \ 0x7C 0xF840 0x4B04 STR R4,[R0], #+4 37 case 19: *d++=*(*s++); \ ??DumpDuff_10: (+1) \ 0x80 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x84 0x6813 LDR R3,[R2, #+0] \ 0x86 0xF840 0x3B04 STR R3,[R0], #+4 38 case 18: *d++=*(*s++); \ ??DumpDuff_11: (+1) \ 0x8A 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0x8E 0x6822 LDR R2,[R4, #+0] \ 0x90 0xF840 0x2B04 STR R2,[R0], #+4 39 case 17: *d++=*(*s++); \ ??DumpDuff_12: (+1) \ 0x94 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x98 0x681C LDR R4,[R3, #+0] \ 0x9A 0xF840 0x4B04 STR R4,[R0], #+4 40 case 16: *d++=*(*s++); \ ??DumpDuff_13: (+1) \ 0x9E 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0xA2 0x6813 LDR R3,[R2, #+0] \ 0xA4 0xF840 0x3B04 STR R3,[R0], #+4 41 case 15: *d++=*(*s++); \ ??DumpDuff_14: (+1) \ 0xA8 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0xAC 0x6822 LDR R2,[R4, #+0] \ 0xAE 0xF840 0x2B04 STR R2,[R0], #+4 42 case 14: *d++=*(*s++); \ ??DumpDuff_15: (+1) \ 0xB2 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0xB6 0x681C LDR R4,[R3, #+0] \ 0xB8 0xF840 0x4B04 STR R4,[R0], #+4 43 case 13: *d++=*(*s++); \ ??DumpDuff_16: (+1) \ 0xBC 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0xC0 0x6813 LDR R3,[R2, #+0] \ 0xC2 0xF840 0x3B04 STR R3,[R0], #+4 44 case 12: *d++=*(*s++); \ ??DumpDuff_17: (+1) \ 0xC6 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0xCA 0x6822 LDR R2,[R4, #+0] \ 0xCC 0xF840 0x2B04 STR R2,[R0], #+4 45 case 11: *d++=*(*s++); \ ??DumpDuff_18: (+1) \ 0xD0 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0xD4 0x681C LDR R4,[R3, #+0] \ 0xD6 0xF840 0x4B04 STR R4,[R0], #+4 46 case 10: *d++=*(*s++); \ ??DumpDuff_19: (+1) \ 0xDA 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0xDE 0x6813 LDR R3,[R2, #+0] \ 0xE0 0xF840 0x3B04 STR R3,[R0], #+4 47 case 9: *d++=*(*s++); \ ??DumpDuff_20: (+1) \ 0xE4 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0xE8 0x6822 LDR R2,[R4, #+0] \ 0xEA 0xF840 0x2B04 STR R2,[R0], #+4 48 case 8: *d++=*(*s++); \ ??DumpDuff_21: (+1) \ 0xEE 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0xF2 0x681C LDR R4,[R3, #+0] \ 0xF4 0xF840 0x4B04 STR R4,[R0], #+4 49 case 7: *d++=*(*s++); \ ??DumpDuff_22: (+1) \ 0xF8 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0xFC 0x6813 LDR R3,[R2, #+0] \ 0xFE 0xF840 0x3B04 STR R3,[R0], #+4 50 case 6: *d++=*(*s++); \ ??DumpDuff_23: (+1) \ 0x102 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0x106 0x6822 LDR R2,[R4, #+0] \ 0x108 0xF840 0x2B04 STR R2,[R0], #+4 51 case 5: *d++=*(*s++); \ ??DumpDuff_24: (+1) \ 0x10C 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x110 0x681C LDR R4,[R3, #+0] \ 0x112 0xF840 0x4B04 STR R4,[R0], #+4 52 case 4: *d++=*(*s++); \ ??DumpDuff_25: (+1) \ 0x116 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x11A 0x6813 LDR R3,[R2, #+0] \ 0x11C 0xF840 0x3B04 STR R3,[R0], #+4 53 case 3: *d++=*(*s++); \ ??DumpDuff_26: (+1) \ 0x120 0xF851 0x4B04 LDR R4,[R1], #+4 \ 0x124 0x6822 LDR R2,[R4, #+0] \ 0x126 0xF840 0x2B04 STR R2,[R0], #+4 54 case 2: *d++=*(*s++); \ ??DumpDuff_27: (+1) \ 0x12A 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x12E 0x681C LDR R4,[R3, #+0] \ 0x130 0xF840 0x4B04 STR R4,[R0], #+4 55 case 1: *d++=*(*s++); \ ??DumpDuff_28: (+1) \ 0x134 0x6809 LDR R1,[R1, #+0] \ 0x136 0x680A LDR R2,[R1, #+0] \ 0x138 0x6002 STR R2,[R0, #+0] 56 case 0: ; 57 } 58 } \ ??DumpDuff_29: (+1) \ 0x13A 0xBC10 POP {R4} \ 0x13C 0x4770 BX LR ;; return 59 Аналогично допиливается вторая итерация с short'ами. И да, возможно, что для short'ов будет веселее вот так: void DumpDuff16(long *d, short **s, int len32) { unsigned long v=0; switch(len32) { default: case 28: v=*(*s++); case 27: *d++=(v<<16)|*(*s++); case 26: v=*(*s++); case 25: *d++=(v<<16)|*(*s++); .... 64 { 65 default: 66 case 28: v=*(*s++); \ ??DumpDuff16_1: (+1) \ 0x28 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x2C 0x5ED3 LDRSH R3,[R2, R3] 67 case 27: *d++=(v<<16)|*(*s++); \ ??DumpDuff16_2: (+1) \ 0x2E 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x32 0xF9B2 0x4000 LDRSH R4,[R2, #+0] \ 0x36 0xEA44 0x4403 ORR R4,R4,R3, LSL #+16 \ 0x3A 0xF840 0x4B04 STR R4,[R0], #+4 68 case 26: v=*(*s++); \ ??DumpDuff16_3: (+1) \ 0x3E 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x42 0xF9B3 0x3000 LDRSH R3,[R3, #+0] 69 case 25: *d++=(v<<16)|*(*s++); \ ??DumpDuff16_4: (+1) \ 0x46 0xF851 0x2B04 LDR R2,[R1], #+4 \ 0x4A 0xF9B2 0x4000 LDRSH R4,[R2, #+0] \ 0x4E 0xEA44 0x4403 ORR R4,R4,R3, LSL #+16 \ 0x52 0xF840 0x4B04 STR R4,[R0], #+4 .... Пардон, там, конечно, unsigned short ** нужен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба Ну и пайплайн врукопашную: void DumpDuff(long *d, long **s, int len32) { long *p=*s++; long v; switch(len32) { default: case 28: v=*p; p=*s++; *d++=v; case 27: v=*p; p=*s++; *d++=v; case 26: v=*p; p=*s++; *d++=v; case 25: v=*p; p=*s++; *d++=v; case 24: v=*p; p=*s++; *d++=v; .... 29 default: 30 case 28: v=*p; p=*s++; *d++=v; \ ??DumpDuff_1: (+1) \ 0x2A 0x681A LDR R2,[R3, #+0] \ 0x2C 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x30 0xF840 0x2B04 STR R2,[R0], #+4 31 case 27: v=*p; p=*s++; *d++=v; \ ??DumpDuff_2: (+1) \ 0x34 0x681A LDR R2,[R3, #+0] \ 0x36 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x3A 0xF840 0x2B04 STR R2,[R0], #+4 32 case 26: v=*p; p=*s++; *d++=v; \ ??DumpDuff_3: (+1) \ 0x3E 0x681A LDR R2,[R3, #+0] \ 0x40 0xF851 0x3B04 LDR R3,[R1], #+4 \ 0x44 0xF840 0x2B04 STR R2,[R0], #+4 .... Кстати, а вот еще момент: Quote LDR Rx,[PC,#imm] might add a cycle because of contention with the fetch unit. Так что загрузка адреса из списка более эффективна. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 6 февраля, 2020 Опубликовано 6 февраля, 2020 · Жалоба 45 минут назад, Rst7 сказал: Все я правильно задал. Циклы разворачиваются не на константное число. А все что можно, делается блоками по несколько итераций, а остальное - уже по одной штучке. Ок, посмотрел внимательнее - да, всё норм. Но такая оптимизация в моём случае - только вредна. Так как общее макс.число копирований у меня - от 14 до 28 штук разного размера (макс.входной размер кадра ==56байт, макс.количество копирований зависит от их разрядности - сколько влезет). Такие оптимизации: "по 1", "по 2", "по 4" будут серьёзно экономить только на больших однородных копированиях (16-битных или 32-битных), а значит тут - бесполезны, даже вредны. Цитата На самом деле, если у Вас действительно ограничено число переменных, то Duff Device - это самое то в данном случае. У меня ограниченный размер выходного кадра. 56 байт. Цитата Ага, ну тогда - DD: Ну и получили примерно то же самое, что у меня на выходе генератора полиморфного кода. Только с ветвлением на входе, да состоящее из двух кусков. Т.е. - по скорости не лучше, а скорее несколько хуже. PS: Вобщем - мы отклонились от темы. Речь тут шла не об обсуждении моего конкретного кода, а о необходимости знания ассемблера как такового. И этот пример (полиморфный код) может быть не самый удачный по данной теме был. А вот по другим 2-м примерам так никто ничего и не сказал. Какая альтернатива? Особенно касается алгоритма поиска максимума/минимума на массиве из 512 несортированных 16-разрядных чисел. Есть мысли как сделать оптимально, обойдясь без ассемблера? Для Cortex-M4 и выше. 23 минуты назад, Rst7 сказал: Так что загрузка адреса из списка более эффективна. Я писал выше, что мой генератор как раз такие команды и генерит. К тому же оптимизируя смещения так, чтобы команды чтения адресов были 2-байтными. Кроме того - мой генератор ещё делает предварительную загрузку адреса переменной, так как команда LDR Rx, [Ry] будет длиннее на 1 такт если значение Ry изменялось в предыдущем такте. У меня это исключается. На выходе генератора получается такой код: ;Примерная схема формируемого кода. Первый столбец - условное наименование команды для ссылок в комментах выше. ;f0 LDR tmpReg0, [tblReg, #bit1*4]; где bit1 - номер первого единичного бита в мл.слове бит-карты ;f0 LDR tmpReg1, [tblReg, #bit2*4]; где bit2 - номер второго единичного бита в мл.слове бит-карты ;f1 LDR/LDRH tmpReg0, [tmpReg0] ;f2 STR/STRH tmpReg0, [dstReg], #step; где step==4 для STR и step==2 для STRH ;f0 LDR tmpReg0, [tblReg, #bit3*4]; где bit3 - номер 3-го единичного бита в мл.слове бит-карты ;f1 LDR/LDRH tmpReg1, [tmpReg1] ;f2 STR/STRH tmpReg1, [dstReg], #step; где step==4 для STR и step==2 для STRH ;f0 LDR tmpReg1, [tblReg, #bit4*4]; где bit4 - номер 4-го единичного бита в мл.слове бит-карты ;f1 LDR/LDRH tmpReg0, [tmpReg0] ;f2 STR/STRH tmpReg0, [dstReg], #step; где step==4 для STR и step==2 для STRH ; ... ;f3 ADDS tblReg, tblReg, #32*4; вставляется при переходе к ст.слову бит-карты если в нём есть единицы ;f0 LDR tmpReg0, [tblReg, #bit1*4]; где bit1 - номер 1-го единичного бита в ст.слове бит-карты ;f1 LDR/LDRH tmpReg1, [tmpReg1] ;f2 STR/STRH tmpReg1, [dstReg], #step; где step==4 для STR и step==2 для STRH ;f0 LDR tmpReg1, [tblReg, #bit2*4]; где bit2 - номер 2-го единичного бита в ст.слове бит-карты ;f1 LDR/LDRH tmpReg0, [tmpReg0] ;f2 STR/STRH tmpReg0, [dstReg], #step; где step==4 для STR и step==2 для STRH ; ... ;f1 LDR/LDRH tmpReg1, [tmpReg1] ;f2 STR/STRH tmpReg1, [dstReg], #step; где step==4 для STR и step==2 для STRH ;f4 BX LR Как видно - PUSH/POP-ов тоже никаких нет. Это для xvr. Вобщем - предлагаю закончить с этим примером и вернуться к остальным 2-м. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться