sonycman 0 30 ноября, 2008 Опубликовано 30 ноября, 2008 · Жалоба Но всё же даже у гнуса бывают косяки с указателями. И всего-то с двумя: case USART_SEND_DATA_EX: length = 4 + 4 + 4 + 20; *pb++ = length + 1; *pb++ = command; *pb++ = temperature.cpu; *pb++ = temperature.gpu; *pb++ = temperature.amb; *pb++ = temperature.hdd; *pb++ = fan_speed[FAN_CPU]; *pb++ = fan_speed[FAN_SIDE]; *pb++ = fan_speed[FAN_REAR]; *pb++ = fan_speed[FAN_FRONT]; *pb++ = cpuFan.GetManualStatus(); *pb++ = rearFan.GetManualStatus(); *pb++ = sideFan.GetManualStatus(); *pb++ = frontFan.GetManualStatus(); *pb++ = cpuFan.minRPM; *pb++ = cpuFan.maxRPM; *pb++ = cpuFan.minCELSIUS; *pb++ = cpuFan.maxCELSIUS; *pb++ = cpuFan.stopCELSIUS; *pb++ = rearFan.minRPM; *pb++ = rearFan.maxRPM; *pb++ = rearFan.minCELSIUS; *pb++ = rearFan.maxCELSIUS; *pb++ = rearFan.stopCELSIUS; *pb++ = sideFan.minRPM; *pb++ = sideFan.maxRPM; *pb++ = sideFan.minCELSIUS; *pb++ = sideFan.maxCELSIUS; *pb++ = sideFan.stopCELSIUS; *pb++ = frontFan.minRPM; *pb++ = frontFan.maxRPM; *pb++ = frontFan.minCELSIUS; *pb++ = frontFan.maxCELSIUS; *pb++ = frontFan.stopCELSIUS; код получается сплошняком из LDS/STS, так как адреса переменных (члены классов) статические и гнус таким образом "оптимизирует". После того, как я перевёл приёмник в локальные переменные (пришлось водрузить буффер на стёк), код немного улучшился: case USART_SEND_DATA_EX: length = 4 + 4 + 4 + 20; *pb++ = length + 1; 1320: 81 e2 ldi r24, 0x21; 33 1322: 8b 83 std Y+3, r24; 0x03 *pb++ = command; 1324: 0c 83 std Y+4, r16; 0x04 *pb++ = temperature.cpu; 1326: 80 91 62 01 lds r24, 0x0162 132a: 8d 83 std Y+5, r24; 0x05 *pb++ = temperature.gpu; 132c: 80 91 63 01 lds r24, 0x0163 1330: 8e 83 std Y+6, r24; 0x06 *pb++ = temperature.amb; 1332: 80 91 64 01 lds r24, 0x0164 1336: 8f 83 std Y+7, r24; 0x07 *pb++ = temperature.hdd; 1338: 80 91 65 01 lds r24, 0x0165 133c: 88 87 std Y+8, r24; 0x08 *pb++ = fan_speed[FAN_CPU]; 133e: 80 91 9d 01 lds r24, 0x019D 1342: 89 87 std Y+9, r24; 0x09 *pb++ = fan_speed[FAN_SIDE]; 1344: 80 91 9e 01 lds r24, 0x019E 1348: 8a 87 std Y+10, r24; 0x0a *pb++ = fan_speed[FAN_REAR]; 134a: 80 91 9f 01 lds r24, 0x019F 134e: 8b 87 std Y+11, r24; 0x0b *pb++ = fan_speed[FAN_FRONT]; 1350: 80 91 a0 01 lds r24, 0x01A0 1354: 8c 87 std Y+12, r24; 0x0c *pb++ = cpuFan.GetManualStatus(); 1356: 8f e0 ldi r24, 0x0F; 15 1358: 92 e0 ldi r25, 0x02; 2 135a: 83 d8 rcall .-3834 ; 0x462 <CFanRegulatorBase::GetManualStatus()> 135c: 8d 87 std Y+13, r24; 0x0d *pb++ = rearFan.GetManualStatus(); 135e: 8c e1 ldi r24, 0x1C; 28 1360: 92 e0 ldi r25, 0x02; 2 1362: 7f d8 rcall .-3842 ; 0x462 <CFanRegulatorBase::GetManualStatus()> 1364: 8e 87 std Y+14, r24; 0x0e *pb++ = sideFan.GetManualStatus(); 1366: 89 e2 ldi r24, 0x29; 41 1368: 92 e0 ldi r25, 0x02; 2 136a: 7b d8 rcall .-3850 ; 0x462 <CFanRegulatorBase::GetManualStatus()> 136c: 8f 87 std Y+15, r24; 0x0f *pb++ = frontFan.GetManualStatus(); 136e: 86 e3 ldi r24, 0x36; 54 1370: 92 e0 ldi r25, 0x02; 2 1372: 77 d8 rcall .-3858 ; 0x462 <CFanRegulatorBase::GetManualStatus()> 1374: 88 8b std Y+16, r24; 0x10 *pb++ = cpuFan.minRPM; 1376: 80 91 18 02 lds r24, 0x0218 137a: 89 8b std Y+17, r24; 0x11 *pb++ = cpuFan.maxRPM; 137c: 80 91 17 02 lds r24, 0x0217 1380: 8a 8b std Y+18, r24; 0x12 *pb++ = cpuFan.minCELSIUS; 1382: 80 91 1a 02 lds r24, 0x021A 1386: 8b 8b std Y+19, r24; 0x13 *pb++ = cpuFan.maxCELSIUS; 1388: 80 91 19 02 lds r24, 0x0219 138c: 8c 8b std Y+20, r24; 0x14 *pb++ = cpuFan.stopCELSIUS; 138e: 80 91 1b 02 lds r24, 0x021B 1392: 8d 8b std Y+21, r24; 0x15 *pb++ = rearFan.minRPM; 1394: 80 91 25 02 lds r24, 0x0225 1398: 8e 8b std Y+22, r24; 0x16 *pb++ = rearFan.maxRPM; 139a: 80 91 24 02 lds r24, 0x0224 139e: 8f 8b std Y+23, r24; 0x17 *pb++ = rearFan.minCELSIUS; 13a0: 80 91 27 02 lds r24, 0x0227 13a4: 88 8f std Y+24, r24; 0x18 *pb++ = rearFan.maxCELSIUS; 13a6: 80 91 26 02 lds r24, 0x0226 13aa: 89 8f std Y+25, r24; 0x19 *pb++ = rearFan.stopCELSIUS; 13ac: 80 91 28 02 lds r24, 0x0228 13b0: 8a 8f std Y+26, r24; 0x1a *pb++ = sideFan.minRPM; 13b2: 80 91 32 02 lds r24, 0x0232 13b6: 8b 8f std Y+27, r24; 0x1b *pb++ = sideFan.maxRPM; 13b8: 80 91 31 02 lds r24, 0x0231 13bc: 8c 8f std Y+28, r24; 0x1c *pb++ = sideFan.minCELSIUS; 13be: 80 91 34 02 lds r24, 0x0234 13c2: 8d 8f std Y+29, r24; 0x1d *pb++ = sideFan.maxCELSIUS; 13c4: 80 91 33 02 lds r24, 0x0233 13c8: 8e 8f std Y+30, r24; 0x1e *pb++ = sideFan.stopCELSIUS; 13ca: 80 91 35 02 lds r24, 0x0235 13ce: 8f 8f std Y+31, r24; 0x1f *pb++ = frontFan.minRPM; 13d0: 80 91 3f 02 lds r24, 0x023F 13d4: 88 a3 std Y+32, r24; 0x20 *pb++ = frontFan.maxRPM; 13d6: 80 91 3e 02 lds r24, 0x023E 13da: 89 a3 std Y+33, r24; 0x21 *pb++ = frontFan.minCELSIUS; 13dc: 80 91 41 02 lds r24, 0x0241 13e0: 8a a3 std Y+34, r24; 0x22 *pb++ = frontFan.maxCELSIUS; 13e2: 80 91 40 02 lds r24, 0x0240 13e6: 8b a3 std Y+35, r24; 0x23 *pb++ = frontFan.stopCELSIUS; 13e8: 80 91 42 02 lds r24, 0x0242 13ec: 8c a3 std Y+36, r24; 0x24 13ee: 10 e2 ldi r17, 0x20; 32 13f0: 04 c0 rjmp .+8 ; 0x13fa <usartSendCommand(unsigned char, void const*)+0x1ea> но всё равно не хватило сообразительности задействовать второй указатель. ИАР без проблем юзает здесь два указателя даже со статическим буффером pb... :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 30 ноября, 2008 Опубликовано 30 ноября, 2008 · Жалоба но всё равно не хватило сообразительности задействовать второй указатель. ИАР без проблем юзает здесь два указателя даже со статическим буффером pb... :) http://electronix.ru/forum/index.php?showt...mp;#entry506246 ну сделайте и обращение к структуре не статическим Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 120 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба -Os -fno-ivopts .... Вы не умеете его готовить. Не могли бы вы более развернуто прокомментировать этот эффект? Как видно, отключение одной из оптимизаций привело к существенному улучшению кода. Разве в компиляторе не предусмотрен анализ результатов оптимизации? Т.е. если после какой-либо оптимизации код стал хуже по основоному критерию (-Os), то не выполнять эту оптимизацию на данном участке кода. Просто интересно. Если заставить разрабов ИАРа довести до ума работу с указателями, то в гнусе не останется больше ничего привлекательного...Скорее наоборот. Если в avr-gcc появится возможность полноценной работы с указателями на разные адресные пространства, то в ИАРе, учитывая его цену, не останется ничего привлекательного для тех, кто стоит перед выбором. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба ну сделайте и обращение к структуре не статическим Как? Если создавать копию структуры на стёке - то это опять лишнее место и время. А как ещё можно сделать переменные класса динамическими без лишнего гиморроя? Скорее наоборот. Если в avr-gcc появится возможность полноценной работы с указателями на разные адресные пространства, то в ИАРе, учитывая его цену, не останется ничего привлекательного для тех, кто стоит перед выбором. Но ведь GCC и так работает с указателями часто лучше, чем IAR? Зато посмотрите, как он хромает при работе с битовыми полями... :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба А это как ? Если копировать побайтно... Ну Вы же должны знать главное правило в программировании. Это я насчет соотношения время/размер: void copy_xor_fast(unsigned char * dst, unsigned char * src1, unsigned char * src2, unsigned char count) { while(count&3) { *dst++=*src1++^*src2++; count--; } if (count) do { unsigned char v0,v1,v2,v3; v0=*src1++; v1=*src1++; v2=*src1++; v3=*src1++; v0^=*src2++; v1^=*src2++; v2^=*src2++; v3^=*src2++; *dst++=v0; *dst++=v1; *dst++=v2; *dst++=v3; } while(count-=4); } Время в функции 888 тактов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба Ну Вы же должны знать главное правило в программировании. Это я насчет соотношения время/размер:я имел в виду побайтово, те байт за проход цикла, но даже здесь void copy_xor_fast(unsigned char *dst, unsigned char *src1, unsigned char *src2, unsigned char count) Время в функции 888 тактов. у gcc 809 тактов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба я имел в виду А я имею в виду, что скорее всего Ваш пример высосан из пальца, чисто для того, чтобы показать узкое место IAR'а. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aesok 0 1 декабря, 2008 Опубликовано 1 декабря, 2008 (изменено) · Жалоба Не могли бы вы более развернуто прокомментировать этот эффект? Я не знаю этот проход оптимизации, он заменил тело цикла: *dst++ = *src1++ ^ *src2++; на: ;; MEM[base: dst + ivtmp.11] = [bit_xor_expr] MEM[base: src2 + ivtmp.11] ^ MEM[base: src1 + ivtmp.11]; ;; ivtmp.11 = ivtmp.11 + 1; Предположив что выполнять в теле цикла 3 сложения регистров и одно сложение с константой выгоднее, чем 4 сложения с константой. Вот что говориться про это проход: /* This pass tries to find the optimal set of induction variables for the loop. It optimizes just the basic linear induction variables (although adding support for other types should not be too hard). It includes the optimizations commonly known as strength reduction, induction variable coalescing and induction variable elimination. It does it in the following steps: 1) The interesting uses of induction variables are found. This includes -- uses of induction variables in non-linear expressions -- addresses of arrays -- comparisons of induction variables 2) Candidates for the induction variables are found. This includes -- old induction variables -- the variables defined by expressions derived from the "interesting uses" above 3) The optimal (w.r. to a cost function) set of variables is chosen. The cost function assigns a cost to sets of induction variables and consists of three parts: -- The use costs. Each of the interesting uses chooses the best induction variable in the set and adds its cost to the sum. The cost reflects the time spent on modifying the induction variables value to be usable for the given purpose (adding base and offset for arrays, etc.). -- The variable costs. Each of the variables has a cost assigned that reflects the costs associated with incrementing the value of the variable. The original variables are somewhat preferred. -- The set cost. Depending on the size of the set, extra cost may be added to reflect register pressure. All the costs are defined in a machine-specific way, using the target hooks and machine descriptions to determine them. 4) The trees are transformed to use the new variables, the dead code is removed. All of this is done loop by loop. Doing it globally is theoretically possible, it might give a better performance and it might enable us to decide costs more precisely, but getting all the interactions right would be complicated. */ Больше информации о этом проходе, а также о том можно ли его более точно настроить для AVR можно попытаться найти в файле tree-ssa-loop-ivopts.c Как видно, отключение одной из оптимизаций привело к существенному улучшению кода. Разве в компиляторе не предусмотрен анализ результатов оптимизации? Нет. Есть порядка 200 проходов, большая часть из которых являются оптимизирующими, друга часть служебными. Для каждого уровня оптимизации существует свой набор проходов. Каждая платформа может модифицировать эти наборы под себя. Пользователь может дополнительно включать отключать проходы с помощью ключей -f*. Анализатором выступает разработчик/пользователь. Анатолий. Изменено 1 декабря, 2008 пользователем aesok Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sonycman 0 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба Заметил интересную особенность GCC по работе с виртуальными функциями. Есть базовый класс с одной виртуальной функцией: class CFanRegulatorBase { ... virtual void pwmOutputValue(byte value) {}; ... }; и четыре производных класса: class CCpuRegulator : public CFanRegulatorBase { ... void pwmOutputValue(byte value); ... }; То есть каждый класс имеет свою уникальную функцию pwmOutputValue. Для чего компилятор создаёт в памяти таблицу с адресами для каждой такой функции. В ИАР для этого отводится 2*5 (четыре функции производных классов плюс одна базового) байт памяти программ. В GCC - почему-то 6*5 байт памяти данных. Не понятно, почему именно шесть байт в таблице, и не совсем понятно, почему именно в оперативке - 30 байт это не мало... Пока что получается, что ИАР компилирует код более компактный как по ROM, так и по RAM... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба А я имею в виду, что скорее всего Ваш пример высосан из пальца, чисто для того, чтобы показать узкое место IAR'а.Неа, он взят из реальной проги, вот кусок: if (wrtype & 0x02) // AND или XOR { if (wrtype & 0x04) // XOR , wrtype==6 { do { tmp1=*pS++; // данные для записи tmp2=*pD; // данные из базы tmp1 ^= tmp2; *pD++=tmp1; // переносим в базу *pB++=tmp1; // заполняем данные ответа } while (--len); } else // AND , wrtype==2 { do { tmp1=*pS++; // данные для записи tmp2=*pD; // данные из базы tmp1 &= tmp2; *pD++=tmp1; // переносим в базу *pB++=tmp1; // заполняем данные ответа } while (--len); } } else // REP или OR { if (wrtype & 0x04) // OR , wrtype==4 { do { tmp1=*pS++; // данные для записи tmp2=*pD; // данные из базы tmp1 |= tmp2; *pD++=tmp1; // переносим в базу *pB++=tmp1; // заполняем данные ответа } while (--len); } else // REP , wrtype==0 { do { tmp1=*pS++; // данные для записи *pD++=tmp1; // переносим в базу *pB++=tmp1; // заполняем данные ответа } while (--len); } } И IARу плохеет на таком коде, а gcc делает почти 1 в 1 с асм. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 120 1 декабря, 2008 Опубликовано 1 декабря, 2008 · Жалоба Есть порядка 200 проходов, большая часть из которых являются оптимизирующими, друга часть служебными. Для каждого уровня оптимизации существует свой набор проходов. Каждая платформа может модифицировать эти наборы под себя. Пользователь может дополнительно включать отключать проходы с помощью ключей -f*.Спасибо, очень познавательно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ARV 0 2 декабря, 2008 Опубликовано 2 декабря, 2008 · Жалоба И IARу плохеет на таком коде, а gcc делает почти 1 в 1 с асм.если в этом коде внутрь цикла do перенести анализ ваших переменных (может, даже вместо if использовать switch), то он станет и короче, да и оттранслируется лучше. ведь разница в четырех ветках всего лишь в способе изменения tmp1.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 2 декабря, 2008 Опубликовано 2 декабря, 2008 · Жалоба если в этом коде внутрь цикла do перенести анализ ваших переменных (может, даже вместо if использовать switch), то он станет и короче, да и оттранслируется лучше. ведь разница в четырех ветках всего лишь в способе изменения tmp1.... Ну-ну. Код как раз грамотно написан, вынесены из циклов проверки. Вот что говориться про это проход: .... Все понятно. Я так и предполагал, что это векторизация, не знал только, что можно отключить. а также о том можно ли его более точно настроить для AVR Ну так для AVR можно сразу выключить эту оптимизацию, потому как нет нативной поддержки в системе комманд. Это будет самая грамотная настройка :) И IARу плохеет на таком коде, а gcc делает почти 1 в 1 с асм. Плохеет. Спору нет. Теперь давайте я попробую проявить телепатические способности ;) Приведенный код - это, видимо, разнообразная печаталка символов на графический дисплей? Если да, то имеет смысл развернуть циклы для ускорения, все-таки это самое узкое место в гуйне. А при развороте проигрыша у IAR'а - ну отсилы несколько процентов. Что на общем фоне повышения производительности - так и не заметно. А вот другого плана алгоритм - я тут попробовал собрать свою процедуру md5, оптимизированную под AVR... Гнусь сдался по всем параметрам - по размеру кода, по скорости, по требуемому стеку (собственно, взаимосвязь проста - ниасилил разложить все что можно в регистрах, полез в стек и сдался). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ARV 0 2 декабря, 2008 Опубликовано 2 декабря, 2008 (изменено) · Жалоба Ну-ну. Код как раз грамотно написан, вынесены из циклов проверки. позволю себе не согласиться. я сделал такую: "обертку" показанного участка #include <avr/io.h> volatile uint8_t wrtype, tmp1, tmp2, len; volatile uint8_t *pD, *pB, *pS; int main(void){ while(1){ // здесь приведенный участок кода 1 в 1 } } скомпилировал эту программку для atmega128 с опцией -Os, в итоге получил 638 байт программного кода. затем я модифицировал код, как говорил: int main(void){ while(1){ do{ tmp1=*pS++; tmp2=*pD; if (wrtype & 0x02) if (wrtype & 0x04) tmp1 ^= tmp2; else tmp1 &= tmp2; else if (wrtype & 0x04) tmp1 |= tmp2; *pD++=tmp1; *pB++=tmp1; } while(--len); }} как я понимаю, логика сохранилась полностью (надеюсь, я не ошибся). результат компиляции: 360 байт памяти программ. Разница существенная, не так ли? а замедление скорости работы, думаю, явно не в 2 раза :) Изменено 2 декабря, 2008 пользователем ARV Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 2 декабря, 2008 Опубликовано 2 декабря, 2008 · Жалоба а замедление скорости работы, думаю, явно не в 2 раза Больше чем в 2 :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться