Перейти к содержанию
    

Перенос кода из под ИАРа на WinAVR

Но всё же даже у гнуса бывают косяки с указателями.

И всего-то с двумя:

    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... :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

но всё равно не хватило сообразительности задействовать второй указатель.

ИАР без проблем юзает здесь два указателя даже со статическим буффером pb... :)

http://electronix.ru/forum/index.php?showt...mp;#entry506246

 

ну сделайте и обращение к структуре не статическим

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

-Os -fno-ivopts

....

Вы не умеете его готовить.

Не могли бы вы более развернуто прокомментировать этот эффект? Как видно, отключение одной из оптимизаций привело к существенному улучшению кода. Разве в компиляторе не предусмотрен анализ результатов оптимизации? Т.е. если после какой-либо оптимизации код стал хуже по основоному критерию (-Os), то не выполнять эту оптимизацию на данном участке кода. Просто интересно.

 

 

Если заставить разрабов ИАРа довести до ума работу с указателями, то в гнусе не останется больше ничего привлекательного...
Скорее наоборот. Если в avr-gcc появится возможность полноценной работы с указателями на разные адресные пространства, то в ИАРе, учитывая его цену, не останется ничего привлекательного для тех, кто стоит перед выбором.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ну сделайте и обращение к структуре не статическим

Как? Если создавать копию структуры на стёке - то это опять лишнее место и время.

А как ещё можно сделать переменные класса динамическими без лишнего гиморроя?

 

Скорее наоборот. Если в avr-gcc появится возможность полноценной работы с указателями на разные адресные пространства, то в ИАРе, учитывая его цену, не останется ничего привлекательного для тех, кто стоит перед выбором.

Но ведь GCC и так работает с указателями часто лучше, чем IAR?

Зато посмотрите, как он хромает при работе с битовыми полями... :(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А это как ? Если копировать побайтно...

 

Ну Вы же должны знать главное правило в программировании. Это я насчет соотношения время/размер:

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 тактов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

void copy_xor_fast(unsigned char *dst, unsigned char *src1, unsigned char *src2, unsigned char count)

Время в функции 888 тактов.

у gcc 809 тактов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

я имел в виду

 

А я имею в виду, что скорее всего Ваш пример высосан из пальца, чисто для того, чтобы показать узкое место IAR'а.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не могли бы вы более развернуто прокомментировать этот эффект?

 

Я не знаю этот проход оптимизации, он заменил тело цикла:

*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*.

 

Анализатором выступает разработчик/пользователь.

 

Анатолий.

Изменено пользователем aesok

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Заметил интересную особенность 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...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А я имею в виду, что скорее всего Ваш пример высосан из пальца, чисто для того, чтобы показать узкое место 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 с асм.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Есть порядка 200 проходов, большая часть из которых являются оптимизирующими, друга часть служебными. Для каждого уровня оптимизации существует свой набор проходов. Каждая платформа может модифицировать эти наборы под себя. Пользователь может дополнительно включать отключать проходы с помощью ключей -f*.
Спасибо, очень познавательно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

И IARу плохеет на таком коде, а gcc делает почти 1 в 1 с асм.
если в этом коде внутрь цикла do перенести анализ ваших переменных (может, даже вместо if использовать switch), то он станет и короче, да и оттранслируется лучше. ведь разница в четырех ветках всего лишь в способе изменения tmp1....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

если в этом коде внутрь цикла do перенести анализ ваших переменных (может, даже вместо if использовать switch), то он станет и короче, да и оттранслируется лучше. ведь разница в четырех ветках всего лишь в способе изменения tmp1....

 

Ну-ну. Код как раз грамотно написан, вынесены из циклов проверки.

 

Вот что говориться про это проход:

....

 

Все понятно. Я так и предполагал, что это векторизация, не знал только, что можно отключить.

 

а также о том можно ли его более точно настроить для AVR

 

Ну так для AVR можно сразу выключить эту оптимизацию, потому как нет нативной поддержки в системе комманд. Это будет самая грамотная настройка :)

 

И IARу плохеет на таком коде, а gcc делает почти 1 в 1 с асм.

 

Плохеет. Спору нет.

 

Теперь давайте я попробую проявить телепатические способности ;)

 

Приведенный код - это, видимо, разнообразная печаталка символов на графический дисплей? Если да, то имеет смысл развернуть циклы для ускорения, все-таки это самое узкое место в гуйне. А при развороте проигрыша у IAR'а - ну отсилы несколько процентов. Что на общем фоне повышения производительности - так и не заметно.

 

А вот другого плана алгоритм - я тут попробовал собрать свою процедуру md5, оптимизированную под AVR... Гнусь сдался по всем параметрам - по размеру кода, по скорости, по требуемому стеку (собственно, взаимосвязь проста - ниасилил разложить все что можно в регистрах, полез в стек и сдался).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну-ну. Код как раз грамотно написан, вынесены из циклов проверки.

позволю себе не согласиться. я сделал такую: "обертку" показанного участка

#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 раза :)

Изменено пользователем ARV

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

а замедление скорости работы, думаю, явно не в 2 раза

 

Больше чем в 2 :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...