Jump to content

    

Навык анализа листингов

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);

 

В общем - любим, умеем, практикуем, но в первую очередь стараемся не плодить кучу такого кода.

Share this post


Link to post
Share on other sites
52 minutes ago, Arlleex said:

Темы asm vs C, Windows vs Linux, AVR vs PIC, видимо, никогда не прекратятся на этом Форуме (да вообще нигде):biggrin:

 

52 minutes ago, Arlleex said:

Как будто насильно заставляет кто-то использовать этот несчастный ассемблер, ей-богу:biggrin:

Тема немного о другом, если поглядите стартовое сообщение)

1 hour ago, xvr said:

PUSH {R4 - R7, LR}

POP {R4 - R7, PC}

Я уже дал одно из объяснений, зачем нужны эти команды.

Share this post


Link to post
Share on other sites
2 минуты назад, Rst7 сказал:

Дайте я догадаюсь ;) Это же надо было исключительно на этапе отладки? Неужели настолько там все друг с другом запутано, что не получилось отладить по-модульно, например, а не собирать переменные из кучи мест?

Запутанно да - сильно, ибо проект сложный. И этап отладки - это не пара дней как можно подумать, а годы, пока живёт и развивается проект.

И "отладка" здесь - это не только отладка ПО, но и железа, которое подключено - для этого оно даже нужнее. И железо разное и меняется и будет меняться.

 

2 минуты назад, Rst7 сказал:

В таком ключе на Ваш вопрос нет ответа. Надо подняться с уровня гайки М3 (поступающих данных от АЦП) и посмотреть на алгоритм работы целиком.

Так вот это осциллографирование как раз и позволяет оценить правильность работы всего алгоритма целиком. И искать пути улучшения.

Вы же пользуетесь эмулятором для отладки? А зачем? Почему не подняться выше и не "посмотреть на алгоритм работы целиком"? И сразу аналитически найти все проблемы?  :wink:

Вот и здесь - то же.

 

2 минуты назад, Rst7 сказал:

В общем - любим, умеем, практикуем, но в первую очередь стараемся не плодить кучу такого кода.

Ну а кто- говорит за "кучу"? У меня ассемблера в во всём ПО - думаю менее 5%.

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

Share this post


Link to post
Share on other sites
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:

Тут дело в том, что некоторые радикально настроенные товарищи уверяют что знание ассемблера вообще не нужно.

Покажите мне, где я говорил про 'совсем не нужно'?

Share this post


Link to post
Share on other sites
7 минут назад, xvr сказал:

Я вам написал - нужно переделывать алгоритм. Могу даже написать как, но боюсь вы не поверите :(

Так напишите. Не нужно бояться - я не страшный  :yes:

Share this post


Link to post
Share on other sites
1 minute ago, jcxz said:

Вы же пользуетесь эмулятором для отладки? А зачем? Почему не подняться выше и не "посмотреть на алгоритм работы целиком"? И сразу аналитически найти все проблемы?

Вы не поверите )))) Только отладочная печать и отладочное махание ножками. Математику отлаживаю на большом брате. Так что ни эмуляторы, ни железные отладчики я не использую.

6 minutes ago, jcxz said:

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

Ну для формошлепов и любителей "нагуглить готовое решение" - не нужно.

Share this post


Link to post
Share on other sites

И возвращаясь к передаче кучи переменных.

Конечно, протокол обмена надо делать другим. Сначала складывать все 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, но на порядок проще.

Share this post


Link to post
Share on other sites
56 minutes ago, jcxz said:

Так напишите. Не нужно бояться - я не страшный  :yes:

Ну хорошо. Обрисовываю основную идею:

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

Для этого все 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 весьма непростая задача (и знания ассемблера тут недостаточно)

Share this post


Link to post
Share on other sites
7 minutes ago, xvr said:

Ну хорошо. Обрисовываю основную идею:

Идея, в общем, ничего так (если флеша вагон), но есть нюанс, касающийся именно ARM. Загрузка регистра из произвольной переменной стоит столько же, сколько *(*list++). На самом деле для ARM загрузка переменной в регистр - это *(*(pc+disp)). Потому никакого выигрыша не будет.

Share this post


Link to post
Share on other sites
41 minutes ago, Rst7 said:

Потому никакого выигрыша не будет.

Уберётся инкремент указателя на каждую загрузку (останется 16 из 64 инкрементов), плюс невыровненные обращения компилятор [наверное] сможет разбросать. Если исходный буфер был выровненный, то некоторые запись в память можно отконвертировать из невыровненных в выровненные. (Не помню, в M4 есть запись невыровненных данных?)

Ну и условных переходов будет меньше

45 minutes ago, Rst7 said:

Загрузка регистра из произвольной переменной

Тут мы пытаемся экономить запись в память

Share this post


Link to post
Share on other sites
1 час назад, Rst7 сказал:

Конечно, протокол обмена надо делать другим. Сначала складывать все long, потом все short. Итого код выглядит так:

Ну во-первых: Я хотел располагать переменные именно в произвольном порядке перемешивая u32 и u16. Т.е. - они отсортированы по смысловой близости. Да - можно было конечно передавать и так, а на приёмной стороне (где быстродействие не важно) сделать перетасовку данных. Кроме того - что-то ещё меня менять порядок переменных, уже не помню.

 

Цитата

Если разворота циклов, сделанных самим компилятором, недостаточно, то можно и самому выступить по написанию Duff Device, что несложно. Но и этот код, прямо скажем, хуже совсем на копейку, нежели JIT, но на порядок проще.

Во-вторых: Как раз "разворотов цикла" по логике работы алгоритма быть не должно. Какие могут быть развороты если входные данные - переменные? Вы просто неправильно задали условия компиляции.

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

1 час назад, xvr сказал:

Потом 16 switch'ей по 16 вариантов.

Проигрыш будет уже на этом этапе. Ибо 16 безусловных switch-ей - это как минимум 16 операций выделения группы бит и перехода по таблице. А у меня максимальное количество одновременно включённых каналов осциллографирования от 14 до 28. А тут только switch-ей - 16, не считая самих копирований.

Ну и флеша ещё вагон потребуется.

1 час назад, xvr сказал:

Очевидно что JIT будет лучше, но сделать JIT, что бы он генерировал оптимальные присваивания для произвольного bm весьма непростая задача (и знания ассемблера тут недостаточно)

Архитектура - ARM (Cortex). Что там сложного в присваиваниях?

Share this post


Link to post
Share on other sites
25 минут назад, xvr сказал:

Уберётся инкремент указателя на каждую загрузку (останется 16 из 64 инкрементов),

А ARM есть автоинкрементная адресация (инкремент входит в саму команду). К тому же для загрузки адреса у меня инкремент указателя не используется. Мой полиморфный код формирует чтение адресов из таблицы адресов переменных с константными смещениями (в коде команды) внутри таблицы.

25 минут назад, xvr сказал:

плюс невыровненные обращения компилятор [наверное] сможет разбросать.

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

25 минут назад, xvr сказал:

Если исходный буфер был выровненный, то некоторые запись в память можно отконвертировать из невыровненных в выровненные. (Не помню, в M4 есть запись невыровненных данных?)

Тут - не понял... :wacko2:

25 минут назад, xvr сказал:

Ну и условных переходов будет меньше

Но они будут. Переходы. Как минимум - 32 штуки. До есть - дофига. В ARM - не важно - условные или нет - длительность одинакова, предсказаний вроде нет.

Share this post


Link to post
Share on other sites
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 ** нужен.

Share this post


Link to post
Share on other sites

Ну и пайплайн врукопашную:

	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.

Так что загрузка адреса из списка более эффективна.

Share this post


Link to post
Share on other sites
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-м.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now