Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: К знатокам
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Страницы: 1, 2, 3
SasaVitebsk
Пишу достаточно простую прогу. Пытаюсь оптимизировать.

Столкнулся с одной бедой. Попробую описать.

Построено на прерываниях. Между прерываниями разные промежутки. Есть короткие, есть длинные. Как назло именно короткое прерывание сильно загружено. Дабы разгрузить его я пытаюсь часть вычислений вынести в предыдущее не загруженное прерывание. Уже полностью перешёл на указатели, но всё равно шляпа получается.

Проблема в том, что я не могу ввести локальные переменные на два прерывания. Таким образом я ввожу статик. Но тогда во втором прерывании компилятор пытается сохранить значения. А мне это не надо ни капельки. Чтобы этого избежать я ввёл локальные переменные и во втором прерывании выполнил присваивание локальным статик. Код получился значительно компактнее, но всё равно выполняется никому не нужное присваивание. А это 6 указателей!

Теперь сам вопрос.
Могу ли я указать компилятору что можно разрушать переменные в данном прерывании. То есть что не надо их хранить. Или как это сделать. Надо типа переобъявить статические переменные локальными. Не знаю как выразится. Надеюсь поняли.
dxp
Все равно как-то туманно. Вы бы псевдокод привели, коротенько, только самое необходимое с пояснениями по тексту, а так гадать, что у вас получается, и что хотите, сложновато.
alexander55
Цитата(SasaVitebsk @ Sep 6 2007, 04:58) *
Пишу достаточно простую прогу. Пытаюсь оптимизировать.

Столкнулся с одной бедой. Попробую описать.

Построено на прерываниях. Между прерываниями разные промежутки. Есть короткие, есть длинные. Как назло именно короткое прерывание сильно загружено. Дабы разгрузить его я пытаюсь часть вычислений вынести в предыдущее не загруженное прерывание. Уже полностью перешёл на указатели, но всё равно шляпа получается.

Проблема в том, что я не могу ввести локальные переменные на два прерывания. Таким образом я ввожу статик. Но тогда во втором прерывании компилятор пытается сохранить значения. А мне это не надо ни капельки. Чтобы этого избежать я ввёл локальные переменные и во втором прерывании выполнил присваивание локальным статик. Код получился значительно компактнее, но всё равно выполняется никому не нужное присваивание. А это 6 указателей!

Теперь сам вопрос.
Могу ли я указать компилятору что можно разрушать переменные в данном прерывании. То есть что не надо их хранить. Или как это сделать. Надо типа переобъявить статические переменные локальными. Не знаю как выразится. Надеюсь поняли.

Как-то все не очеь ясно, но и из того, что я понял, могу предложить ввести какой-то класс со всеми данными и в нем функции, работы с этими данными. В прерываниях и где-нужно их вызывать и не париться больше со статиками глобальными, локальными и еще чем-то. За Вас все сделает компилятор.
Сергей Борщ
Да, действительно мутновато. Если я правильно понял, то вы можете завести глобальные переменные, в одном прерывании в них сохранять, а во втором заведите локальные переменные, копируйте в них глобальные и работайте с локальными копиями. Если оба прерывания в одном файле - объявите эти глобальные как static, тогда они не будут видны в других файлах. И еще одна мысль - если переменных не много, не знаю как в новых, а в старых версиях можно было залочить несколько регистров и разместить эти переменные в залоченных регистрах.
MALLOY2
Можно еще все нужные переменные загнать в 1 структуру, создать глобальный указатель на структру, в прерываниии глобальный указатель копировать в локальный с ним и работать.
Dog Pawlowa
Цитата(SasaVitebsk @ Sep 6 2007, 03:58) *
Построено на прерываниях. Между прерываниями разные промежутки. Есть короткие, есть длинные. Как назло именно короткое прерывание сильно загружено. Дабы разгрузить его я пытаюсь часть вычислений вынести в предыдущее не загруженное прерывание. Уже полностью перешёл на указатели, но всё равно шляпа получается.

Я тоже не особенно понял суть проблемы, а свои подобные решаю так...
Я тоже разбиваю некоторую обработку внутри прерываний на более мелкие части, чтобы сократить время прерывания, но не понимаю, как это связано с классом переменных.
Все переменные являются статическими (точнее, определены снаружи), после выполнения части обработки происходит выход из прерывания, а при последующем входе осуществляется другая часть расчета. Выбор - по interrupt_counter. Убогость системы прерываний AVR удручает, но что делать? Чтобы сократить перебор в switch, можно сделать массив функций и выбирать по индексу, но оптимизатор сам это может сделать.

Вот мой пример обработки клавиатуры :
Код
        switch (interrupt_counter)     // to reduce time of function
    {
    case    0:    interrupt_counter=1;        //refresh statuses
            kbd_status3=kbd_status2;
            kbd_status2=kbd_status1;
            kbd_status1=kbd_status0;
            kbd_status0=Kbd;
            interrupt_counter=interrupt_counter;
            break;

    case    1:   interrupt_counter=2;        //check pressed key every tic
            pressed_keys=kbd_status0&kbd_status1&kbd_status2;
            if (pressed_keys&(~kbd_status3))
            if (bSound==SoundAlwaysOn) Beep(5,1);
            found_events|=pressed_keys&(~kbd_status3);        // found edge
            if ((pressed_keys==kUp+kDn)&&  // now this two keys
                ((~kbd_status3)&(kUp+kDn)))//and before it wasn't so
                {found_events|=kUpDn;    // set new bit
                found_events&=~(kUp+kDn);    break; } //reset usual bits
            if ((pressed_keys==kUp+kSt)&&  // now this two keys
                ((~kbd_status3)&(kUp+kSt)))//and before it wasn't so
                {found_events|=kUpSt;    // set new bit
                found_events&=~(kUp+kSt);    break; }//reset usual bits                
            if ((pressed_keys==kDn+kSt)&&  // now this two keys
                ((~kbd_status3)&(kDn+kSt)))//and before it wasn't so
                {found_events|=kDnSt;    // set new bit
                found_events&=~(kDn+kSt); break; }    //reset usual bits                    
        break;            
                
    case    2:    interrupt_counter=3;        //check if key the same as before
            if ((pressed_keys&kUp)||(pressed_keys&kDn))    //key for repeat
            {    if (pressed_keys==old_pressed_keys) // same key?
                {    if (pressed_counter)         // yes, time over?
                    {pressed_counter--;}      //No
                    else     {    pressed_counter=NextDelay;        //Yes
                            found_events=pressed_keys; }        //reset
                    }
                else                                            // other key
                    {pressed_counter=StartDelay;    
                    }
            };
            old_pressed_keys=pressed_keys;
            break;
    case    3:    interrupt_counter++;
              break;
    default: interrupt_counter=0;
  }
alexander55
Цитата(Dog Pawlowa @ Sep 6 2007, 11:51) *
Я тоже разбиваю некоторую обработку внутри прерываний на более мелкие части, чтобы сократить время прерывания, но не понимаю, как это связано с классом переменных.

Попробую объяснить. Используем C++.
Объявление класса
class TCalc {
int x,y,z; // промежуточные переменные
public:
TCalc(void) {x=y=z=0;}; // начальная инициализация
void F!(void); // первая функция заносит результат в x
void F2(void); // вторая функция заносит результат в y
void F3(void); // третья функция заносит результат в z
int Read_Z(void) {return z;};
};
Реализация,допустим для F3
void TCalc::F3(void) {z=x+y*y;};

В main.cpp есть строчка TCalc Calc;
В других, где нужно extern TCalc Calc;
В прерывании 1 Calc.F1();
В прерывании 2 Calc.F2();
В прерывании 3 Calc.F3();
Где нужен результат ....=Calc.Read_Z();
Обратите внимание никаких статиков, инициализаций в main, volatile и т.д.
Для любителей указателей можно сделать и на них, но рекомендую прочитать соответствующий пост для понимамания, где они нужны, а где вредны.
SasaVitebsk
Лучше всех понял проблему Сергей Борщ. Я так и сделал. Но всё равно есть лишние операции. Хотелось бы от них избавится. Приведу код с пояснениями.
Код
__no_init    uint8_t        static    *uFaz1,*uFaz2,*uFaz3,
                                *uFaz4,*uFaz5,*uFaz6;    // Вспомогательный. Указатель на текущую фазу двигателя

Это объявление по сути в этих переменных нет необходимости. Чисто чтобы одну операцию разбить на два прерывания.
Код
// Обработка Шима тех каналов (двигателей) которые отрабатывают угол 45
// вывод уровня 0.707 уровень 2
#pragma    vector=TIMER1_COMPB_vect
__interrupt    static void    pvPWWLvl2(void)
{
// Вывод предварительно расчитанных значений портов
PORTE = bOportE[2];
PORTA = bOportA[2];
PORTC = bOportC[2];
// Предварительный расчёт фаз двигателей. Требуется для фазы C
uint8_t    bFaz;
bFaz = (wStateReal[0] & (MAXFAZ-1))>>2;                // Выделить фазу одного двигателя порта
uFaz1 = &bfPortSh1[0][0]+bFaz;
bFaz = (wStateReal[1] & (MAXFAZ-1))>>2;                // Выделить фазу одного двигателя порта
uFaz2 = &bfPortSh2[0][0]+bFaz;
bFaz = (wStateReal[2] & (MAXFAZ-1))>>2;                // Выделить фазу одного двигателя порта
uFaz3 = &bfPortSh3[0][0]+bFaz;
bFaz = (wStateReal[3] & (MAXFAZ-1))>>2;                // Выделить фазу одного двигателя порта
uFaz4 = &bfPortSh4[0][0]+bFaz;
bFaz = (wStateReal[4] & (MAXFAZ-1))>>2;                // Выделить фазу одного двигателя порта
uFaz5 = &bfPortSh5[0][0]+bFaz;
bFaz = (wStateReal[5] & (MAXFAZ-1))>>2;                // Выделить фазу одного двигателя порта
uFaz6 = &bfPortSh6[0][0]+bFaz;
}

Это первое прерывание с места где "Предварительный ..." идёт подготовка инфы для следующего прерывания

Код
// Обработка Шима тех каналов (двигателей) которые отрабатывают угол 67.5. Расчёт предустановленных значений портов.
// вывод уровня 0.9239 уровень 3
#pragma    vector=TIMER1_COMPC_vect
__interrupt    static void    pvPWWLvl3(void)
{
// Вывод предварительно расчитанных значений портов
PORTE = bOportE[3];
PORTA = bOportA[3];
PORTC = bOportC[3];
// Расчёт предустановленных значений портов.
uint8_t    i, *faz1,*faz2,*faz3,*faz4,*faz5,*faz6;
faz1=uFaz1;
faz2=uFaz2;
faz3=uFaz3;
faz4=uFaz4;
faz5=uFaz5;
faz6=uFaz6;
for(i=0;i<MAXLVLPWW;i++)
{
   bOportE[i] = *faz1++ | *faz2++;                    // сформировать значение порта
   bOportA[i] = *faz3++ | *faz4++;                    // сформировать значение порта
   bOportC[i] = *faz5++ | *faz6++;                    // сформировать значение порта
};
}


Здесь вы видите локальные переменные которые введены чтобы избавится от сохранения uFaz. Но суть в том, что сохранять то мне не надо. В следующем цикле эти значения будут высчитаны заново. Введение локальных переменных Дало самый оптимальный код, но может всё таки кто-нибудь из опыта подскажет другое решение. Важно быстродействие последнего прерывания
scifi
Цитата(SasaVitebsk @ Sep 6 2007, 15:27) *
Введение локальных переменных Дало самый оптимальный код, но может всё таки кто-нибудь из опыта подскажет другое решение. Важно быстродействие последнего прерывания

Быстрее всего можно сделать при помощи ассемблера. Но тогда огласите, какой процессор. Например, если это ARM, то можно в обработчике прерывания, которое подготавливает данные, подготовленные данные сохранить в регистрах процессора R8_fiq, ..., R14_fiq. В быстром прерывании перейти в режим FIQ и использовать эти регистры.
SasaVitebsk
Цитата(scifi @ Sep 6 2007, 16:05) *
Быстрее всего можно сделать при помощи ассемблера. Но тогда огласите, какой процессор. Например, если это ARM, то можно в обработчике прерывания, которое подготавливает данные, подготовленные данные сохранить в регистрах процессора R8_fiq, ..., R14_fiq. В быстром прерывании перейти в режим FIQ и использовать эти регистры.

Нет процессор at90can128. Применять ASM не хочу. У меня в первый день все двигатели заработали. Ошибки только инициализации и расчётных констант были. Ну и одна ошибка выхода за пределы массива. Что-то я не припомню чтобы на АСМе у меня в первый день что-то заработало. Хочу средствами Си. Нет значит нет. Немного снижу частоту ШИМа.

Спасибо за участие
vmp
Похоже, здесь основная проблема - это ограничения Си. Для передачи между функциями (в частном случае прерываниями) приходится использовать статические переменные. Каждая их модификация на AVR - это загрузка в регистр, модификация, сохранение в память. Локальные же переменные могут храниться в регистрах и доступ к ним заметно быстрее.
Что бы я сделал с данным конкретным кодом.
1. Объединил бы глобальные переменные в структуру. Доступ к элементам структуры - более быстрый.
2. Перешел бы от указателей (слова) к байтовым индексам. По идее они должны работать быстрее, но надо смотреть, что сгенерит компилятор.
3. Попробовал бы развернуть цикл.
dxp
Цитата(vmp @ Sep 7 2007, 20:42) *
Похоже, здесь основная проблема - это ограничения Си. Для передачи между функциями (в частном случае прерываниями) приходится использовать статические переменные. Каждая их модификация на AVR - это загрузка в регистр, модификация, сохранение в память. Локальные же переменные могут храниться в регистрах и доступ к ним заметно быстрее.

Действительно, в стандартном С такая проблема есть. Но конкретно в EWAVR есть возможность выделить регистр(ы) для своих целей, и компилятор их использовать не будет. Делается это, насколько помню, с помощью расширенного ключевого слова __regvar.

Цитата(vmp @ Sep 7 2007, 20:42) *
2. Перешел бы от указателей (слова) к байтовым индексам. По идее они должны работать быстрее, но надо смотреть, что сгенерит компилятор.

Там (в EWAVR) можно разместить объект в секции TINY, которая размещается в младшем сегменте - в 8-разрядном адресном пространстве, при обращении к объектам, размещенным в этом адресном пространстве, обращение осуществляется на основе, понятно, 8-битных адресов. Все это тоже, ессно, расширение, объект объявляется с помощью расширенного ключевого слова __tiny.
Rst7
Цитата(SasaVitebsk @ Sep 6 2007, 14:27) *
Код
// Обработка Шима тех каналов (двигателей) которые отрабатывают угол 67.5. Расчёт предустановленных значений портов.
// вывод уровня 0.9239 уровень 3
#pragma    vector=TIMER1_COMPC_vect
__interrupt    static void    pvPWWLvl3(void)
{
// Вывод предварительно расчитанных значений портов
PORTE = bOportE[3];
PORTA = bOportA[3];
PORTC = bOportC[3];
// Расчёт предустановленных значений портов.
uint8_t    i, *faz1,*faz2,*faz3,*faz4,*faz5,*faz6;
faz1=uFaz1;
faz2=uFaz2;
faz3=uFaz3;
faz4=uFaz4;
faz5=uFaz5;
faz6=uFaz6;
for(i=0;i<MAXLVLPWW;i++)
{
   bOportE[i] = *faz1++ | *faz2++;                    // сформировать значение порта
   bOportA[i] = *faz3++ | *faz4++;                    // сформировать значение порта
   bOportC[i] = *faz5++ | *faz6++;                    // сформировать значение порта
};
}


Здесь вы видите локальные переменные которые введены чтобы избавится от сохранения uFaz. Но суть в том, что сохранять то мне не надо. В следующем цикле эти значения будут высчитаны заново. Введение локальных переменных Дало самый оптимальный код, но может всё таки кто-нибудь из опыта подскажет другое решение. Важно быстродействие последнего прерывания


Тут вообще много кода генерит иар. Так как проц у вас с немалым флешем, я думаю можно особо память программ не экономить. Посему предлагаю такой вариант:

Код
//Заменяем массивы на просто переменные
char bOportE0;
char bOportE1;
char bOportE2;
char bOportE3;
char bOportA0;
char bOportA1;
char bOportA2;
char bOportA3;
char bOportC0;
char bOportC1;
char bOportC2;
char bOportC3;
#pragma    vector=TIMER1_COMPC_vect
__interrupt    static void    pvPWWLvl3(void)
{
  PORTE = bOportE3;
  PORTA = bOportA3;
  PORTC = bOportC3;
  uint8_t *faz1,*faz2;
  
  faz1=uFaz1;
  faz2=uFaz2;
  bOportE0=*faz1++|*faz2++;
  bOportE1=*faz1++|*faz2++;
  bOportE2=*faz1++|*faz2++;
  bOportE3=*faz1++|*faz2++;

  faz1=uFaz3;
  faz2=uFaz4;
  bOportA0=*faz1++|*faz2++;
  bOportA1=*faz1++|*faz2++;
  bOportA2=*faz1++|*faz2++;
  bOportA3=*faz1++|*faz2++;
  
  faz1=uFaz5;
  faz2=uFaz6;
  bOportC0=*faz1++|*faz2++;
  bOportC1=*faz1++|*faz2++;
  bOportC2=*faz1++|*faz2++;
  bOportC3=*faz1++|*faz2++;

}


и отключаем для этого модуля кластеризацию переменных. Это освободит один индексный регистр и здорово сократит код. А вообще, наверное надо копать не от финальных процедур, а чуть выше, возможно, пересмотрев алгоритм генерации данных, можно будет сильно улучшить производительность... Если есть возможность, расскажите, какие данные вам надо лить в порты, попробуем придумать что-то оригинальное...
Сергей Борщ
Цитата(dxp @ Sep 8 2007, 13:12) *
Там (в EWAVR) можно разместить объект в секции TINY, которая размещается в младшем сегменте - в 8-разрядном адресном пространстве,
Увы, у этого процессора младшие 256 байт заняты регистрами периферии. Объем tiny памяти у него равен нулю, как и у меги48/88/168. В аналогичной ситуации программа, использующая tiny-память не переносится с меги8 на мегу88.
mdmitry
Возможно, поможет сократить время выполнения генерация кода прерывания не автоматическая (компилятором), а руками, т.е. использовать инструкцию аналогичную GCC NAKED, кажется, _raw для IAR.
SasaVitebsk
Всем спасибо. Буду осмысливать и пробовать - экспериментировать.

Цитата(Rst7 @ Sep 8 2007, 13:49) *
и отключаем для этого модуля кластеризацию переменных. Это освободит один индексный регистр и здорово сократит код. А вообще, наверное надо копать не от финальных процедур, а чуть выше, возможно, пересмотрев алгоритм генерации данных, можно будет сильно улучшить производительность... Если есть возможность, расскажите, какие данные вам надо лить в порты, попробуем придумать что-то оригинальное...


biggrin.gif Ещё раз спасибо за альтруизм и желание помочь, но я тогда что буду делать и за что деньги получать? Спасибо итак. Честно скажу есть у меня мысли. Появились точнее. Вас пока нагружать не буду, как "золотой запас". smile.gif
Rst7
Цитата(SasaVitebsk @ Sep 9 2007, 17:54) *
Всем спасибо. Буду осмысливать и пробовать - экспериментировать.
biggrin.gif Ещё раз спасибо за альтруизм и желание помочь, но я тогда что буду делать и за что деньги получать? Спасибо итак. Честно скажу есть у меня мысли. Появились точнее. Вас пока нагружать не буду, как "золотой запас". smile.gif


Ну так пришли же с вопросом, чего бы не помочь. А, глядишь, другим идея пригодится...
SasaVitebsk
Тут проскочила чья-то фраза, что компилятор лучше работает со структурами чем с массивами. И я решил проверить некоторые моменты моей проги.
Выводы просты - казалось бы мелочи, но они имеют важнейшее значение. И, главное, если вдуматься и проанализировать, то всё это лежит на поверхности!

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

uint16_t Speed[MAXDVG],SpeedReal[MAXDVG],State[MAXDVG],StateReal[MAXDVG];

Теперь переписал примерно так

struct
{
uint16_t Speed, SpeedReal...
} Dvg[MAXDVG];

там где идёт единичное обращение к конкретному полю конкретного двигателя, - естественно ничего не изменилось, но там где в цикле идёт работа с двигателями и сравниваются различные поля одного двигателя - там код уменьшился раза в два!

Переписал таки же образом работу с АЦП.

Если вдуматься, то это медленный дрейф к объектам и его свойствам и к С++ как дальнейшее развитие этих идей. smile.gif

Похоже надо более полно изучать С++ и постепенно переходить.
alexander55
Цитата(SasaVitebsk @ Sep 18 2007, 13:21) *
Похоже надо более полно изучать С++ и постепенно переходить.

Вы правы. Я шел к С++ так.
Ассемблеры - С - С с структурами - С++ (структуры с функциями) - С++(наследование, скрытие данных, полиформизм).
4 кита (шаблоны) я пока не очень понимаю, зачем он нужен.
Могу порекомендовать книги Шилда (это просто и четко).
vmp
Цитата(SasaVitebsk @ Sep 18 2007, 13:21) *
Тут проскочила чья-то фраза, что компилятор лучше работает со структурами чем с массивами.

Вообще-то это была вольная цитата из документа:
AVR035: Efficient C Coding for AVR:
Цитата
When global variables are required they should be collected in structures whenever
appropriate. This makes it possible for the C compiler to address them indirectly.

Для отдельных переменных компилятор каждый раз указывает полный адрес, тогда как для структуры достаточно загрузить адрес один раз, а затем использовать адресацию со смещением. Такая возможность определяется особенностями архитектуры AVR, а не самого языка Си.
rezident
Цитата(vmp @ Sep 18 2007, 16:45) *
Для отдельных переменных компилятор каждый раз указывает полный адрес, тогда как для структуры достаточно загрузить адрес один раз, а затем использовать адресацию со смещением. Такая возможность определяется особенностями архитектуры AVR, а не самого языка Си.

Извиняюсь конечно, но косвенная адресация (т.н. вами "адресация со смещением") не только одной лишь "архитектуре AVR" присуща. wink.gif
vmp
Цитата(rezident @ Sep 18 2007, 17:08) *
Извиняюсь конечно, но косвенная адресация (т.н. вами "адресация со смещением") не только одной лишь "архитектуре AVR" присуща. wink.gif

Не путаем два разных вида адресации. Косвенная (регистровая) - адрес равен значению регистра (пары регистров для AVR). Со смещением - сумме значения регистра (пары регистров) и константы (смещения). На примере PDP-11 (раз уж пошла речь о различных архитектурах):
Косвенная:
011200 MOV (R2),R0 ; Содержимое слова по адресу, равному значению R2 поместить в R0
Со смещением:
016200 MOV 4(R2),R0 ; Содержимое слова по адресу, равному значению R2 плюс 4 поместить в R0
000004
Особенность архитектуры AVR состоит в том, что в ней значение смещения помещается в код команды, которая занимает одно слово, что дает преимущество перед абсолютной адресацией, которая требует 2 слова.
Dog Pawlowa
Цитата(vmp @ Sep 18 2007, 16:35) *
Особенность архитектуры AVR состоит в том, что в ней значение смещения помещается в код команды, которая занимает одно слово, что дает преимущество перед абсолютной адресацией, которая требует 2 слова.

Все здорово, не затронут оказался компилятор. Как-то мне с трудом верится, что он оптимизирует так хорошо. Если бы он был таким умным, то мог бы вставлять команды с использованием смещения во время доступа к соседним ячейкам независимо от того, определены они как элементы структуры, или как разные переменные.
А если ему нужно помогать, используя указатель на структуру, это значит, нужно самому программисту заниматься оптимизацией.
Дальше только писать на ассемблере....
singlskv
Цитата(Dog Pawlowa @ Sep 18 2007, 20:30) *
Все здорово, не затронут оказался компилятор. Как-то мне с трудом верится, что он оптимизирует так хорошо. Если бы он был таким умным, то мог бы вставлять команды с использованием смещения во время доступа к соседним ячейкам независимо от того, определены они как элементы структуры, или как разные переменные.
К сожалению, разные переменные стоящие рядом в памяти могут оказаться
переменными из разных единиц компиляции (разных С файлов)
Цитата
А если ему нужно помогать, используя указатель на структуру, это значит, нужно самому программисту заниматься оптимизацией.
Дальше только писать на ассемблере....
Если хотите оптимизацию на данной конкретной платформе, то компилятору нужно
несомненно помагать, НО, вне зависимости от вашей помощи, решение останется переносимым вне
зависимости от используемой платформы...
IgorKossak
Цитата(Dog Pawlowa @ Sep 18 2007, 19:30) *
Все здорово, не затронут оказался компилятор. Как-то мне с трудом верится, что он оптимизирует так хорошо. Если бы он был таким умным, то мог бы вставлять команды с использованием смещения во время доступа к соседним ячейкам независимо от того, определены они как элементы структуры, или как разные переменные.
А если ему нужно помогать, используя указатель на структуру, это значит, нужно самому программисту заниматься оптимизацией.
Дальше только писать на ассемблере....

Если речь зашла о компиляторе IAR, то ему как раз и не нужно помогать рассовывая локальные (да и не только) переменные по структурам или массивам. Он и сам это прекрасно делает если ему задать оптимизацию Clustering of variables.
singlskv
Цитата(SasaVitebsk @ Sep 18 2007, 13:21) *
Если вдуматься, то это медленный дрейф к объектам и его свойствам и к С++ как дальнейшее развитие этих идей. smile.gif
Похоже надо более полно изучать С++ и постепенно переходить.
А вот здесь я бы на Вашем месте сильно не спешил.
Дело в том, что любой код на С++ может быть преренесен на С без потери скорости/компактности,
а обратное к сожалению не верно, те код на С перенесенный на С++ гарантированно будет
не меньше/не быстрее чем исходный.
Собственно часть компиляторов так и работает, считая код на С++ препроцессорной
надстройкой над С...
Хотя конечно иногда удобно, тока нужно четко себе представлять в каком месте будет оверхед.
zltigo
Цитата(singlskv @ Sep 18 2007, 21:41) *
Собственно часть компиляторов так и работает, считая код на С++ препроцессорной
надстройкой над С...

Загнули smile.gif.
singlskv
Цитата(zltigo @ Sep 18 2007, 23:34) *
Загнули smile.gif.
Неа, ни разу,
лет 15 назад на юниксах именно так и осуществлялась компиляция из С++ в С.
Да и по сути, современные компиляторы делают тоже самое, тока они нам уже
не показывают промежуточного результата в виде С листинга.
zltigo
Цитата(singlskv @ Sep 18 2007, 22:47) *
лет 15 назад..

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

Допустим, что именно "препроцессор", тогда с какой стати 'C' исходник откомпилированый таким "плюсовым" компилятором код будет хоть чем-то отличаться smile.gif
singlskv
Цитата(zltigo @ Sep 18 2007, 23:57) *
Дела давно минувших дней, допустим, какие-то компиляторы, которые авторы решили назвать С++...
Операционка там была IRIX(это на основе BSD), а компутеры были Silicon Graphics, ну
это те, которые еще плюшками балуются (суперкомпутерами) время от времени.
Может быть это конечно и не был С++ компилятор, но так назывался... biggrin.gif
Цитата
Допустим, что именно "препроцессор", тогда с какой стати 'C' исходник откомпилированый таким "плюсовым" компилятором код будет хоть чем-то отличаться smile.gif
А вот этого я и не утверждал,
имелась в виду не компиляция С исходника компилятором С++, а именно перенос алгоритма
на модель С++, и вот там оверхед неизбежен...
SasaVitebsk
Цитата(Dog Pawlowa @ Sep 18 2007, 19:30) *
Все здорово, не затронут оказался компилятор. Как-то мне с трудом верится, что он оптимизирует так хорошо. Если бы он был таким умным, то мог бы вставлять команды с использованием смещения во время доступа к соседним ячейкам независимо от того, определены они как элементы структуры, или как разные переменные.
А если ему нужно помогать, используя указатель на структуру, это значит, нужно самому программисту заниматься оптимизацией.
Дальше только писать на ассемблере....


А зря не верится. И я тоже не верил. Более того, когда писал на асме вообще не использовал данные команды. Считал их бесполезными. smile.gif Оно и понятно. Применение команд типа LDD где используется смещение делает прогу на асме фактически нечитаемой. К чему обращаешься - непонятно. Другое дело компилятор. Ему на читаемость результирующего листинга асм - наплевать. Он даже мнемонические имена в константы рубит. Зато автоматически расчитывает смещение.

Не смею утверждать, но по моему он даже делает анализ совместного применения переменных и размещает их рядом в памяти. Чтобы удобнее было.

Во всяком случае применение команд LDD и STD очень широкое.
dxp
Цитата(singlskv @ Sep 19 2007, 01:41) *
А вот здесь я бы на Вашем месте сильно не спешил.
Дело в том, что любой код на С++ может быть преренесен на С без потери скорости/компактности,
а обратное к сожалению не верно, те код на С перенесенный на С++ гарантированно будет
не меньше/не быстрее чем исходный.

smile.gif С++ на С не переносится в принципе, т.к. С является подмножеством С++ (хотя и не 100% совместимым). А вот С-код на С++ переносится достаточно легко, в некоторых случаях даже делать ничего не надо, он сразу работает. И по эффективности тут никаких потерь нет. Попробуйте какую-нибудь С программу, написанную под IAR С скомпилить тем же IAR'ом в С++ режиме. Сравните результат. smile.gif

Цитата(singlskv @ Sep 19 2007, 01:41) *
Собственно часть компиляторов так и работает, считая код на С++ препроцессорной
надстройкой над С...

Это вы про С с классами говорите, который компилировался С front. Ну, так это еще не полноценный С++, а его предтеча. Давайте уже оперировать реалиями сегодняшнего дня.

Цитата(SasaVitebsk @ Sep 19 2007, 06:46) *
А зря не верится. И я тоже не верил. Более того, когда писал на асме вообще не использовал данные команды. Считал их бесполезными. smile.gif Оно и понятно. Применение команд типа LDD где используется смещение делает прогу на асме фактически нечитаемой. К чему обращаешься - непонятно.

Ну, это как писать. Сделайте смещения именованными и все сразу станет понятно. Когда-то, когда я в целях тренировки кое-что еще писал на асм для AVR, делал так (писано было еще в 1998 году, пакет - незабвенный IAR 1.40 smile.gif ):

Код
#define LASER_CODE           0
#define TPREVIOUS_POINT      2
#define STATE                4
#define NSPURIOUS            5
#define NSYMBOLS             6
#define FLASER_CODE_DONE     7

  rseg    UDATA0

     Laser_code:         ds   2   ; 0-1
     tPrevious_point:    ds   2   ; 2-3
     State:              ds   1   ; 4
     nSpurious:          ds   1   ; 5
     nSymbols:           ds   1   ; 6
     fLaser_code_done:   ds   1   ; 7
...

  rseg    RCODE
    ...
    ldi   r30, low(Laser_code)
    ldi   r31,high(Laser_code)
    ...
    ldd   r18,Z+TPREVIOUS_POINT        ;  load previous point
    ldd   r19,Z+TPREVIOUS_POINT + 1    ;
    ...
    ldd   r18,Z+STATE                  ; load state
    ...
    ldd   r18,Z+LASER_CODE   ; load laser code
    ldd   r19,Z+LASER_CODE+1 ;
    ror   r19                ; modify laser code
    ror   r18                ;
    std   Z+LASER_CODE,r18   ; store laser code
    std   Z+LASER_CODE+1,r19 ;
    ...
    std   Z+TPREVIOUS_POINT,r20   ; store current point as previous
    std   Z+TPREVIOUS_POINT+1,r21 ;
                             ;
    ldd   r18,Z+NSYMBOLS     ;
    inc   r18                ; inc nSymbols
    std   Z+NSYMBOLS,r18     ;
    ...
    ldd   r18,Z+NSPURIOUS              ;  inc nSpurious
    inc   r18                          ;
    std   Z+NSPURIOUS,r18              ;
    ...


и т.д. Как видите, все вполне читаемо и понятно. Код оптимален. По сути тут руками сделана оптимизация Clustering variables.
Rst7
Цитата(IgorKossak @ Sep 18 2007, 21:07) *
Если речь зашла о компиляторе IAR, то ему как раз и не нужно помогать рассовывая локальные (да и не только) переменные по структурам или массивам. Он и сам это прекрасно делает если ему задать оптимизацию Clustering of variables.


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

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

А вот C++ - штука двоякая, главное достоинство - чуть меньше писанины, но есть и подводные камни. Например, в связи с ограниченностью количества индексных регистров возможно код с классами будет хуже - из-за неявного this, занимающего пару регистров.
alexander55
Цитата(Rst7 @ Sep 19 2007, 09:23) *
А вот C++ - штука двоякая, главное достоинство - чуть меньше писанины

Писанины поменьше - это не главное.
А главное, на мой взгляд, следующее:
- наращивание проекта при нормальном наследовании не ведет к его необозримости;
- все ошибки локализованы в объектах;
- простая корректировка.
С++ был создан для больших проектов. В С нужно в куче мест подправить, а в С++ в одном.
dxp
Цитата(Rst7 @ Sep 19 2007, 12:23) *
А вот C++ - штука двоякая, главное достоинство - чуть меньше писанины, но есть и подводные камни. Например, в связи с ограниченностью количества индексных регистров возможно код с классами будет хуже - из-за неявного this, занимающего пару регистров.

Отнюдь. В С код при обращении к структуре будет таким же - компилятор загрузит в pointer адрес структуры и будет обращаться со смещением. Ровно как и в случае с классом. Потому, что это эффективно. Если это менее эффективно - например, надо, скажем, всего одно член-данное прочитать и выгоднее обратиться напрямую, то ничего не мешает это сделать и в случае с С-структурой, и в случае с С++-классов. Это все зависит от компилятора, а не от языка.
Rst7
Цитата(dxp @ Sep 19 2007, 15:18) *
Отнюдь. В С код при обращении к структуре будет таким же - компилятор загрузит в pointer адрес структуры и будет обращаться со смещением. Ровно как и в случае с классом. Потому, что это эффективно. Если это менее эффективно - например, надо, скажем, всего одно член-данное прочитать и выгоднее обратиться напрямую, то ничего не мешает это сделать и в случае с С-структурой, и в случае с С++-классов. Это все зависит от компилятора, а не от языка.


ключевое слово в моем посте - "возможно".

Хуже будет если, например, размер класса больше максимально допустимого смещения в команде индексной адресации. Или нужен лишний указатель. И еще можно придумать сто вариантов. Или будет также как и на чистом Си. Лучше - не будет. Следовательно - плюс цпп - только уменьшение писаниы.
alexander55
Цитата(Rst7 @ Sep 19 2007, 16:34) *
ключевое слово в моем посте - "возможно".

Хуже будет если, например, размер класса больше максимально допустимого смещения в команде индексной адресации. Или нужен лишний указатель. И еще можно придумать сто вариантов. Или будет также как и на чистом Си. Лучше - не будет. Следовательно - плюс цпп - только уменьшение писаниы.

Если у бабушки были бы усы и борода - она была бы дедушкой. (Народная мудрость).
А серьезно, все может быть.
dxp
Цитата(Rst7 @ Sep 19 2007, 19:34) *
Хуже будет если, например, размер класса больше максимально допустимого смещения в команде индексной адресации. Или нужен лишний указатель. И еще можно придумать сто вариантов. Или будет также как и на чистом Си. Лучше - не будет. Следовательно - плюс цпп - только уменьшение писаниы.

C++ - это принципиально иной подход к программированию. При внешней схожести С и С++ очень разные языки. Прежде всего разные тем, что на них думать надо совсем по-разному (если, конечно, от ++ хочется получить то, что они дают, а не остаться в рамках процедурного С).
singlskv
Цитата(dxp @ Sep 19 2007, 08:18) *
smile.gif С++ на С не переносится в принципе, т.к. С является подмножеством С++ (хотя и не 100% совместимым). А вот С-код на С++ переносится достаточно легко, в некоторых случаях даже делать ничего не надо, он сразу работает. И по эффективности тут никаких потерь нет. Попробуйте какую-нибудь С программу, написанную под IAR С скомпилить тем же IAR'ом в С++ режиме. Сравните результат. smile.gif
Вы не внимательно читали мои посты, я говорил лишь о том что любую прогу
на С++ можно написать на С не менее эфективно(почти всегда более эфективно).
Правда, усилий может понадобится больше.
В конечном итоге это примерно такой же спор как насчет С vs ASM.
Очевидно что на ASM всегда можно написать лучше(быстрее/компактнее по коду) чем на С.
так же для меня вполне очевидно, что то же будет касаться С++ vs C.
Вопрос скорее в решаемой задаче и в количестве железных ресурсов которые
не жалко...
Ок, задам Вам такой вопрос:
как Вы относитесь к "прерываниям с классами" ?
Цитата
Это вы про С с классами говорите, который компилировался С front. Ну, так это еще не полноценный С++, а его предтеча. Давайте уже оперировать реалиями сегодняшнего дня.
Да, это действительно был Cfront в редакции AT&T кажись..., подзабылось уже...
Тока тогда он уже был С++ с практически всеми атрибутами современных версий,
по моему, исключений еще небыло в современном виде, а шаблоны уже были...
Вобщем здесь уже спорить не буду, не помню всех деталей...
zltigo
Цитата(singlskv @ Sep 19 2007, 22:59) *
Очевидно что на ASM всегда можно написать лучше(быстрее/компактнее по коду) чем на С.
так же для меня вполне очевидно, что то же будет касаться С++ vs C.

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

Когда в 80x годах я взялся за C++, причем за именно ++, а не 'С', то с моей первой программой приключилась такая история - я выиграл спор smile.gif. Потребовалась во времена IBM PC/XT для сугубо личных целей простенькая псевдографическая текстовая библиотечка. В общем ничего сверестественного открять окошко-(вывести текст) - еще несколько огошек, перекрытие, всплытие - все без особых затей. Ну и начал С++ изучать. Был у менгя парень, очень талантливый, уже несколько лет много пишущий на 'C' и виртуозно на ASM - он меня от плюсов отговаривал сильно, ну монстрально, тормознуто и т.д.... Я не отступал и он предложил мне доказать, написав библиотеку на 'C' и используя ее в одной и той-те тестовой программе убедиться в жуткой тормознутости C++. Причем в те времена 4,7 Mhz процессоров скорость прекрасно оченивалась невооруженным взглядом smile.gif.
За несколько недель, вечерами-урывками почти за одинаковое время были написаны две библиотеки.
Напоминаю, это была моя первая программа! Моя C++ была меньше по размеру исходников, меньше по размеру кода и работала быстрее. Причем при этом я Турбодебагером не пользовался - ну просто некогда было изучать smile.gif, да и само заработало, а коллега довольно плотно в отладке сидел. Случай, конечно, несколько вырожденный, ибо уж больно оконные задачи на плюсы хорошо легли... На самом деле обе работали очень медленно sad.gif Пошли вставки на ASM, коллега уязвленный потратил массу времени на вылизывание сишного варианта, все находки были реализованы, листинги вычитаны... В конце-концов библиотека стала сишной с очень большими вкраплениями ASM. Но в принципе переход на C был обусловлен изрядной тупостью первого Борландячего плюсового компилятора. Уже в более поздние времена работая с Watcom явных претензий к реализации С++ не было. Да и Борланд попозже исправился, до того, что я стал старые Борландячие проекты (да и вообще все) компилить плюсовым компилятором - эффективнее получалось!
singlskv
Цитата(zltigo @ Sep 20 2007, 01:03) *
Оба этих утверждения при имеющейся тенденциям к
- усложнению программ;
- уменьшению времени на их разработку;
- уменьшения времени жизни целевого железа;
- увеличении цены ошибки;
Становятся все больше и больше гипотетическими и все нереализуемее на практике.
Толку в гипотетических преимуществах, если за обозримое время и деньги их нельзя реализовать.
Вот с этим соглашусь практически полностью,
ну на 50% процентов как минимум,
нужно тока рассматривать это относительно существуещего железа,
С действительно уже практически заменил АСМ,
C++ Еще совсем нет. То есть его уже как бы вполне можно использовать, но тока
пока не в критичных по времени выполнения процедурах.
Повторюсь, Вы используете "Прерывания с классами"?
dxp
Цитата(singlskv @ Sep 20 2007, 02:59) *
Вы не внимательно читали мои посты, я говорил лишь о том что любую прогу
на С++ можно написать на С не менее эфективно(почти всегда более эфективно).
Правда, усилий может понадобится больше.

Я внимательно читал. И обратное утверждение верно.

Цитата(singlskv @ Sep 20 2007, 02:59) *
В конечном итоге это примерно такой же спор как насчет С vs ASM.

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

Цитата(singlskv @ Sep 20 2007, 02:59) *
Очевидно что на ASM всегда можно написать лучше(быстрее/компактнее по коду) чем на С.

Да. Хотя на практике случаи бывают и обратные.

Цитата(singlskv @ Sep 20 2007, 02:59) *
так же для меня вполне очевидно, что то же будет касаться С++ vs C.

См выше.

Цитата(singlskv @ Sep 20 2007, 02:59) *
Ок, задам Вам такой вопрос:
как Вы относитесь к "прерываниям с классами" ?

Что такое "прерывания с классами"?
alexander55
Цитата(singlskv @ Sep 20 2007, 01:27) *
Повторюсь, Вы используете "Прерывания с классами"?

Извините меня за тупость, но я что-то не понимаю.
Есть класс. Часть его функций используется не в прерываниях, часть в прерываниях. Я это применяю и проблем никогда не имею.
А что такое "прерывания с классами" я не могу понять (может что-то новенькое).
Непомнящий Евгений
Цитата(alexander55 @ Sep 20 2007, 08:58) *
А что такое "прерывания с классами" я не могу понять (может что-то новенькое).


Может имелся в виду вызов методов класса из прерываний?

Лично у меня очень часто, иногда - даже виртуальные smile.gif ...
singlskv
Цитата(Непомнящий Евгений @ Sep 20 2007, 15:42) *
Может имелся в виду вызов методов класса из прерываний?
Лично у меня очень часто, иногда - даже виртуальные smile.gif ...
Примерно это и имелось в виду.

А на каком проце вы вызываете виртуальные методы классов из прерывания ?

Видимо джиттер в Ваших задачах несущественен ?
dxp
Цитата(singlskv @ Sep 20 2007, 23:13) *
Примерно это и имелось в виду.

А на каком проце вы вызываете виртуальные методы классов из прерывания ?

Видимо джиттер в Ваших задачах несущественен ?

Какая связь между джиттером и виртуальными функциями классов?
Непомнящий Евгений
Цитата(singlskv @ Sep 20 2007, 20:13) *
А на каком проце вы вызываете виртуальные методы классов из прерывания ?

Видимо джиттер в Ваших задачах несущественен ?


Atmega128-1280-2560. Сейчас сижу на этой линейке.
Там, где он существенен, я делаю методы класса встраиваемыми (и не использую виртуальные) - и никакого джиттера smile.gif.
Тут надо соблюсти баланс между удобством и скоростью обработки. Там где скорость обработки может быть не сильно высокой, на первый план выходит удобство программирования и реюзабельность кода.

Что касается методов классов и виртуальных методов, то по сравнению с ф-циями С для первых оверхед - это лишний двухбайтовый аргумент, а для вторых - этот же аргумент + считывание указателя из флэша и переход по нему. К тому же, за счет передачи указателя, обычно сокращается число передаваемых аргументов - все необходимые данные уже есть в экземпляре класса.
Так что там, где допустима ф-ция С, вполне можно использовать и методы объектов С++.



Цитата(dxp @ Sep 21 2007, 07:49) *
Какая связь между джиттером и виртуальными функциями классов?


Джиттер == оверхед или я чего не понял?

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


Тока что прочитал: джиттер - нежелательные фазовые и/или частотные случайные отклонения передаваемого сигнала....
Тогда связь действительно малопонятна...
dxp
Цитата(Непомнящий Евгений @ Sep 21 2007, 11:36) *
Джиттер == оверхед или я чего не понял?

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

Цитата(Непомнящий Евгений @ Sep 21 2007, 11:36) *
Если так - то самая прямая - расходы на вызов виртуальной ф-ции больше, чем на вызов обычной ф-ции -члена ...

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

Частенько когда говорят об оверхеде виртуальных функций просто забывают, что сравнивают их при этом с обычными функциями. А это неверно, т.к. это разные по функциональности вещи. Сравнивать надо с таблицами указателей на функции и косвенным вызовом обычных функций. И тут оверхеда по быстродействию как-то и не видно.

Оверхед по памяти действительно есть - это необходимость хранения vptr в объекте класса, но это реально очень небольшой оверхед по памяти - один указатель.

P.S. В терминах С++ термин "метод" является синонимом термина "виртуальная функция". Т.ч. невиртуальные фукнции - это не методы, это просто функции-члены. Предлагаю придерживаться этой терминологии, дабы не вносить путаницы. smile.gif
Непомнящий Евгений
Цитата(dxp @ Sep 21 2007, 09:50) *
P.S. В терминах С++ термин "метод" является синонимом термина "виртуальная функция". Т.ч. невиртуальные фукнции - это не методы, это просто функции-члены. Предлагаю придерживаться этой терминологии, дабы не вносить путаницы. smile.gif


Я всегда считал, что функция-член == метод, а вирт. ф-ция-член == вирт. метод. Но не буду спорить beer.gif

А насчет оверхеда виртуальных функций можно посмотреть вот с какой стороны:
чтобы сделать эквивалентное в С и минимизировать ручную работу и ошибки, надо хорошо продумать стратегию + возможно реализовать несколько макросов + все равно останется масса ручной работы. Поэтому в С "виртуальные" функции используются только там, где это действительно нужно и после долгих размышлений об альтернативных вариантах. С другой стороны, в С++ достаточно написать слово virtual, что несколько развращает. Поэтому возникает желание запихивать виртуальные ф-ции во все дырки. Соответственно, растет и оверхед biggrin.gif
alexander55
Цитата(dxp @ Sep 21 2007, 09:50) *
P.S. В терминах С++ термин "метод" является синонимом термина "виртуальная функция". Т.ч. невиртуальные фукнции - это не методы, это просто функции-члены. Предлагаю придерживаться этой терминологии, дабы не вносить путаницы. smile.gif

Я уверен, никто, кто работает с С++, не запутается, а если человек работает с С - то он даже не обратит на это внимания.
PS. Я в ОС-ветке задал вопросик. Ответьте, если не трудно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.