Jump to content

    

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

4 hours ago, jcxz said:

Ну-ка - покажите класс - напишите на си код, который по эффективности будет хотя-бы сравним с:

Кстати не факт, что ваш код эффективен - push и pop 4х регистров на входе/выходе может съесть больше, чем вы выиграли на жонглировании регистрами

 

Share this post


Link to post
Share on other sites
3 hours ago, Forger said:

Есть еще среди них упорные адепты этой старой "школы"

Если говорить про меня, то я просто хочу получить новые знания. Программировать полностью на асме не заставляю ни кого. У нас уже есть один приверженец линукса))) Который навязывает его всем и вся.

1 hour ago, Forger said:

но и аппаратно встроены в МК....

Не надо)

32 minutes ago, xvr said:

Intel компилятор под x86 это умеет.

Ну эти камешки я не программирую.

32 minutes ago, xvr said:

Да и интристики никто не отменял

Это, лишь, иная форма записи ассемблерных команд...

1 minute ago, xvr said:

push и pop 4х регистров на входе/выходе может съесть больше,

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

Share this post


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

Кстати не факт, что ваш код эффективен - push и pop 4х регистров на входе/выходе может съесть больше, чем вы выиграли на жонглировании регистрами

Если бы Вы хотя-бы что-то подтвердили фактами, т.е. - кодом, то Ваши слова имели бы какой-то вес. На представленные 3 примера Вы ничего не смогли предоставить, никакого фактического доказательства кроме пустых теоретизирований. Из чего делаю вывод, что в теме Вы не разбираетесь.  :unknw:

PS: PUSH/POP каких-то регистров вообще не понимаю - к чему тут?

 

4 часа назад, haker_fox сказал:

Это, лишь, иная форма записи ассемблерных команд...

Вот именно...

Share this post


Link to post
Share on other sites
9 hours ago, jcxz said:

вот другой пример: Есть код, который состоит из длинного полотенца:

Мне кажется, Вы сильно загоняетесь.

Решение в лоб:

	unsigned long vars[64];
	 
	#define PROC(B,T,N) \
	if (mask&(1UL<<B)) {T *_d; _d=d; *_d++=vars[N]; d=_d;}
	 
	void f(unsigned long mask, void *d)
	{
	  PROC(0,long,0);
	  PROC(1,short,1);
	  PROC(2,long,2);
	  PROC(3,long,3);
	  PROC(4,short,4);
	  PROC(5,short,5);
	  PROC(6,long,6);
	  PROC(7,short,7);
	}
	

дает вполне вменяемый результат:

Spoiler

				
			   \                                 In section .bss, align 4
		      2          unsigned long vars[64];
		   \                     vars:
		   \        0x0                      DS8 256
		      3          
		      4          #define PROC(B,T,N) \
		      5          if (mask&(1UL<<B)) {T *_d; _d=d; *_d++=vars[N]; d=_d;}
		      6          				
			   \                                 In section .text, align 2, keep-with-next
		      7          void f(unsigned long mask, void *d)
		      8          {
		      9            PROC(0,long,0);
		   \                     f: (+1)
		   \        0x0   0x....             LDR.N    R2,??DataTable1
		   \        0x2   0x07C3             LSLS     R3,R0,#+31
		   \        0x4   0xBF44             ITT      MI 
		   \        0x6   0xF8D2 0xC000      LDRMI    R12,[R2, #+0]
		   \        0xA   0xF841 0xCB04      STRMI    R12,[R1], #+4
		     10            PROC(1,short,1);
		   \        0xE   0x0783             LSLS     R3,R0,#+30
		   \       0x10   0xBF44             ITT      MI 
		   \       0x12   0xF8D2 0xC004      LDRMI    R12,[R2, #+4]
		   \       0x16   0xF821 0xCB02      STRHMI   R12,[R1], #+2
		     11            PROC(2,long,2);
		   \       0x1A   0x0743             LSLS     R3,R0,#+29
		   \       0x1C   0xBF44             ITT      MI 
		   \       0x1E   0xF8D2 0xC008      LDRMI    R12,[R2, #+8]
		   \       0x22   0xF841 0xCB04      STRMI    R12,[R1], #+4
		     12            PROC(3,long,3);
		   \       0x26   0x0703             LSLS     R3,R0,#+28
		   \       0x28   0xBF44             ITT      MI 
		   \       0x2A   0xF8D2 0xC00C      LDRMI    R12,[R2, #+12]
		   \       0x2E   0xF841 0xCB04      STRMI    R12,[R1], #+4
		     13            PROC(4,short,4);
		   \       0x32   0x06C3             LSLS     R3,R0,#+27
		   \       0x34   0xBF44             ITT      MI 
		   \       0x36   0xF8D2 0xC010      LDRMI    R12,[R2, #+16]
		   \       0x3A   0xF821 0xCB02      STRHMI   R12,[R1], #+2
		     14            PROC(5,short,5);
		   \       0x3E   0x0683             LSLS     R3,R0,#+26
		   \       0x40   0xBF44             ITT      MI 
		   \       0x42   0xF8D2 0xC014      LDRMI    R12,[R2, #+20]
		   \       0x46   0xF821 0xCB02      STRHMI   R12,[R1], #+2
		     15            PROC(6,long,6);
		   \       0x4A   0x0643             LSLS     R3,R0,#+25
		   \       0x4C   0xBF44             ITT      MI 
		   \       0x4E   0xF8D2 0xC018      LDRMI    R12,[R2, #+24]
		   \       0x52   0xF841 0xCB04      STRMI    R12,[R1], #+4
		     16            PROC(7,short,7);
		   \       0x56   0x0600             LSLS     R0,R0,#+24
		   \       0x58   0xBF44             ITT      MI 
		   \       0x5A   0x69D2             LDRMI    R2,[R2, #+28]
		   \       0x5C   0x800A             STRHMI   R2,[R1, #+0]
		     17          }
		   \       0x5E   0x4770             BX       LR               ;; return
		 				
			

Тут, конечно, может возникнуть вопрос другого характера. Например, если почти все параметры используются, то грузить их в регистры при помощи LDM будет малость эффективнее. А вот у IAR'а с использованием LDM как-то не сложилось, черт его знает, почему.

Но возможен другой вариант. Допустим, что из всего списка в реальности надо вообще всего пару-тройку параметров генерить. Тогда куда более эффективным решением будет такое:

	typedef void (*FillFunc)(void *, void *, long *);
	 
	#define GLUE(A,B) A##B
	 
	#define FillParam(T,N) \
	void GLUE(FillParam,N)(void *functab, void *d, long *s) \
	{ \
	  T *_d; \
	  _d=d; \
	  *_d++=s[N]; \
	  FillFunc *_functab=functab; \
	  FillFunc nf=*_functab++; \
	  nf(_functab,_d,s); \
	}
	 
	FillParam(long,0)
	FillParam(short,1)
	FillParam(long,2)
	FillParam(short,3)
	 
	void FillParamEnd(void *functab, void *d, long *s)
	{
	}
	 
	FillFunc FillerTask[]=
	{
	  FillParam0,FillParam1,FillParam2,FillParam3,
	  FillParamEnd
	};
	 
	void FillParamBegin(void *d, long *s)
	{
	  FillFunc *functab=FillerTask;
	  FillFunc nf=*functab++;
	  nf((void*)functab,d,s);
	}
	

Результат как на асме:

Spoiler

				
			     19          typedef void (*FillFunc)(void *, void *, long *);
		     20          
		     21          #define GLUE(A,B) A##B
		     22          
		     23          #define FillParam(T,N) \
		     24          void GLUE(FillParam,N)(void *functab, void *d, long *s) \
		     25          { \
		     26            T *_d; \
		     27            _d=d; \
		     28            *_d++=s[N]; \
		     29            FillFunc *_functab=functab; \
		     30            FillFunc nf=*_functab++; \
		     31            nf(_functab,_d,s); \
		     32          }
		     33          				
			   \                                 In section .text, align 2, keep-with-next
		     34          FillParam(long,0)
		   \                     FillParam0: (+1)
		   \        0x0   0x6813             LDR      R3,[R2, #+0]
		   \        0x2   0xF841 0x3B04      STR      R3,[R1], #+4
		   \        0x6   0xF850 0x3B04      LDR      R3,[R0], #+4
		   \        0xA   0x4718             BX       R3				
			   \                                 In section .text, align 2, keep-with-next
		     35          FillParam(short,1)
		   \                     FillParam1: (+1)
		   \        0x0   0x6853             LDR      R3,[R2, #+4]
		   \        0x2   0xF821 0x3B02      STRH     R3,[R1], #+2
		   \        0x6   0xF850 0x3B04      LDR      R3,[R0], #+4
		   \        0xA   0x4718             BX       R3				
			   \                                 In section .text, align 2, keep-with-next
		     36          FillParam(long,2)
		   \                     FillParam2: (+1)
		   \        0x0   0x6893             LDR      R3,[R2, #+8]
		   \        0x2   0xF841 0x3B04      STR      R3,[R1], #+4
		   \        0x6   0xF850 0x3B04      LDR      R3,[R0], #+4
		   \        0xA   0x4718             BX       R3				
			   \                                 In section .text, align 2, keep-with-next
		     37          FillParam(short,3)
		   \                     FillParam3: (+1)
		   \        0x0   0x68D3             LDR      R3,[R2, #+12]
		   \        0x2   0xF821 0x3B02      STRH     R3,[R1], #+2
		   \        0x6   0xF850 0x3B04      LDR      R3,[R0], #+4
		   \        0xA   0x4718             BX       R3
		     38          				
			   \                                 In section .text, align 2, keep-with-next
		     39          void FillParamEnd(void *functab, void *d, long *s)
		     40          {
		     41          }
		   \                     FillParamEnd: (+1)
		   \        0x0   0x4770             BX       LR               ;; return
		     42          				
			   \                                 In section .data, align 4
		     43          FillFunc FillerTask[]=
		   \                     FillerTask:
		   \        0x0   0x....'....        DC32 FillParam0, FillParam1, FillParam2, FillParam3, FillParamEnd
		   \              0x....'....  
		   \              0x....'....  
		   \              0x....'....  
		   \              0x....'....  
		     44          {
		     45            FillParam0,FillParam1,FillParam2,FillParam3,
		     46            FillParamEnd
		     47          };
		     48          				
			   \                                 In section .text, align 2, keep-with-next
		     49          void FillParamBegin(void *d, long *s)
		     50          {
		   \                     FillParamBegin: (+1)
		   \        0x0   0x460A             MOV      R2,R1
		     51            FillFunc *functab=FillerTask;
		     52            FillFunc nf=*functab++;
		     53            nf((void*)functab,d,s);
		   \        0x2   0x....             LDR.N    R3,??DataTable1_1
		   \        0x4   0x4601             MOV      R1,R0
		   \        0x6   0x1D18             ADDS     R0,R3,#+4
		   \        0x8   0x681B             LDR      R3,[R3, #+0]
		   \        0xA   0x4718             BX       R3
		     54          }
		 				
			

Естественно, FillerTask можно сгенерить с нужной Вам периодичностью из редко изменяемой маски.

 

Ну и, конечно, возможна вообще циничная генерация самомодифицирующегося кода из команд LDR R2,[R0,#+disp] и STR(H) R2,[R1],#4(2). Его генерация отлично на Си пишется. И как бы не смешно это звучало, но это будет самый оптимальный вариант (ну, правда, максимум может понадобится 64*6+2=386 байт ОЗУ для хранения генерируемого кода.

Share this post


Link to post
Share on other sites

Кстати, господа, пардон во втором примере за безумные приведения к FiiFunc*, но я просто не соображу, можно ли описать тип указателя на функцию, в которой один из параметров - указатель на такую функцию.

Share this post


Link to post
Share on other sites

Продолжаем разговор ;)

11 hours ago, jcxz said:

Есть массив из 512-и 16-битных беззнаковых чисел. Нужно найти максимальное число в этом массиве.

Вот тут я бы пересмотрел вообще саму идеологию. Все эти прямые поиски - они все равно O(N), хоть на асме, хоть на Си. Как данные в массив попадают? Может быть их имеет смысл как-то предсортировать, если они попадают в массив малыми порциями, и этим вообще глобально улучшить ситуацию?

Share this post


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

Вот тут я бы пересмотрел вообще саму идеологию. Все эти прямые поиски - они все равно O(N), хоть на асме, хоть на Си. Как данные в массив попадают? Может быть их имеет смысл как-то предсортировать, если они попадают в массив малыми порциями, и этим вообще глобально улучшить ситуацию?

Как конкретно - не знаю. Эту задачу тут не так давно кто-то озвучивал.

Попадать могут например из АЦП->DMA и не малой порцией, а сразу всем блоком = 512 сэмплов. И как в этом случае обойтись без асма?

10 часов назад, Rst7 сказал:

Решение в лоб:

Решение "в лоб" очень неоптимальным получается.

 

Цитата

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

Через LDM невозможно, так как переменные var0, ... раскиданы в памяти произвольным образом. varX - это условно так обозначено, чтобы описать задачу. А в общем случае - это могут быть и отдельные члены разных классов или структур.

 

Цитата

Но возможен другой вариант. Допустим, что из всего списка в реальности надо вообще всего пару-тройку параметров генерить.

Да, в реале так может быть. Даже в bm может быть установлен всего один бит==1. Даже - собственно количество единичных битов там ограничено неким небольшим числом. И обычно их мало единиц там.

 

Цитата

Ну и, конечно, возможна вообще циничная генерация самомодифицирующегося кода из команд LDR R2,[R0,#+disp] и STR(H) R2,[R1],#4(2). Его генерация отлично на Си пишется. И как бы не смешно это звучало, но это будет самый оптимальный вариант

Ну вот именно так я и решил эту задачу! :wink:  Самомодифицирующимся кодом. Получилось выполнение такого кода значительно быстрее чем классического со множеством условных веток.

Не важно на чём он генерится, но результирующая функция будет на асме. И без знания ассемблера её не напишешь. О чём и идёт разговор в этом треде.

 

Цитата

(ну, правда, максимум может понадобится 64*6+2=386 байт ОЗУ для хранения генерируемого кода.

ОЗУ там нужно не только для хранения кода, но и для хранения таблицы адресов переменных. Функция генерящая код, генерит и таблицу адресов переменных. И выглядит это не совсем так как Вы написали, а как три команды:

LDR Rx, [PC, #...]         ;загрузка адреса переменной
LDR[H] Rx, [Rx]            ;загрузка значения переменной
STR[H] Rx, [Ry], #4 или #2 ;запись значения

Примерно так. С вычислением смещения от PC до нужного индекса таблицы адресов.

 

Share this post


Link to post
Share on other sites
10 hours ago, Rst7 said:

Ну и, конечно, возможна вообще циничная генерация самомодифицирующегося кода из команд LDR R2,[R0,#+disp] и STR(H) R2,[R1],#4(2).

Склоняю голову... ибо ничего не понимаю((( Если можно, поясните "на пальцах".

Share this post


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

Кстати, господа, пардон во втором примере за безумные приведения к FiiFunc*, но я просто не соображу, можно ли описать тип указателя на функцию, в которой один из параметров - указатель на такую функцию.

Вариант в лоб с кучей функций вызываемых через указатели - это пожалуй самый тормозной будет. Из-за огромного количества переходов. Классическое ветвление на операторах условного выполнения - и то гораздо лучше. Даже не рассматривал такой вариант.

3 минуты назад, haker_fox сказал:

Склоняю голову... ибо ничего не понимаю((( Если можно, поясните "на пальцах".

Я про эту задачу тут уже как-то писал. Тема называлось по-моему как-то "Полиморфный код" что-ли.

Share this post


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

Вариант в лоб с кучей функций вызываемых через указатели - это пожалуй самый тормозной будет.

Нет, если установленных в 1 бит немного. Куда быстрее, чем куча if'ов.

1 hour ago, jcxz said:

Через LDM невозможно, так как переменные var0, ... раскиданы в памяти произвольным образом. varX - это условно так обозначено, чтобы описать задачу. А в общем случае - это могут быть и отдельные члены разных классов или структур.

Простите, а кто вообще этот протокол обмена придумал? Зачем такие безумные сложности?

Share this post


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

Склоняю голову... ибо ничего не понимаю((( Если можно, поясните "на пальцах".

Как было отмечено, не так давно все это оговаривалось:this:

 

12 часов назад, Rst7 сказал:

Все эти прямые поиски - они все равно O(N), хоть на асме, хоть на Си.

В Cortex-M4 есть инструкции, которые могут за один проход сравнить соответствующие полуслова в двух регистрах или группу по 4 байта в двух регистрах:smile:

Хотя это больше к вопросу о равномерности времени выполнения функции поиска, (детерминированности, что ли)...

Share this post


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

В Cortex-M4 есть инструкции, которые могут за один проход сравнить соответствующие полуслова в двух регистрах или группу по 4 байта в двух регистрах:smile:

Хотя это больше к вопросу о равномерности времени выполнения функции поиска, (детерминированности, что ли)...

Это же и есть O(N). Т.е. время будет пропорционально количеству отсчетов. Коэффициент пропорциональности зависит от оптимизации в том числе, но куда полезнее подумать о возможности переделать алгоритм так, чтобы он превратился, например, в O(log2(N)). Думать надо вот в какую сторону - поиск максимума в отсортированном массиве - O(1). Можно считать - бесплатно. Прямой поиск - O(N). Возможно есть какие-то априорные знания о массиве, какие-то способы предсортировки данных, etc

Просто может так оказаться, что завтра N будет равно не 512, а 4096, и никакой прямой поиск, написанный на ассемблере, не спасет.

Share this post


Link to post
Share on other sites
16 hours ago, jcxz said:

Если бы Вы хотя-бы что-то подтвердили фактами, т.е. - кодом, то Ваши слова имели бы какой-то вес. На представленные 3 примера Вы ничего не смогли предоставить, никакого фактического доказательства кроме пустых теоретизирований. Из чего делаю вывод, что в теме Вы не разбираетесь.  

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

16 hours ago, jcxz said:

PS: PUSH/POP каких-то регистров вообще не понимаю - к чему тут?

PUSH {R4 - R7, LR}

POP {R4 - R7, PC}

 

За сим беседы с вами прекращаю, за бесперспективностью - код я вам не приведу (могу от x86, но он вас не устроит), а слова вас не убеждают :(

Да и Факты у вас весьма оригинальные - интересен не АСМ код, а время его выполнения, и не сам по себе, а в сравнении с С версией. Этого вы не привели, из чего я делаю вывод, что фактов у вас тоже нет, как это не парадоксально

Share this post


Link to post
Share on other sites

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

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

ИМХО, невозможно говорить что есть правильно, а что нет. Кому-то деньги позволяют при узком горле в производительности взять и уйти на другой камень.

А кто-то должен из 8-битного МК вытянуть все возможное, потому что стоит копье или еще какая причина (а-ля исторически юзает компания допотопный AVR, и грозное консервативное начальство не сломить модными 32-битными архитектурами).

 

Да в чем вообще проблема? Не было необходимости лезть в листинг? Да не лезь - это же замечательно!

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

Сентенции вслух без конкретного адресата, шоб ненароком не обидеть никого:spiteful:

Share this post


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

Простите, а кто вообще этот протокол обмена придумал? Зачем такие безумные сложности?

А где здесь протокол?

А придумала это жизнь. Нужно было сделать осциллографирование (в реальном времени) содержимого некоторых переменных в ПО. Переменные разбросаны по всей ОЗУ. Есть конечный список переменных (всего <=64шт), одновременно может быть выбрано не более чем N (зависящее от размера выходного пакета). И эта функция  частотой сэмплирования (const) собирает содержимое переменных в единый кадр. Который потом улетает в интерфейс связи.

А как ещё такое можно оптимально реализовать? Предложения? Загрузка CPU этим кодом должна быть минимальной.

2 часа назад, Rst7 сказал:

Думать надо вот в какую сторону - поиск максимума в отсортированном массиве - O(1). Можно считать - бесплатно. Прямой поиск - O(N). Возможно есть какие-то априорные знания о массиве, какие-то способы предсортировки данных, etc

Я Вам уже приводил пример - Вы так ничего и не ответили на него: Например - данные поступают от АЦП. И как заставить АЦП присылать уже отсортированные данные, расскажите?

 

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

PUSH {R4 - R7, LR}

POP {R4 - R7, PC}

И...? К чему это? Как это относится к теме???

Цитата

За сим беседы с вами прекращаю, за бесперспективностью - код я вам не приведу (могу от x86, но он вас не устроит), а слова вас не убеждают :(

Пустые слова - не убеждают.

Цитата

Да и Факты у вас весьма оригинальные - интересен не АСМ код, а время его выполнения, и не сам по себе, а в сравнении с С версией. Этого вы не привели

Что я не привёл?? :wacko2:  Поднимите глаза выше. Я приводил примеры кода на ассемблере, спрашивал как это сделать на си, чтобы после компиляции получалось хотя-бы не хуже - Вы слились, молча. Приводил код на си, спрашивал как получить оптимальное время выполнения того кода, не зная ассемблера - тоже слились.

Если не разбираетесь в теме, зачем тогда вообще писать? Вот этого не понимаю. Пустыми словами тут никого не убедишь.  :unknw:

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