Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Никак не обмануть gcc 4.1.2
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
demiurg_spb
Пытаюсь оптимизировать процедуру очистки дисплея, но gcc не поддаётся дрессуреsmile.gif
Оптимизация: Os.
Код
#define LCD_WIDTH             128U
#define LCD_HEIGHT            64U
#define LCD_STR_HEIGHT     8U

unsigned char lcd_vram[LCD_WIDTH][LCD_HEIGHT/LCD_STR_HEIGHT];    // Буфер видеопамяти LCD.

//====================================
void lcd_clrscr(void)
{
    unsigned char*p = &lcd_vram[0][0];

#if (((LCD_WIDTH*LCD_HEIGHT/LCD_STR_HEIGHT)/8)>255)
# error "(sizeof(lcd_vram)/8)>255 !!!"
#endif

    register unsigned char i = sizeof(lcd_vram)/8;
    do
    {
        *p++ = 0; // 1
        *p++ = 0; // 2
        *p++ = 0; // 3
        *p++ = 0; // 4
        *p++ = 0; // 5
        *p++ = 0; // 6
        *p++ = 0; // 7
        *p++ = 0; // 8
    }
    while (--i);
}


Получаю листинг такого вида:
Код
lcd_clrscr:
        ldi r30,lo8(lcd_vram)
        ldi r31,hi8(lcd_vram)
.L2:  
        st Z,__zero_reg__
        std Z+1,__zero_reg__
        std Z+2,__zero_reg__
        std Z+3,__zero_reg__
        std Z+4,__zero_reg__
        std Z+5,__zero_reg__
        std Z+6,__zero_reg__
        std Z+7,__zero_reg__
        adiw r30,8
        ldi r24,hi8(lcd_vram+1024)
        cpi r30,lo8(lcd_vram+1024)
        cpc r31,r24
        brne .L2
        ret

Мне никак не удаётся заставить gcc использовать однобайтный счётчик цикла, отказаться от adiw и использовать st+,__zero_reg__. Так код стал бы и короче и быстрее...

И попутно вопрос №2:
Я считал, что static const является альтернативой define.
Но, видимо, я заблуждался...

Код
static const unsigned char MY_ARRAY_SIZE = 2;
//#define MY_ARRAY_SIZE 2

unsigned char my_array[MY_ARRAY_SIZE] = {0,0};

получаю сообщение:
../../../arclib/common/lcd128x64.c:10: error: variably modified 'my_array' at file scope
../../../arclib/common/lcd128x64.c:10: warning: excess elements in array initializer
../../../arclib/common/lcd128x64.c:10: warning: (near initialization for 'my_array')

А если через #define MY_ARRAY_SIZE 2 - то всё ок.

Знающие люди, объясните такое поведение.
Спасибо!
AHTOXA
Цитата(demiurg_spb @ Aug 15 2008, 17:45) *
Пытаюсь оптимизировать процедуру очистки дисплея, но gcc не поддаётся дрессуреsmile.gif


Ну значит не умеетsmile.gif Если так нужны эти такты, то проще не дрессировать гцц, а использовать инлайн - ассемблер:

Код
void lcd_clrscr(void)
{
    unsigned char *p = &lcd_vram[0][0];
    unsigned char i = 128;
    __asm__ __volatile__
        ( "L_%=: "
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "st %a0+, __zero_reg__\n\t"
          "subi    %1, 1\n\t"
          "brne    L_%= \n\t"
        :
        : "z" (p), "r" (i)
        );
}


Как-то так.
rezident
Цитата(demiurg_spb @ Aug 15 2008, 17:45) *
И попутно вопрос №2:
Я считал, что static const является альтернативой define.
Но, видимо, я заблуждался...
Конечно заблуждаетесь! #define это команда препроцессора, а не компилятора. По ней он всего лишь делает тупую подстановку и никаких проверок (соответствия типа, например) не происходит.
static const на разных архитектурах может интерпретироватся по-разному. Где-то под переменную будет выделена отдельная ячейка в ОЗУ, где-то константа будет размещена во Flash. Но в любом случае
static определяет класс переменных, констант и функций, "область видимости" которых ограничена текущим модулем или функцией.
const указывает на то, что переменная в текущем модуле или функции явным образом не модифицируется. Значение переменной типа const (если она в ОЗУ была расположена) можно изменять либо явным приведением типа, либо неявно, через указатель.

В вашем примере случай
Код
#define MY_ARRAY_SIZE 2
unsigned char my_array[MY_ARRAY_SIZE] = {0,0};

преобразуется препроцессором в
Код
unsigned char my_array[2] = {0,0};

А тот который дает ошибку
Код
static const unsigned char MY_ARRAY_SIZE = 2;
unsigned char my_array[MY_ARRAY_SIZE] = {0,0};

означает попытку объявить переменную my_array типа unsigned char и одновременно присвоить одной переменной со смещением равным значению MY_ARRAY_SIZE сразу два значения. Естественно, что компилятор обалдевает от такой наглости и выдает вам ошибку. biggrin.gif
Vokchap
Цитата(rezident @ Aug 15 2008, 18:10) *
static определяет класс переменных, констант и функций, "область видимости" которых ограничена текущим модулем или функцией.

Время жизни переменной также определяет (для полноты определения). smile.gif smile.gif
_Pasha
У Вас sizeof(lcd_vram) 16-битное. Избавьтесь от 16-ти битного представления - вот и вся дрессура smile.gif Только мне кажется, это компилер Вас дрессирует smile.gif Пытаеццо.
rezident
Цитата(Vokchap @ Aug 15 2008, 21:25) *
Время жизни переменной также определяет (для полноты определения). smile.gif smile.gif
Не время жизни, а область размещения. При "удачном" стечении обстоятельств локальная переменная может на стеке прожить столько же, сколько программа работает smile.gif
demiurg_spb
Цитата(_Pasha @ Aug 15 2008, 20:06) *
У Вас sizeof(lcd_vram) 16-битное. Избавьтесь от 16-ти битного представления - вот и вся дрессура smile.gif Только мне кажется, это компилер Вас дрессирует smile.gif Пытаеццо.


Если я пишу

Код
unsigned char i=sizeof(lcd_vram)/8;


то разрядность sizeof(lcd_vram) не имеет никакого значения, т.к. переменная i явно объявлена как байт.


Спасибо за разъяснения про static const.
На асме вставки делать не буду из религиозных соображений.
Хочется чтоб gcc становился ещё умнее...
_Pasha
Только что глянул код и листинг еще раз, и увидел:
оптимизатор исключил переменную i, воспользовавшись ZL:ZH как регистром базы и, однвременно, переменной цикла. А если так, то все попытки ввести "дрессурой" перем. цикла в явном виде, порождают менее эффективный код. Вывод : ни о чем беспокоиться не надо. Компилер сделал правильно.
AHTOXA
Цитата(_Pasha @ Aug 15 2008, 23:27) *
все попытки ввести "дрессурой" перем. цикла в явном виде, порождают менее эффективный код.


А из моего асмового варианта получилось:
Код
    278c:    80 e8           ldi    r24, 0x80; 128
    278e:    e7 e6           ldi    r30, 0x67; 103
    2790:    f0 e0           ldi    r31, 0x00; 0

00002792 <L_22>:
    2792:    11 92           st    Z+, r1
    2794:    11 92           st    Z+, r1
    2796:    11 92           st    Z+, r1
    2798:    11 92           st    Z+, r1
    279a:    11 92           st    Z+, r1
    279c:    11 92           st    Z+, r1
    279e:    11 92           st    Z+, r1
    27a0:    11 92           st    Z+, r1
    27a2:    81 50           subi    r24, 0x01; 1
    27a4:    b1 f7           brne    .-20     ; 0x2792 <L_22>


Это менее эффективно?
_Pasha
Цитата(AHTOXA @ Aug 15 2008, 21:57) *
Это менее эффективно?

А Вы посчитайте - если не ошибаюсь, Вы выиграли 2 такта и/или 3 слова программ. smile.gif
Зачем полировать то, что может быть гораздо шершавее ?
Другой вопрос, что на сях корявенько - через switch(lcd_state){...} состояния делаются, на асме через упрощенную кооперативную многопоточность куда проще и даже читабельнее, но это оффтоп.
AHTOXA
Цитата(_Pasha @ Aug 16 2008, 01:39) *
А Вы посчитайте - если не ошибаюсь, Вы выиграли 2 такта и/или 3 слова программ. smile.gif


Мелочь, но приятно:-) И потом, это же в цикле, выигрыш в тактах надо умножить на 128 :-)

Цитата
Зачем полировать то, что может быть гораздо шершавее ?


Тут я полностью с вами согласен, я этим обычно не занимаюсь. Просто подумал, раз автор темы спрашивает, то может ему это действительно важно? (Оказалось - нет, ему религия важнее smile.gif )

Цитата
Другой вопрос, что на сях корявенько - через switch(lcd_state){...} состояния делаются, на асме через упрощенную кооперативную многопоточность куда проще и даже читабельнее, но это оффтоп.


Не, никакая красота не вернёт меня в лоно ассемблера:-)
demiurg_spb
Цитата(_Pasha @ Aug 15 2008, 23:39) *
А Вы посчитайте - если не ошибаюсь, Вы выиграли 2 такта и/или 3 слова программ. smile.gif

Выигрыш 3 такта и 3 слова, т.к. adiw выполняется за 2 такта.
Итог для этого случая 128*3=384 такта,
а если циклов 256, то 768 тактов....

Цитата(_Pasha @ Aug 15 2008, 23:39) *
Зачем полировать то, что может быть гораздо шершавее ?

А действительно, зачем вообще что-либо делать?
Ведь можно продолжать смотреть диафильмы на проекторе с ручным приводом,
а DVD и HDTV - это не для нас, потому что мы не в состоянии поднять FPS... smile3046.gif
Когда компилятор считает себя умнее программиста - это немного раздражает...
aesok
Цитата(demiurg_spb @ Aug 17 2008, 18:56) *
Выигрыш 3 такта и 3 слова, т.к. adiw выполняется за 2 такта.
Итог для этого случая 128*3=384 такта,


Допустим что фрейм буфер очищается 25 раз в секунду, это значит что выигрыш
25 * 384 = 9600 тактов в секунду, или примерно 0.1% при тактовой частоте 10МГц.

Цитата
а если циклов 256, то 768 тактов....


0.2%

Цитата
А действительно, зачем вообще что-либо делать?
Ведь можно продолжать смотреть диафильмы на проекторе с ручным приводом,


Код генерируемый GCC 15 инструкций, Ваш 13 - выигрыш по размеру примерно 20%.
Цикл генерируемый GCC выполняется за 23 такта, Ваш за 20, выигрыш 15%.

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

Как часто Вам надо очищать буфер в этой программе?

Анатолий.
zltigo
Цитата(aesok @ Aug 18 2008, 00:45) *
Как часто Вам надо очищать буфер в этой программе?

Самое смешное, что буфер обычно очищать не нужно, нужно очищать хвосты оставшиеся от старой информации. Очитить все и занести заново это лобовой алгоритм, который не то, что не нужно вылизывать, но и использовать не следует.
demiurg_spb
Цитата(aesok @ Aug 18 2008, 02:45) *
Конечно компилятор сработал не идеально, но про диафильмы - это уже
литературное преувеличение.

Ну куда же без этогоsmile.gif
Но сути не меняет.

Цитата(aesok @ Aug 18 2008, 02:45) *
Как часто Вам надо очищать буфер в этой программе?

Всегда 2 раза в секунду,
иногда до 10 раз в секунду +
на каждое действие пользователя.

Этот же алгоритм выводит подготовленную страницу из SRAM в ОЗУ контроллера дисплея...
Да и вообще циклов в программе просто немеряно...
Но что это меняет?
Я просто обратил внимание на узкое место в циклах и на мой взгляд на это стоит обратить внимание уважаемым разработчикам ГЦЦ и ничего более...
Я сам иногда имею сильное желание приобщится к этому процессу, но в силу разных причин не получается...

Цитата(zltigo @ Aug 18 2008, 02:53) *
Самое смешное, что буфер обычно очищать не нужно, нужно очищать хвосты оставшиеся от старой информации. Очитить все и занести заново это лобовой алгоритм, который не то, что не нужно вылизывать, но и использовать не следует.

В моём случае именно этот алгоритм меня полностью удовлетворяет.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.