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

Оптимизация кода IAR С++ v5.10 компилятора

Оптимизации, мать их так :(

Ну еще можете заняться индивидуальным террором - прагмами указывать разные ключи оптимизации для разных кусков текста.

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


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

Ну еще можете заняться индивидуальным террором - прагмами указывать разные ключи оптимизации для разных кусков текста.

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

Наверное, никак пока не привыкну к компиляторам после чисто ассемблерного программирования... :(

 

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

Может быть, только у меня такая "шняга"? :)

 

ЗЫ: Вот зачем надо было вводить второй стёк для данных? В листинге не нашёл ни единой команды push/pop. Целый регистр-указатель под это угробили. А потом конечно, пихаем сначала r16:r17 в Z, юзаем его, затем пихаем в него r18:r19 и т.д, вместо естественной работы с двумя указателями...

Ужас :(

 

Обработчики прерываний (в виде единой функции!) с длиннющим сохранением регистров на стёк, когда на асме обошёлся бы парой-тройкой...

 

Порой больно смотреть на такое разбазаривание ресурсов проца... :(

Но начинаю привыкать к С, уже лень становится писать что-либо на асме...

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


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

Оптимизации, мать их так

 

Ну попробуйте такой вариант -

#pragma optimize=no_cse
__x bool    usartSendData(const byte    *buffer, byte    length)
{
  byte    a, len;
  if (length > sizeof(tx_buffer) || length == 0)
  {
    statusTx.length_err    =    TRUE;
    return FALSE;
  }
  while (statusTx.length);
  while    ((byte)(sizeof(tx_buffer) - (byte)statusTx.queue) < length);
  len    =    length;
  a = sizeof(tx_buffer) - statusTx.pointer;
  if (length > a)
  {
    byte *p=tx_buffer + statusTx.pointer;
    len -= a;
    for (;a > 0;a--) *p++ = *buffer++;
    statusTx.pointer    =    0;
  }
  statusTx.pointer    +=    len;
  {
    byte *p=tx_buffer + statusTx.pointer;;
    for (;len > 0;len--) *p++ = *buffer++;
  }
  statusTx.length    =    length;
  UCSR0B    |=    1 << UDRIE0;
  statusTx.length_err    =    FALSE;
  return    TRUE;
}

bool    usartSendString(fbyte __flash    *pointer)
{
    return usartSendCommand(USART_TEXT, (const void*)pointer, strlen_P(pointer));
}


#pragma optimize=no_cse
__z bool    usartSendCommand(byte command, const void* pointer, byte length)
{
    byte    a;
    byte    *pBuffer;


    switch (command)
    {
    case    USART_TEXT:
        if (!length || length > USART_MAXDATALENGTH) return FALSE;
        pBuffer    =    comm_buffer + USART_PREFFIX;
        *pBuffer++    =    command;
        *pBuffer++    =    length;

//        copymem_P(pBuffer, (fbyte __flash *)pointer,  length);
    a=length;
    {
      byte *p=pBuffer;
      fbyte    __flash*    text;
      text    =    (fbyte __flash*)pointer;
      do
      {
            *p++    =    *text++;
      }
      while(--a);
      *p=CalculateCRC(pBuffer, length);
    }
        break;
    default:
        return    FALSE;
    }
    pBuffer    =    comm_buffer;
    *pBuffer++    =    'S';
    *pBuffer    =    'N';
    return usartSendData(comm_buffer, length + USART_FRONTEND);
}

 

Тут много магии - отдельные указатели (почему-то иногда очень плохо у IAR'а по ходу выполнения функции переменные перемещаются по регистрам), no_cse... А в общем итоге видимо проблема заключается в том, что использование структур и двух указателей сразу приводит к нехватке регистровых пар. Видимо, тут GCC был бы на высоте.

 

Обработчики прерываний (в виде единой функции!) с длиннющим сохранением регистров на стёк, когда на асме обошёлся бы парой-тройкой...

 

Если какие-то функции другие вызываются - будет сохранение всех scratch-регистров, если нет - все будет хорошо.

 

Вот зачем надо было вводить второй стёк для данных?

 

Вот GCC не делает второй стек для данных. Все хорошо, пока переменные одной функции помещаются в регистрах. Как только надо исполнить стековую переменную - начинается мрак, там такой код для пролога/эпилога, что с непривычки за голову хватаешься...

 

Тут конечно, надо бы было сделать 2 комманды при разработке ядра, LDD r,SP+disp и STD SP+disp,r и пару комманд увеличения/уменьшения указателя стека - и тогда бы здорово помогло... Но поезд ушел 10 лет назад :)

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


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

Ну попробуйте такой вариант

...

Если какие-то функции другие вызываются - будет сохранение всех scratch-регистров, если нет - все будет хорошо.

...

Вот GCC не делает второй стек для данных. Все хорошо, пока переменные одной функции помещаются в регистрах. Как только надо исполнить стековую переменную - начинается мрак, там такой код для пролога/эпилога, что с непривычки за голову хватаешься...

 

Тут конечно, надо бы было сделать 2 комманды при разработке ядра, LDD r,SP+disp и STD SP+disp,r и пару комманд увеличения/уменьшения указателя стека - и тогда бы здорово помогло... Но поезд ушел 10 лет назад :)

 

Спасибо Дмитрий, попробую :)

 

Насчёт прерываний - в том то и дело, что каких-либо вызовов функций нет, а сохраняется по 7-9 регистров на CSTACK. Возможно, слишком громоздкий код получается, конечно.

 

По стеку понятно, для большого кол-ва переменных это будет выгодно, наверное, хотя у себя я пока не наблюдал подгрузку данных с регистра Y...

 

ЗЫ: Хочу передать Вам большой респект, как кодеру известной в своё время команды Codebusters :)

До сих пор помню Satisfaction megademo, сколько раз тогда запускал её и любовался эффектами и музыкой...

Приятно, что этот форум посещают подобные вам люди :)

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


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

Да, чудесным образом лишние инструкции исчезли! :a14:

Как я понял, всё это путём небольшого изменения порядка следования команд, введения дополнительных указателей/или переменных для исключения лишних вычислений и парочкой прагм...

 

Да-а, башку можно сломать от такого напряга B)

 

В общем вот что получилось в итоге:

bool    usartSendData(const byte    *buffer, byte    length)
{
    byte    a, len;
    if (length > sizeof(tx_buffer) || length == 0)
    {
        statusTx.length_err    =    TRUE;
        return FALSE;
    }
    while (statusTx.length);
    while    ((byte)(sizeof(tx_buffer) - (byte)statusTx.queue) < length);
    len    =    length;
    a = sizeof(tx_buffer) - statusTx.pointer;
    if (length > a)
    {
        byte *p=tx_buffer + statusTx.pointer;
        len -= a;
        for (;a > 0;a--) *p++ = *buffer++;
        statusTx.pointer    =    0;
    }
    a    =    len;
    byte *p=tx_buffer + statusTx.pointer;
    for (;a > 0;a--) *p++ = *buffer++;
      statusTx.pointer    +=    len;
    statusTx.length    =    length;
    UCSR0B    |=    1 << UDRIE0;
    statusTx.length_err    =    FALSE;
    return    TRUE;
}

#pragma optimize=no_cse
__z    bool    usartSendCommand(byte command, const void* pointer, byte length)
{
    byte    a;
    byte    *pBuffer;
    
    switch (command)
    {
    case    USART_TEXT:
        if (!length || length > USART_MAXDATALENGTH) return FALSE;
        pBuffer    =    comm_buffer + USART_PREFFIX;
        *pBuffer++    =    command;
        *pBuffer++    =    length;
        a=length;
        byte *p;
        p    =    pBuffer;
        fbyte    __flash*    text;
      text    =    (fbyte __flash*)pointer;
      while(a--) *p++    =    *text++;
      *p = CalculateCRC(pBuffer, length);
        break;
    default:
        return    FALSE;
    }
    pBuffer    =    comm_buffer;
    *pBuffer++    =    'S';
    *pBuffer    =    'N';
    return usartSendData(comm_buffer, length + USART_FRONTEND);
}

Все три цикла копирования стали просто идеальными!

Причём первая функция сохраняет "стройность" и без "прагмы" и "__x".

Магия? :rolleyes:

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


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

Да-а, башку можно сломать от такого напряга B)

В общем-то не слишком и сложно, если подумать, то не сложнее писания врукопашную на ASM. Опыт и навыки естественно нужны. Проблемы только с переносимостью между компиляторами :(

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


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

Тут много магии - отдельные указатели (почему-то иногда очень плохо у IAR'а по ходу выполнения функции переменные перемещаются по регистрам), no_cse... А в общем итоге видимо проблема заключается в том, что использование структур и двух указателей сразу приводит к нехватке регистровых пар. Видимо, тут GCC был бы на высоте.
О, это именно то о чем я твержу уже очень давно но все почему-то относятся к этому

скептически, IAR действительно дуреет когда количество источников/приемников данных

в близлежащем коде >2, причем обращение к структурам у него часто идет как обращение

к источнику/приемнику данных. При этом выбор того что будет адресоваться через регистровую

пару зависит от порядка обращения в исходнике. У Gcc с этим чуть по лучше, правда, если уж

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

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


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

Проблемы только с переносимостью между компиляторами

 

делаем так -

 

#ifndef __IAR_SYSTEMS_ICC__
#define __x
#define __z
#define __z_x
#define __x_z
#endif

 

И нет проблемы ;)

 

У Gcc с этим чуть по лучше

 

Это пока стекового фрейма нет. Как только он появляется - все, гаплык.... Кстати, а не подскажете по-быстрому, как GCC заставить генерить асмовский листинг с исходным текстом на Си в каментах?

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


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

Это пока стекового фрейма нет. Как только он появляется - все, гаплык.... Кстати, а не подскажете по-быстрому, как GCC заставить генерить асмовский листинг с исходным текстом на Си в каментах?
Это да, стараюсь до стекового фрейма не доводить...

 

если нужен листинг не для сборки а для посмотреть:

 

avr-objdump -S proname.elf >progname.lst

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


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

делаем так -

 

#ifndef __IAR_SYSTEMS_ICC__
#define __x
#define __z
#define __z_x
#define __x_z
#endif

 

И нет проблемы ;)

Хм, непонятно, а где-же указание компилеру, какие действия выполнять при обнаружении подобного преффикса?

Это что, зашито мёртво в любой компилер?

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


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

Хм, непонятно, а где-же указание компилеру, какие действия выполнять при обнаружении подобного преффикса?
Там как раз и написано что если компилятор неИАР то вместо __x, итд подставить

пустое место.

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


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

Там как раз и написано что если компилятор неИАР то вместо __x, итд подставить

пустое место.

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

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


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

Еще непонятки с оптимизацией, правда в IAR AVR 4.30

3 варианта одного и того-же

     54              *ptr=b;
   \   00000026   8310               ST      Z, R17
     55              ptr-=7;
     56              if(*(ptr++)!=0xE0) return;//ptr++;
   \   00000028   9737               SBIW    R31:R30, 7
   \   0000002A   01DF               MOVW    R27:R26, R31:R30
   \   0000002C   9631               ADIW    R31:R30, 1
   \   0000002E   910C               LD      R16, X
   \   00000030   3E00               CPI     R16, 224
   \   00000032   F459               BRNE    ??RSRecByte_0
     57              if(*(ptr++)!=0x00) return;//ptr++;
   \   00000034   9101               LD      R16, Z+
   \   00000036   2300               TST     R16
   \   00000038   F441               BRNE    ??RSRecByte_0

     54              *ptr=b;
   \   00000022   8310               ST      Z, R17
     55          //    ptr-=7;
     56              if(*(ptr++)!=0xE0) return;//ptr++;
   \   00000024   9101               LD      R16, Z+
   \   00000026   3E00               CPI     R16, 224
   \   00000028   F459               BRNE    ??RSRecByte_0
     57              if(*(ptr++)!=0x00) return;//ptr++;
   \   0000002A   9101               LD      R16, Z+
   \   0000002C   2300               TST     R16
   \   0000002E   F441               BRNE    ??RSRecByte_0

     54              *ptr=b;
   \   00000022   8310               ST      Z, R17
     55              ptr-=7;
   \   00000024   9737               SBIW    R31:R30, 7
     56              if(*(ptr)!=0xE0) return;ptr++;
   \   00000026   9101               LD      R16, Z+
   \   00000028   3E00               CPI     R16, 224
   \   0000002A   F471               BRNE    ??RSRecByte_0
     57              if(*(ptr)!=0x00) return;ptr++;
   \   0000002C   9101               LD      R16, Z+
   \   0000002E   2300               TST     R16
   \   00000030   F459               BRNE    ??RSRecByte_0

Кроме того появляется сохранение/восстановление R26:27.

Вроде немного, но совершенно непонятная разница.

Получается, лучше дробить длинные выражения по кускам?

В общем-то не слишком и сложно, если подумать, то не сложнее писания врукопашную на ASM. Опыт и навыки естественно нужны.

К сожалению, опыт приходится наживать свой и только мало-мало чужого, в отличие от писюкового программизма. Нет литературы, ориентированной на оптимальное написание на С.

 

P.S. Это пока только заготовка

void RSRecByte(){
  if(!IsRecByteRS())return;
  byte b=GetByteRS();
  if(RSStatus==RS_RECCMD){//ïðèåì êîìàíäû
    byte *ptr=RSRecCmdBuffer; for(byte i=0;i<7;i++,ptr++)*ptr=*(ptr+1);
    *ptr=b;
    ptr-=7;
    if(*ptr!=0xE0) return;ptr++;
    if(*ptr!=0x00) return;ptr++;
    if(*ptr!=SysNum) return;ptr++;
    if(*ptr!=0x01) return;ptr++;
    RSStatus=1;
  }else{//ïðèåì äàííûõ
  }
}

Знать-бы какие подсказки компилер понимает :(

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


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

3 варианта одного и того-же

 

Местные телепаты требуют полностью функцию ;) Желательно, с обрамлением, чтобы можно было собрать.

 

А, просмотрелся... Вы уже асилили подсказать компилятору..

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


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

P.S. Это пока только заготовка

А попробуйте что-то вроде

  if(RSStatus==RS_RECCMD)
{
      memcpy(RSRecCmdBuffer, &RSRecCmdBuffer, 7); 
      RSRecCmdBuffer[7] = b;

memcpy - intrinsic, компилятор может ее реализовать эффективнее, чем компилируя ваш цикл.

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


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

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

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

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

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

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

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

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

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

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