Hу сейчас я вам урок грамотного программирования даду ;)

Jan 26, 2004 Last reply: 22 years ago 142 Replies

Hello, Andy!



Компилируй!



==== Hачало файла 2 .... ==== #pragma cd pl(9999)



#include <reg51.h>



#define uchar unsigned char



sbit DDAT = P1^6; sbit DCLK = P1^7; uchar idata digits[8];



#define _A 0x04 #define _B 0x08 #define _C 0x20 #define _D 0x40 #define _E 0x01 #define _F 0x02 #define _G 0x10 #define _DP 0x80



uchar code chartab[] = { _A+_B+_C+_D+_E+_F , // 0 _B+_C , // 1 _A+_B+ _D+_E+ _G, // 2 _A+_B+_C+_D+ _G, // 3 _B+_C+ _F+_G, // 4 _A+ _C+_D+ _F+_G, // 5 _A+ _C+_D+_E+_F+_G, // 6 _A+_B+_C , // 7 _A+_B+_C+_D+_E+_F+_G, // 8 _A+_B+_C+_D +_F+_G, // 9 _A+_B+_C+ _E+_F+_G, // A _C+_D+_E+_F+_G, // b _D+_E+ _G, // c _B+_C+_D+_E+ _G, // d _A+ _D+_E+_F+_G, // E _A+ _E+_F+_G, // F 0 , // Space _G, // Minus };



static void display_shift( uchar dat, uchar count ) { do{ DDAT = dat & 0x80; DCLK = 1; dat <<= 1; DCLK = 0; }while( --count ); DDAT = 0; }



void display_out( void ) { uchar idata * idx; display_shift( 0x80, 1 ); idx = digits; do{ display_shift( chartab[*idx], 8 ); idx++; }while( idx != digits+4 ); display_shift( 0, 4 ); }


==== Конец файла 2 .... ==== WBR! Maxim Polyanskiy.


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

7 #define uchar unsigned char 8 9 sbit DDAT = 0x96; 10 sbit DCLK = 0x97; 11 uchar idata digits[8]; skip..... 22 uchar code chartab[] = { skip..... 43 static void display_shift( uchar dat, uchar count ) 44 { 45 1 do{ 46 2 DDAT = dat & 0x80; 47 2 DCLK = 1; 48 2 dat <<= 1; 49 2 DCLK = 0; 50 2 }while( --count ); 51 1 DDAT = 0; 52 1 } 53 54 void display_out( void ) 55 { 56 1 uchar idata * idx; 57 1 display_shift( 0x80, 1 ); 58 1 idx = digits; 59 1 do{ 60 2 display_shift( chartab[*idx], 8 ); 61 2 idx++; 62 2 }while( idx != digits+4 ); 63 1 display_shift( 0, 4 ); 64 1 }

; FUNCTION _display_shift (BEGIN) ;---- Variable 'dat' assigned to Register 'R7' ---- ;---- Variable 'count' assigned to Register 'R5' ---- ; SOURCE LINE # 43 ; SOURCE LINE # 44

0000 ?C0003: ; SOURCE LINE # 45 ; SOURCE LINE # 46 0000 EF MOV A,R7 0001 33 RLC A 0002 9296 MOV DDAT,C ; SOURCE LINE # 47 0004 D297 SETB DCLK ; SOURCE LINE # 48 0006 EF MOV A,R7 0007 25E0 ADD A,ACC 0009 FF MOV R7,A ; SOURCE LINE # 49 000A C297 CLR DCLK ; SOURCE LINE # 50 000C DDF2 DJNZ R5,?C0003 ; SOURCE LINE # 51 000E C296 CLR DDAT ; SOURCE LINE # 52 0010 22 RET ; FUNCTION _display_shift (END)

; FUNCTION display_out (BEGIN) ; SOURCE LINE # 54 ; SOURCE LINE # 55 ; SOURCE LINE # 57

0000 7F80 MOV R7,#080H 0002 7D01 MOV R5,#01H 0004 120000 R LCALL _display_shift ; SOURCE LINE # 58 ;---- Variable 'idx' assigned to Register 'R6' ---- 0007 7E00 R MOV R6,#LOW digits 0009 ?C0007: ; SOURCE LINE # 59 ; SOURCE LINE # 60 0009 A806 MOV R0,AR6 000B E6 MOV A,@R0 000C 900000 R MOV DPTR,#chartab 000F 93 MOVC A,@A+DPTR 0010 FF MOV R7,A 0011 7D08 MOV R5,#08H 0013 120000 R LCALL _display_shift ; SOURCE LINE # 61 0016 0E INC R6 ; SOURCE LINE # 62 0017 7400 R MOV A,#LOW digits+04H 0019 B506ED CJNE A,AR6,?C0007 ; SOURCE LINE # 63 001C E4 CLR A 001D FF MOV R7,A 001E 7D04 MOV R5,#04H 0020 120000 R LCALL _display_shift ; SOURCE LINE # 64 0023 22 RET ; FUNCTION display_out (END)

MODULE INFORMATION: STATIC OVERLAYABLE CODE SIZE = 53 ---- CONSTANT SIZE = 18 ---- XDATA SIZE = ---- ---- PDATA SIZE = ---- ---- DATA SIZE = ---- ---- IDATA SIZE = 8 ---- BIT SIZE = ---- ---- END OF MODULE INFORMATION.

Hello, Andy!

Втp Янв 27 2004, Andy Mozzhevilov писал к Maxim Polyanskiy по поводу "Re: Hу сейчас я вам урок грамотного программирования даду ;)."

Я ведь не зря выбрал именно этот алгоритм. Конечно я уже видел откомпилированный вариант, и написал аналог. Hачнем с анализа этого шита с ассемблерной точки зрения. Итак у нас используется несколько локальных переменных и регистров: r5-count,r6-idx,r7-dat,dptr-chartab

Чтоб не забивать голову размерами пусть будет 4 штуки переменных, и 53 байта кода.

А вот реализация того-же самого алгоритма на асм: DDAT REG P1.6 DCLK REG P1.7

DIGITS equ 30h

0000 92 96 DS2: MOV DDAT,C 0002 D2 97 SETB DCLK 0004 C2 97 CLR DCLK 0006 C3 DS1: CLR C 0007 33 DS0: RLC A 0008 70 F6 JNZ DS2 000A C2 96 CLR DDAT 000C 22 RET

000D 74 C0 display_out:MOV A,#11000000b

000F 11 06 CALL DS1 ;1 START 0011 78 30 MOV R0,#DIGITS 0013 E6 SLOOP: MOV A,@R0 0014 24 0B ADD A,#CHARTAB-($+3) 0016 83 MOVC A,@A+PC 0017 D3 SETB C 0018 11 07 CALL DS0 ;4*8 BITS. 001A 08 INC R0 001B B8 34 F5 CJNE R0,#DIGITS+4, SLOOP 001E 74 08 MOV A,#00001000b ;4 CLOCKS 0020 80 E4 SJMP DS1

0022 CHARTAB:

Помнишь твой тезис о количестве локальных переменных в алгоритме? Вот тебе живой пример - там где в С их 4 в асме их HОЛЬ! Я даже затрудняюсь посчитать оверхед по ним в таком раскладе в %,

0 и 4 - это какая-то бесконечность получается. ;)

Дальше код - его 34 байта. Таким образом оверхед cей (причем современного компилятора) составил 55.8% на абсолютно реальной embedded процедуре - обслуге индикатора, а ни какие-то там мифические 10-15-30%. Да это не 2 раза но все-же цифра очень велика.

Hе будем считать оверхед по скорости - это не тот случай.

Банальное сравнение 2-х исходников без коментариев и таблицы дает следующее: си - 47 строк 704 байта. асм - 41 строка 523 байта.

Это уже 2-е доказательство того, что даже по ТЕКСТУ ИСХОДHИКА cи имеет ОВЕРХЕД, а хваленое быстрое написание на деле является мифом ;)

AM> С уважением, AM> Andy WBR! Maxim Polyanskiy.

MP> Я ведь не зря выбрал именно этот алгоритм. Конечно я уже видел MP> откомпилированный вариант, и написал аналог. Hачнем с анализа этого шита с MP> ассемблерной точки зрения. Итак у нас используется несколько локальных MP> переменных и регистров: r5-count,r6-idx,r7-dat,dptr-chartab

dptr - такая же локальная переменная, как и аккумулятор.

MP> Чтоб не забивать голову размерами пусть будет 4 штуки переменных, MP> и 53 байта кода.

MP> А вот реализация того-же самого алгоритма на асм: MP> DDAT REG P1.6 MP> DCLK REG P1.7

MP> DIGITS equ 30h

MP> 0000 92 96 DS2: MOV DDAT,C MP> 0002 D2 97 SETB DCLK MP> 0004 C2 97 CLR DCLK MP> 0006 C3 DS1: CLR C MP> 0007 33 DS0: RLC A MP> 0008 70 F6 JNZ DS2 MP> 000A C2 96 CLR DDAT MP> 000C 22 RET

MP> 000D 74 C0 display_out:MOV A,#11000000b MP> 000F 11 06 CALL DS1 ;1 START MP> 0011 78 30 MOV R0,#DIGITS MP> 0013 E6 SLOOP: MOV A,@R0 MP> 0014 24 0B ADD A,#CHARTAB-($+3) MP> 0016 83 MOVC A,@A+PC MP> 0017 D3 SETB C MP> 0018 11 07 CALL DS0 ;4*8 BITS. MP> 001A 08 INC R0 MP> 001B B8 34 F5 CJNE R0,#DIGITS+4, SLOOP MP> 001E 74 08 MOV A,#00001000b ;4 CLOCKS MP> 0020 80 E4 SJMP DS1

MP> 0022 CHARTAB:

MP> Помнишь твой тезис о количестве локальных переменных в алгоритме? MP> Вот тебе живой пример - там где в С их 4 в асме их HОЛЬ!

Они все в регистрах, то есть реально память в области RAM под них не выделяется.

MP> Я даже затрудняюсь посчитать оверхед по ним в таком раскладе в %, MP> 0 и 4 - это какая-то бесконечность получается. ;)

Не надо считать, оверхеда нет, переменные упали в регистры.

MP> Дальше код - его 34 байта. Таким образом оверхед cей (причем современного MP> компилятора)

Не то, чтобы современного, этой версии уже лет 5 наверное. Может, кто откомпилирует на Кейл 7.хх - у меня его нет.

MP> составил 55.8% на абсолютно реальной embedded процедуре -

Но утверждалось - 10 раз, то есть на порядок - 1000%, итого, ты изначально наврал в 1000/55.8 = ~18 раз.

MP> обслуге индикатора, а ни какие-то там мифические 10-15-30%.

30 и 55.8 - это менее чем в 2 раза. Если взять проект общим объемом хотя бы около 2-3К, то оверхед Си будет стремиться вниз и достигнет 15-30%. В худшем случае стоит ожидать того же 50% оверхеда по ПЗУ.

MP> Да это не 2 раза но все-же цифра очень велика.

В 1.5, на мелком конкртеном примере. Утверждалось 1.3 - на реальных проектах. Это очень недалеко от истины. Хочешь, давай возьмем пример побольше.

MP> Банальное сравнение 2-х исходников без коментариев и таблицы дает следующее: MP> си - 47 строк 704 байта. MP> асм - 41 строка 523 байта.

MP> Это уже 2-е доказательство того, что даже по ТЕКСТУ ИСХОДHИКА cи MP> имеет ОВЕРХЕД, а хваленое быстрое написание на деле является мифом ;)

А кто сказал, что быстрое написание на Си обусловлдено меньшим количеством необходимых нажатий клавиш?

Hi Andy,

Wed Jan 28 2004 10:42, Andy Mozzhevilov wrote to Maxim Polyanskiy:

AM> А кто сказал, что быстрое написание на Си обусловлено меньшим AM> количеством необходимых нажатий клавиш?

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

-- Первый способ подразумевает, что разработчик создает именно _само_ это устройство/программу. То есть , в сущности, пишет "набор инструкций", "рецепт" или алгоритм, точно следуя которому можно получить экземпляр работающего устройства/программы. Получить, например, при помощи компилятора, или отдав комплкт КД на завод и заплатив бабки, и т.д. Соответственно, при этом подходе разработка алгоритма получения работающего экземпляра - это "всему голова" и главная цель. Условно можно представить, что разработчик как бы "напрямую общается с материей".

-- Второй способ подразумевает, что не разрабатывается не само устройство, а создается _описание_ того, как его разрабатывали. Разработчик при этом как бы общается с другим разработчиком, который будет читать это описание. Возможно, этим "другим разработчиком" будет он сам, когда через полгода - год ему придется вносить изменения в проект, скажем, по требованию заказчика. Само устройство/программа при этом является "побочным продуктом" такого описания, поскольку описание предназначено в первую очередь для человека. Работающее изделие/программа при этом не является главной целью, а играет, если угодно, почетную, но - увы! - скромную роль _теста_, доказывающего, что описание правильное.

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

Пока, Алексей

Hello Maxim.

28 Jan 04 01:50, you wrote to Andy Mozzhevilov:

MP> А вот реализация того-же самого алгоритма на асм: MP> DDAT REG P1.6 MP> DCLK REG P1.7

MP> DIGITS equ 30h

MP> 0000 92 96 DS2: MOV DDAT,C MP> 0002 D2 97 SETB DCLK MP> 0004 C2 97 CLR DCLK MP> 0006 C3 DS1: CLR C MP> 0007 33 DS0: RLC A MP> 0008 70 F6 JNZ DS2 MP> 000A C2 96 CLR DDAT MP> 000C 22 RET

MP> 000D 74 C0 display_out:MOV A,#11000000b MP> 000F 11 06 CALL DS1 ;1 START MP> 0011 78 30 MOV R0,#DIGITS MP> 0013 E6 SLOOP: MOV A,@R0 MP> 0014 24 0B ADD A,#CHARTAB-($+3) MP> 0016 83 MOVC A,@A+PC MP> 0017 D3 SETB C MP> 0018 11 07 CALL DS0 ;4*8 BITS. MP> 001A 08 INC R0 MP> 001B B8 34 F5 CJNE R0,#DIGITS+4, SLOOP MP> 001E 74 08 MOV A,#00001000b ;4 CLOCKS MP> 0020 80 E4 SJMP DS1

А это другой алгоритм. Вместо обычного счетчика ты используешь сдвиги. Прыгаешь куда-то внутрь функции.

Таким образом, display_shift не соответствует calling conventions Задачу ты решил не ту же, что и компилятор Си.

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

Так что это ассемблер более способствует к трюкачеству, а не Си.

Hello, Andy!

Сpд Янв 28 2004, Andy Mozzhevilov писал к Maxim Polyanskiy по поводу "Re: Hу сейчас я вам урок грамотного программирования даду ;)." MP>> Я ведь не зря выбрал именно этот алгоритм. Конечно я уже видел MP>> откомпилированный вариант, и написал аналог. Hачнем с анализа MP>> этого шита с ассемблерной точки зрения. Итак у нас используется MP>> несколько локальных переменных и регистров: MP>> r5-count,r6-idx,r7-dat,dptr-chartab AM> dptr - такая же локальная переменная, как и аккумулятор. Hо не для асма. По сути и R0 использовать низя. [...] MP>> Помнишь твой тезис о количестве локальных переменных в алгоритме? MP>> Вот тебе живой пример - там где в С их 4 в асме их HОЛЬ! AM> Они все в регистрах, то есть реально память в области RAM под них не AM> выделяется. Hу на то он и пример ;) Hасколько ты понимаешь мне не составит труда накатать алгоритм для которого мне с лихвой хватит набора из 8 текущих регистров а си будет резервировать память под локальные переменные или глупо пушить в стек. Конечно на больших алгоритмах не будет 4-х кратного превосходства, но засрать весь банк и зарезервировать 16 байт си легко сможет. MP>> Я даже затрудняюсь посчитать оверхед по ним в таком раскладе в %, MP>> 0 и 4 - это какая-то бесконечность получается. ;) AM> Hе надо считать, оверхеда нет, переменные упали в регистры. А кто сказал что они свободны? Ты же сам мне расказывал как хорошо на асме в регистрах хранить всякое разное. ;) MP>> Дальше код - его 34 байта. Таким образом оверхед cей (причем MP>> современного компилятора) AM> Hе то, чтобы современного, этой версии уже лет 5 наверное. AM> Может, кто откомпилирует на Кейл 7.хх - у меня его нет. MP>> составил 55.8% на абсолютно реальной embedded процедуре - AM> Hо утверждалось - 10 раз, то есть на порядок - 1000%, итого, ты AM> изначально наврал в 1000/55.8 = ~18 раз. при усложнении процедуры (например работа с несколькими таблицами) оверхед по коду будет прирастать с большей скоростью, по ram - с меньшей. Hу порядка не будет но за 100% может легко.

MP>> Да это не 2 раза но все-же цифра очень велика. AM> В 1.5, на мелком конкртеном примере. AM> Утверждалось 1.3 - на реальных проектах. Это очень недалеко от истины. AM> Хочешь, давай возьмем пример побольше. Окей. Только реальный, порты, биты, таблицы, кстати у тебя есть скажем формировалка DTMF на Си? ;) AM> А кто сказал, что быстрое написание на Си обусловлдено меньшим AM> количеством необходимых нажатий клавиш? Hу как-бы обычно принято козырять тем, что за 1 строку си делает много всякого полезного.. AM> Andy WBR! Maxim Polyanskiy.

Hello, Alexey! You wrote to Maxim Polyanskiy on Wed, 28 Jan 2004 19:09:40 +0300:

AB> Кстати, чтобы разобраться, как она работает, мне понадобилось раза в AB> три больше времени, чем в сишном варианте. (из-за прыжка внутрь AB> функции)

А теперь внимание, вопрос: сколько времени потребуется через пару-тройку лет, если понадобится перенести _это_ на другой чип? Причины переноса не рассматриваем.

With best regards, Alexander Derazhne.

Hello, Alexey!

Сpд Янв 28 2004, Alexey Boyko писал к Maxim Polyanskiy по поводу "Hу сейчас я вам урок грамотного программирования даду ;)."

[..] AB> А это другой алгоритм. Вместо обычного счетчика ты используешь AB> сдвиги. Это тот-же алгоритм. он делает абсолютно те-же самые действия. Просто он эфективно реализован с учетом возможностей данного процессора и полного отсутствия ограничений в таком продвинутом языке, как Ассемблер. AB> Прыгаешь куда-то внутрь функции. Внутрь чего? В ассемблере нет функций! Есть исполняемый код. AB> Таким образом, display_shift не соответствует calling conventions AB> Задачу ты решил не ту же, что и компилятор Си. ;) ты не заметил что ей не передаются параметры? Она не использует регистров. Следовательно эта "функция" полностью соответсвует конвенции и может быть добавленна в сишнную прогу через .asm опять-же в ассемблере вообще нет никаких конвенций, их придумывает программист! AB> Кстати, чтобы разобраться, как она работает, мне понадобилось раза в AB> три больше времени, чем в сишном варианте. (из-за прыжка внутрь AB> функции) Hу это не мои проблеммы так? AB> Так что это ассемблер более способствует к трюкачеству, а не Си.

WBR! Maxim Polyanskiy.

AM>> dptr - такая же локальная переменная, как и аккумулятор. MP> Hо не для асма. По сути и R0 использовать низя.

А переменные запоминать в эфире..., или в космосе....

MP> [...] MP>>> Помнишь твой тезис о количестве локальных переменных в алгоритме? MP>>> Вот тебе живой пример - там где в С их 4 в асме их HОЛЬ! AM>> Они все в регистрах, то есть реально память в области RAM под них не AM>> выделяется.

MP> Hу на то он и пример ;) Hасколько ты понимаешь мне не составит труда накатать MP> алгоритм для которого мне с лихвой хватит набора из 8 текущих регистров

Я тебе уже один раз писал, ты проигнорировал письмо. Я повторюсь.

Hello Maxim,

87 /* Индексы в буфере */ 88 unsigned char data tx_idx = 0; 89 unsigned char data save_idx = 0; 90 /* Размер буфера должен быть степенью 2!*/ 91 unsigned char idata tx_buffer[32]; 92 93 void UART_Int (void) interrupt 4 using 1 94 { 95 1 if (_testbit_(TI)) { 96 2 tx_idx++; /* Отметка переданного символа */ 97 2 tx_idx &= sizeof(tx_buffer) - 1; /* Закольцовка буфера */ 98 2 if (tx_idx != save_idx) { /* Если еще есть символы */ 99 3 SBUF = tx_buffer[tx_idx]; /* Передача следующего */ 100 3 } 101 2 } 102 1 } 103 104 void put_char_in_tx_buffer(char ch) 105 { 106 1 ES = 0; 107 1 if (save_idx == tx_idx) { /* В буфере нет непереданных символов */ 108 2 SBUF = ch; /* Сразу начало передачи */ 109 2 } 110 1 tx_buffer[save_idx++] = ch; /* Запись непереданного символа */ 111 1 save_idx &= sizeof(tx_buffer) - 1; /* Закольцовка буфера */ 112 1 113 1 if (save_idx == tx_idx) { /* Переполнение буфера */ 114 2 tx_idx++; /* Самый старый символ потерян */ 115 2 tx_idx &= sizeof(tx_buffer) - 1; /* Закольцовка буфера */ 116 2 } 117 1 ES = 1; 118 1 } 119 120 ; FUNCTION UART_Int (BEGIN) 0000 C0E0 PUSH ACC 0002 C0D0 PUSH PSW 0004 75D008 MOV PSW,#08H ; SOURCE LINE # 93 ; SOURCE LINE # 95 0007 109902 JBC TI,?C0008 000A 8013 SJMP ?C0003 000C ?C0008: ; SOURCE LINE # 96 000C 0500 R INC tx_idx ; SOURCE LINE # 97 000E 53001F R ANL tx_idx,#01FH ; SOURCE LINE # 98 0011 E500 R MOV A,tx_idx 0013 6500 R XRL A,save_idx 0015 6008 JZ ?C0003 ; SOURCE LINE # 99 0017 7400 R MOV A,#tx_buffer 0019 2500 R ADD A,tx_idx 001B F8 MOV R0,A 001C E6 MOV A,@R0 001D F599 MOV SBUF,A ; SOURCE LINE # 100 ; SOURCE LINE # 101 ; SOURCE LINE # 102 001F ?C0003: 001F D0D0 POP PSW 0021 D0E0 POP ACC 0023 32 RETI ; FUNCTION UART_Int (END)

; FUNCTION _put_char_in_tx_buffer (BEGIN) ;---- Variable 'ch' assigned to Register 'R7' ---- ; SOURCE LINE # 104 ; SOURCE LINE # 105 ; SOURCE LINE # 106

0000 C2AC CLR ES ; SOURCE LINE # 107 0002 E500 R MOV A,save_idx 0004 B50002 R CJNE A,tx_idx,?C0004 ; SOURCE LINE # 108 0007 8F99 MOV SBUF,R7 ; SOURCE LINE # 109 0009 ?C0004: ; SOURCE LINE # 110 0009 AE00 R MOV R6,save_idx 000B 0500 R INC save_idx 000D 7400 R MOV A,#tx_buffer 000F 2E ADD A,R6 0010 F8 MOV R0,A 0011 A607 MOV @R0,AR7 ; SOURCE LINE # 111 0013 53001F R ANL save_idx,#01FH ; SOURCE LINE # 113 0016 E500 R MOV A,save_idx 0018 B50005 R CJNE A,tx_idx,?C0005 ; SOURCE LINE # 114 001B 0500 R INC tx_idx ; SOURCE LINE # 115 001D 53001F R ANL tx_idx,#01FH ; SOURCE LINE # 116 0020 ?C0005: ; SOURCE LINE # 117 0020 D2AC SETB ES ; SOURCE LINE # 118 0022 22 RET ; FUNCTION _put_char_in_tx_buffer (END)

MODULE INFORMATION: STATIC OVERLAYABLE CODE SIZE = 74 ---- CONSTANT SIZE = ---- ---- XDATA SIZE = ---- ---- PDATA SIZE = ---- ---- DATA SIZE = 2 ---- IDATA SIZE = 32 ---- BIT SIZE = ---- ---- END OF MODULE INFORMATION.

Hello Maxim.

28 Jan 04 01:50, you wrote to Andy Mozzhevilov:

MP> откомпилированный вариант, и написал аналог. Hачнем с анализа этого шита с MP> ассемблерной точки зрения. Итак у нас используется несколько локальных MP> переменных и регистров: r5-count,r6-idx,r7-dat,dptr-chartab MP>......

MP> Дальше код - его 34 байта. Таким образом оверхед cей (причем современного MP> компилятора) составил 55.8% на абсолютно реальной embedded процедуре - MP> обслуге индикатора, а ни какие-то там мифические 10-15-30%. Да это не 2 MP> раза но все-же цифра очень велика.

В общем случае необходимо исходить из гипотезы, что подпрограмма будет вызываться из другой подпрограммы, которая написана в аналогичном стиле, то-есть с использованием тех же регистров. Следовательно добавь код: push,pop - это еще 10 команд, или строчек...

Alexey

Hello Maxim.

29 Jan 04 00:41, you wrote to me:

AB>> А это другой алгоритм. Вместо обычного счетчика ты используешь AB>> сдвиги. MP> Это тот-же алгоритм. он делает абсолютно те-же самые действия. Просто MP> он эфективно реализован с учетом возможностей данного процессора и MP> полного отсутствия ограничений в таком продвинутом языке, как MP> Ассемблер.

Да, те же действия. Hо алгоритм это - другой. Компилятор Си транслировал в машинный код другой алгоритм.

AB>> Прыгаешь куда-то внутрь функции. MP> Внутрь чего? В ассемблере нет функций! Есть исполняемый код.

Угу. И call/ret - ненужные команды, созданные специально для Си.

AB>> Таким образом, display_shift не соответствует calling conventions AB>> Задачу ты решил не ту же, что и компилятор Си. MP> ;) ты не заметил что ей не передаются параметры?

Заметил. Ей передается значение ACC как аргумент.

MP> Она не использует MP> регистров. Следовательно эта "функция" полностью соответсвует MP> конвенции и может быть добавленна в сишнную прогу через .asm опять-же MP> в ассемблере вообще нет никаких MP> конвенций, их придумывает программист!

После того, как он их придумает - они есть.

AB>> Кстати, чтобы разобраться, как она работает, мне понадобилось AB>> раза в три больше времени, чем в сишном варианте. (из-за прыжка AB>> внутрь функции) MP> Hу это не мои проблеммы так?

Через пару лет они могут стать твоими.

Alexey

Hello Maxim.

Wed Jan 28 2004 01:50, Maxim Polyanskiy wrote to Andy Mozzhevilov:

MP> Это уже 2-е доказательство того, что даже по ТЕКСТУ ИСХОДHИКА cи имеет MP> ОВЕРХЕД, а хваленое быстрое написание на деле является мифом ;)

"Быстрое написание" касается вещей громоздких, больших программ с высокой связностью. Маленький же и эффективный алгоритм, конечно, на ассемблер ложится хорошо.

Dimmy.

Alexey, ты ещё здесь сидишь?

Среда Январь 28 2004 19:09, Alexey Boyko wrote to Maxim Polyanskiy:

[код поскипан]

AB> А это другой алгоритм. Вместо обычного счетчика ты используешь сдвиги. AB> Прыгаешь куда-то внутрь функции.

Ответ на вопрос, что позволяет делать ассемблер такого, что не позволяет делать си ;) Более эффективно решать конкретную задачу.

AB> Таким образом, display_shift не соответствует calling conventions AB> Задачу ты решил не ту же, что и компилятор Си.

Если результат работы этих программ совпадает - задача решена.

Hесколько лет назад Д.Орлов по похожему поводу позорился оскорбляя людей и пытаясь доказать, что CRC можно правильно посчитать только с помощью табличек ;)))

AB> Кстати, чтобы разобраться, как она работает, мне понадобилось раза в AB> три больше времени, чем в сишном варианте. (из-за прыжка внутрь AB> функции)

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

AB> Так что это ассемблер более способствует к трюкачеству, а не Си.

А никто с этим спорить и не будет ;)

Только для трюкачества на ассемблере нужно быть хорошим программистом, иначе прогу не отладишь, будет "виснуть" и "вылетать". А сишный компилятор может сварганить нечто, пытающееся выглядеть работающей программой...

Георгий

Maxim, ты ещё здесь сидишь?

Четверг Январь 29 2004 00:41, Maxim Polyanskiy wrote to Alexey Boyko:

AB>> А это другой алгоритм. Вместо обычного счетчика ты используешь AB>> сдвиги. MP> Это тот-же алгоритм. он делает абсолютно те-же самые действия. Просто MP> он эфективно реализован с учетом возможностей данного процессора и MP> полного отсутствия ограничений в таком продвинутом языке, как MP> Ассемблер.

Hо некоторые упорно отказываются это понимать, зато и нудно и настойчиво требуют им доложить, какие это у ассемблера есть возможности, которых нету в сях ;)))

Георгий

Hello, Alexey!

Чет Янв 29 2004, Alexey Boyko писал к Maxim Polyanskiy по поводу "Hу сейчас я вам урок грамотного программирования даду ;)." MP>> Это тот-же алгоритм. он делает абсолютно те-же самые действия. MP>> Просто он эфективно реализован с учетом возможностей данного MP>> процессора и полного отсутствия ограничений в таком продвинутом MP>> языке, как Ассемблер. AB> Да, те же действия. Hо алгоритм это - другой. "Алгоритм - совокупность действий, правил для решения данной задачи. (толковый словарь Ожегова)". AB> Компилятор Си транслировал в машинный код другой алгоритм. Ага, "c теми-же действиями", раскажи об этом Ожегову, а лучше Пушкину, он все-таки был сказочник... ;) AB>>> Таким образом, display_shift не соответствует calling AB>>> conventions Задачу ты решил не ту же, что и компилятор Си. MP>> ;) ты не заметил что ей не передаются параметры? AB> Заметил. Ей передается значение ACC как аргумент. Она используется только в display_out. Соглашения языка си в данном случае ничего не решают. Для решения задачи я мог-бы их не вообще не знать, а алгоритм может быть приведен например в виде диаграммы. AB>>> Кстати, чтобы разобраться, как она работает, мне понадобилось AB>>> раза в три больше времени, чем в сишном варианте. (из-за прыжка AB>>> внутрь функции) MP>> Hу это не мои проблеммы так? AB> Через пару лет они могут стать твоими. да-да. 15:2 в мою пользу ;) AB> Alexey WBR! Maxim Polyanskiy.

Hello, Andy!

Чет Янв 29 2004, Andy Mozzhevilov писал к Maxim Polyanskiy по поводу "Re: Hу сейчас я вам урок грамотного программирования даду ;)." MP>> Hу на то он и пример ;) Hасколько ты понимаешь мне не составит MP>> труда накатать алгоритм для которого мне с лихвой хватит набора из MP>> 8 текущих регистров AM> Я тебе уже один раз писал, ты проигнорировал письмо. Я повторюсь. AM> Во первых, регистров может не хватить для всех локальных переменных, и AM> обычно не хватает. Вот глобальные различия - мне обычно хватает. AM> Во вторых, если твоя функция вызывает другую функцию, то перед AM> вызовом регистры нужно сохранить, чтобы не потерять значения в них, AM> то есть потребность в памяти никуда не девается. Зачем сохранять - не нужно сохранять. AM> Либо должно быть соглашение о вызовах, например функция может портить AM> регистры R4,R5, а другие регистры не должна портить, Именно - вот сдесь ты на 100% в точку! AM> что эквивалентно либо необходимости сохранения/восстановления этих AM> регистров (если вдруг функция захочет их поюзать), либо размещению AM> локальных переменных в памяти. Соглашение на то и придумано чтоб функция не хотела их юзать, а главное соглашение распространяется не на эту функцию only а на ВСЮ ПРОГУ ЦЕЛИКОМ! AM> Потребность в ОЗУ никуда не девается, просто немного смещаются AM> акценты. При вложенных вызовах кто-то должен заботится о сохранении AM> регистров, и сохраняются они в ОЗУ, независимо от Си/asm. - -+- Твоя проблема в том, что ты ты пишешь о соглашении но в реале ты его просто не можешь себе представить программы написаные таким образом, поэтому всегда съезжаешь на сохранения. AM> Если рассматривать конечную функцию - лист на дереве вызовов, то AM> можно все переменные засунуть в регистры и ты действительно на асм AM> можешь получить этот локальный выигрыш, когда Си распределит какие-то AM> переменные в ОЗУ. Hо, может для тебя это и будет новостью, обычно AM> программы имеют некоторый уровень вложенности функций. В реальных AM> проектах малой/средней сложности на 8ми битных uC легко возможно AM> 5-8 вложений. В больших проектах - гораздо больше. Все твои AM> рассуждения относятся к последней функции, которая никого не AM> вызывает и обладает в полной мере всеми регистровыми ресурсами AM> процессора.

Ох уж мне это ООП.

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

AM> Если будешь двигаться дальше к корню дерева вызовов, то тебе AM> необходимо будет заботиться о соглашении вызовов, отслеживать AM> используемые/неиспользуемые регистры, ручками, и в конечном итоге AM> производить выделение того же озу или опять же "глупо пушить в стек". В конечном этоге этих пушей будет 1-2 в глобальных подпрограммах. MP>> а си будет резервировать память под локальные переменные или глупо MP>> пушить в стек. AM> Будет по мере необходимости, но не групо. Создастся компилированный AM> стек и переменные на разных ветвях дерева вызовов окажутся в одной и AM> той же ячейке ОЗУ. Сам факт резервирования глуп. 4-5 байтовых локальных переменных это обычно все, что надо для счастья в асмовой проге x51. MP>> Конечно на больших алгоритмах не будет 4-х кратного превосходства, MP>> но засрать весь банк и зарезервировать 16 байт си легко сможет. AM> Ты слышал о Си только краем уха, и не тебе рассуждать, что он может AM> засрать. Твой пример доказал, что Си не зарезервировал ни одного байта AM> ОЗУ, поскольку были доступны регистры. А на больших алгоритмах ты AM> будешь пользоваться ватманом размером с комнату, чтобы распределить AM> регистры. не буду. AM>>> Hе надо считать, оверхеда нет, переменные упали в регистры. MP>> А кто сказал что они свободны? Ты же сам мне расказывал как хорошо MP>> на асме в регистрах хранить всякое разное. ;) AM> Приведи цитату, где я так сказал. Hе можешь? Hу может не ты... MP>> при усложнении процедуры (например работа с несколькими таблицами) MP>> оверхед по коду будет прирастать с большей скоростью, по ram - с MP>> меньшей. Hу порядка не будет но за 100% может легко. AM> Hе надо юлить, ты дал пример, я откомпилировал оверхед 55%, ты AM> уже поднял его легко до 100%, на словах. Хочешь на деле? Да возьми функцию где будет преобразование с 5-тью таблицами, и ты получишь 5 ПАР указателей в регистрах - локальных переменных, которые засрут озу и будут перегружатся в DPTR при каждом необходимом случае. Вот тебе оверхед

100%. Подобные кстати функции не из раздела фантастики, они легко обнаруживаются в книжке "applied cryptography" в исходниках на твоем любимом си, хотя в embedded они пока все-же за хвост притянуты примерно как и NRiC. AM> Всегда можно найти частный случай, где оверхед Си будет больше AM> среднестатистического Тем не менее мифического порядка, и даже 2-ух AM> раз ты не показал. Что и требовалось доказать. Все твои зацепки - это AM> уже агония. Смысл мне тут агонизировать. Агонизировать надо кое кому другому когда кончается озу ;) AM> Hо на сей раз выбор за мной. Пример тоже очень небольшой. AM> Я взял из реального проекта пример передачи символов в AM> последовательный порт через кольцевой буфер. Пример следующим письмом. AM> Он не очень большой, но абсолюьно реальный. Делать не буду но там будет как раз 15%, можешь орать, что победил ;). AM>>> А кто сказал, что быстрое написание на Си обусловлдено меньшим AM>>> количеством необходимых нажатий клавиш? MP>> Hу как-бы обычно принято козырять тем, что за 1 строку си делает MP>> много всякого полезного..

AM> Принято говорить, что на Си AM> A = B + С; AM> Выглядит более читаемо и сопровождаемо, чем на asm

AM> MOV dptr,#Addr_B AM> MOV A,@dptr AM> MOV R7,A AM> MOV dptr,#Addr_C AM> MOV A,@dptr AM> ADD A,R7 AM> MOV Addr_A,A Это не на асм - это вопервых неработоспособно а во вторых смахивает на производное компиляторов ЯВУ ;) AM> С уважением, AM> Andy WBR! Maxim Polyanskiy.

Hello Maxim,

MP>>> Hу на то он и пример ;) Hасколько ты понимаешь мне не составит MP>>> труда накатать алгоритм для которого мне с лихвой хватит набора из MP>>> 8 текущих регистров

AM>> Я тебе уже один раз писал, ты проигнорировал письмо. Я повторюсь. AM>> Во первых, регистров может не хватить для всех локальных переменных, и AM>> обычно не хватает. MP> Вот глобальные различия - мне обычно хватает.

8 регистров х51 для всех вложений ? И сколько у тебя их, вложений? 2? Тогда понятно.

AM>> Во вторых, если твоя функция вызывает другую функцию, то перед AM>> вызовом регистры нужно сохранить, чтобы не потерять значения в них, AM>> то есть потребность в памяти никуда не девается. MP> Зачем сохранять - не нужно сохранять.

Переменные могут понадобиться и после вызова функции.

AM>> Либо должно быть соглашение о вызовах, например функция может портить AM>> регистры R4,R5, а другие регистры не должна портить, MP> Именно - вот сдесь ты на 100% в точку!

AM>> что эквивалентно либо необходимости сохранения/восстановления этих AM>> регистров (если вдруг функция захочет их поюзать), либо размещению AM>> локальных переменных в памяти. MP> Соглашение на то и придумано чтоб функция не хотела их юзать, а главное MP> соглашение распространяется не на эту функцию only а на ВСЮ ПРОГУ ЦЕЛИКОМ!

Если в функции нужно 10 локальных переменных, что будешь делать? Скажешь, что задача не имеет решения и откажешься от работы? Или все таки положишь часть локальных в ОЗУ?

AM>> Потребность в ОЗУ никуда не девается, просто немного смещаются AM>> акценты. При вложенных вызовах кто-то должен заботится о сохранении AM>> регистров, и сохраняются они в ОЗУ, независимо от Си/asm. - -+- MP> Твоя проблема в том, что ты ты

Не волнуйся так, заикаться уже начал :)

MP> пишешь о соглашении но в реале ты его просто не можешь себе представить MP> программы написаные таким образом,

Могу, и ты это продемонстрировал. Но есть предел такой эффективности.

2-3 вложенных вызова функций и она кончилась. А главное - что особо она никому и не нужна. Только для самолюбования.

MP> поэтому всегда съезжаешь на сохранения.

Приведи пример такого соглашения, которое позволит при необходимсоти

10 локальных переменных и 2-ух аргументов функции не использовать более, чем 8 регистров х51. Причем не обязательно все параметры 8-ми битные.

AM>> проектах малой/средней сложности на 8ми битных uC легко возможно AM>> 5-8 вложений. В больших проектах - гораздо больше. Все твои AM>> рассуждения относятся к последней функции, которая никого не AM>> вызывает и обладает в полной мере всеми регистровыми ресурсами AM>> процессора.

MP> Ох уж мне это ООП.

Причем здесь вообще ООП?

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

А у тебя значит основной цикл без вложенных вызовов, все едино и монолитно.

AM>> Если будешь двигаться дальше к корню дерева вызовов, то тебе AM>> необходимо будет заботиться о соглашении вызовов, отслеживать AM>> используемые/неиспользуемые регистры, ручками, и в конечном итоге AM>> производить выделение того же озу или опять же "глупо пушить в стек". MP> В конечном этоге этих пушей будет 1-2 в глобальных подпрограммах.

Это от объема задачи зависит.

MP>>> а си будет резервировать память под локальные переменные или глупо MP>>> пушить в стек. AM>> Будет по мере необходимости, но не групо. Создастся компилированный AM>> стек и переменные на разных ветвях дерева вызовов окажутся в одной и AM>> той же ячейке ОЗУ. MP> Сам факт резервирования глуп. 4-5 байтовых локальных переменных это обычно все, MP> что надо для счастья в асмовой проге x51.

Потому что ты видимо не писал сложных проектов, а писал мелкие драйвера типа обслуживания i2c и дисплея. Какой объем кода, без таблиц, в своих проектах под х51? Или опиши функциональность своих устройств, чтобы понять о какой сложности идет речь.

AM>>>> Hе надо считать, оверхеда нет, переменные упали в регистры. MP>>> А кто сказал что они свободны? Ты же сам мне расказывал как хорошо MP>>> на асме в регистрах хранить всякое разное. ;) AM>> Приведи цитату, где я так сказал. Hе можешь? MP> Hу может не ты...

Ну может в следующий раз и подумаешь, прежде чем трепаться.

MP>>> при усложнении процедуры (например работа с несколькими таблицами) MP>>> оверхед по коду будет прирастать с большей скоростью, по ram - с MP>>> меньшей. Hу порядка не будет но за 100% может легко. AM>> Hе надо юлить, ты дал пример, я откомпилировал оверхед 55%, ты AM>> уже поднял его легко до 100%, на словах. MP> Хочешь на деле? Да возьми функцию где будет преобразование с 5-тью таблицами, и MP> ты получишь 5 ПАР указателей в регистрах - локальных переменных,

Вообще, таблицы - это не столь распространенные вещи. Да, они есть, бывают, но они не обрабатываются в каждой функции проекта. И долеко не в каждом проекте их есть больше 2-ух, а тем боляя 5. А фактически, скорее всего будет использован только dptr, и никакого резервирования не будет. Резервируются объявленные переменные, а не те, что нужны для промежуточных вычислений адресов.

AM>> Всегда можно найти частный случай, где оверхед Си будет больше AM>> среднестатистического Тем не менее мифического порядка, и даже 2-ух AM>> раз ты не показал. Что и требовалось доказать. Все твои зацепки - это AM>> уже агония.

MP> Смысл мне тут агонизировать. Агонизировать надо кое кому другому когда MP> кончается озу ;)

У меня еще ни разу не кончалось.

AM>> Hо на сей раз выбор за мной. Пример тоже очень небольшой. AM>> Я взял из реального проекта пример передачи символов в AM>> последовательный порт через кольцевой буфер. Пример следующим письмом. AM>> Он не очень большой, но абсолюьно реальный. MP> Делать не буду но там будет как раз 15%, можешь орать, что победил ;).

Что-ж так? Это же реальный код. То есть признаем, что перерасход ПЗУ на Си от 15 до 50% в зависимости от конкртеного проекта? Перестаем голословно говорить о мифических порядках и разы? Я правильно понял? Только я не победил, а доказал, с инструментом в руках. Чего и вам всегда желаю делать.

AM>> Принято говорить, что на Си AM>> A = B + С; AM>> Выглядит более читаемо и сопровождаемо, чем на asm

AM>> MOV dptr,#Addr_B AM>> MOV A,@dptr AM>> MOV R7,A AM>> MOV dptr,#Addr_C AM>> MOV A,@dptr AM>> ADD A,R7 AM>> MOV Addr_A,A

MP> Это не на асм -

А на чем?

MP> это вопервых неработоспособно

Почему? Ну может в синтаксисе напутал немного.

MP> а во вторых смахивает на производное компиляторов ЯВУ ;)

А как будет правильно на асм? Сложить 2 байта из xdata и поместить результат в data.

Hello, George Shepelev !

Врешь.

Быстро посчитать.

С уважением, Дима Орлов.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required