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

ARM7 сравнение компиляторов

Если разница в скорости 10%, а код больше на 40%, то это библиотеки или вы не отключили функции дебагера.

Нет дебагер и библиотеки здесь не причем! Я вот по map файлу смотрел, да и библиотеки у меня IARоввские были! GNU действительно генерирует большой код, но производительность от этого не так сильно страдает, а вот как видно в тумбе даже быстрее.

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


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

А вот для Cortex-M3 IAR 5.40 генерит очень медленный код (даже на максимальной оптимизации).

 

Листинг в студию.

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


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

Листинг в студию.

 

http://tnkernel.com/downloads/tnkernel-2-5...xM3-LPC1766.zip

 

This ZIP archive contains:

 

 

- A latest TNKernel Cortex-M3 port version (2.5.3)

- An examples(source code) for the NXP© LPC1766 MCU

- A projects for the Rowley CrossWorks Studio 1.7, Keil RVC v.3.xx, IAR ARM v.5.xx, GCC 4.3.2 (Codesourcery 2008q3-66)

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


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

На примере простого кода, работы с битовыми полями, я его уже давно как то выкладывал, для версий 4.42, 5.11 и RVCT (уже не помню какого)

struct bf_s {
    unsigned f1:6;
    unsigned f2:2;
    unsigned f3:20;
    unsigned f4:4;
} bf;

void short_clear(void)
{
  bf.f2 = 0;
}

void short_set(void)
{
  bf.f2 = 3;
}

void short_const(void)
{
  bf.f2 = 1;
}

void short_var(unsigned val)
{
  bf.f2 = val;
}

void long_clear(void)
{
  bf.f3 = 0;
}

void long_set(void)
{
  bf.f3 = (1<<20)-1;
}

void long_const(void)
{
  bf.f3 = 1;
}

void long_var(unsigned val)
{
  bf.f3 = val;
}

void long_var_if(unsigned val,unsigned cond)
{
  if (cond) bf.f3 = val;
  else bf.f4=val;
}

unsigned a;
unsigned b;
unsigned c;
void var_if(unsigned val)
{
  if (val) a = c;
  else b = c;
  c = val;
}

Сейчас скомпилил последним иаром V5.40.2.51604/W32

Улучшений серьезных нет :(

BFI и условное выполнение до сих пор IAR делать не умеет!

Для примера листинг одной функции

RVCT -O3 -Otime --cpu Cortex-M3
long_var_if PROC
        LDR      r3,|L1.176|
        CMP      r1,#0
        LDR      r2,[r3,#0]
        ITE      EQ
        BFIEQ    r2,r0,#28,#4
        BFINE    r2,r0,#8,#20
        STR      r2,[r3,#0] ; bf
        BX       lr
        ENDP


IAR --cpu Cortex-M3 -Ohs
long_var_if:
            LDR.N    R2,??DataTable13 ;; bf
            LDR      R3,[R2, #+0]
            CBZ      R1,??long_var_if_0
            LDR.N    R1,??DataTable14 ;; 0xf00000ff
            ANDS     R1,R1,R3
            LDR.N    R3,??DataTable15 ;; 0xfffff00
            AND      R0,R3,R0, LSL #+8
            ORRS     R0,R0,R1
            B.N      ??long_var_if_1
??long_var_if_0:
            LSLS     R1,R3,#+4
            LSRS     R1,R1,#+4
            ORR      R0,R1,R0, LSL #+28
??long_var_if_1:
            STR      R0,[R2, #+0]

            BX       LR

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


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

 

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

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


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

На примере простого кода, работы с битовыми полями, я его уже давно как то выкладывал, для версий 4.42, 5.11 и RVCT (уже не помню какого)

 

Еще раз посомтрел листинги, и заодно GNUC попробовал.

GNUC - BFI использует! А вот условное выполнение нет :(.

А вот новый IAR оказывается умеет иногда использовать условное выполнение и почему не всегда - загадка, вот листинги последней функции

GNUC gcc version 4.3.3 (Sourcery G++ Lite 2009q1-161)  -O3
var_if:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    push    {r4}
    mov    r4, r0
    cbnz    r0, .L29
    movw    r1, #:lower16:c
    movt    r1, #:upper16:c
    movw    r2, #:lower16:b
    ldr    r3, [r1, #0]
    movt    r2, #:upper16:b
    str    r3, [r2, #0]
.L27:
    str    r4, [r1, #0]
    pop    {r4}
    bx    lr
.L29:
    movw    r1, #:lower16:c
    movt    r1, #:upper16:c
    movw    r2, #:lower16:a
    ldr    r3, [r1, #0]
    movt    r2, #:upper16:a
    str    r3, [r2, #0]
    b    .L27

IAR

var_if:
            LDR.N    R1,??var_if_0   ;; a
            LDR      R2,[R1, #+8]
            CMP      R0,#+0
            ITE      NE
            STRNE    R2,[R1, #+0]
            STREQ    R2,[R1, #+4]
            STR      R0,[R1, #+8]

            BX       LR              ;; return


RVCT 
var_if PROC
        LDR      r2,|L1.176|
        CMP      r0,#0
        LDR      r1,[r2,#0xc]
        ITE      EQ
        STREQ    r1,[r2,#8] ; b
        STRNE    r1,[r2,#4] ; a
        STR      r0,[r2,#0xc] ; c
        BX       lr
        ENDP

 

как видно еще GNUC по другому подходит к загрузке адреса в регистр!

Но IMHO это неоптимально, вместо одной 16 битной инструкции и 32 бит данных используется две 32 битных команды.

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


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

как видно еще GNUC по другому подходит к загрузке адреса в регистр!

Но IMHO это неоптимально, вместо одной 16 битной инструкции и 32 бит данных используется две 32 битных команды.

 

Это для скорости, так наверное конвейер не слетает:) При -Os gcc грузит адрес одной инструкцией.

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


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

Генерит GCC условные команды IT

я взял асм своего проека где Stm32 UsbLib использую - там вот обнаружился такой кусок к примеру

 

void ClearDTOG_RX(uint8_t bEpNum)

{

800160e: b2c0 uxtb r0, r0

_ClearDTOG_RX(bEpNum);

8001610: f853 2020 ldr.w r2, [r3, r0, lsl #2]

8001614: f412 4f80 tst.w r2, #16384 ; 0x4000

8001618: bf1f itttt ne

800161a: f853 1020 ldrne.w r1, [r3, r0, lsl #2]

800161e: f648 728f movwne r2, #36751 ; 0x8f8f

8001622: 400a andne r2, r1

8001624: f442 4280 orrne.w r2, r2, #16384 ; 0x4000

8001628: bf18 it ne

800162a: f843 2020 strne.w r2, [r3, r0, lsl #2]

}

800162e: 4770 bx lr

8001630: 40005c00 .word 0x40005c00

 

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

struct bf_s {
    unsigned f1:20;
    char f2:8;
    char f3:8;
    unsigned f4:20;
} bf;

void long_var_if(char val,char cond)
{
  if (cond) 
   {
      bf.f3 = val;
      bf.f1 = val;
      bf.f2 = val;
   }
  else 
      bf.f4=val;
}

char var_if(char a ,  char b  )
{
  char x = a; 
  if (a)
    {
      x = b;
    }  
  return x;  
}

 

arm-kgp-elf-gcc -mthumb -march=armv7-m -mcpu=cortex-m3 -S -O3 test2.c

 

результат:

    .syntax unified
    .thumb
    .file    "test2.c"
    .text
    .align    2
    .global    long_var_if
    .thumb
    .thumb_func
    .type    long_var_if, %function
long_var_if:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    tst    r1, #255
    itete    ne
    ldrne    r3, .L4
    ldreq    r3, .L4
    ldrne    r2, [r3, #0]
    ldreq    r2, [r3, #4]
    uxtb    r0, r0
    itett    ne
    bfine    r2, r0, #0, #20
    bfieq    r2, r0, #8, #20
    strne    r2, [r3, #0]
    strbne    r0, [r3, #4]
    ite    ne
    strbne    r0, [r3, #3]
    streq    r2, [r3, #4]
    bx    lr
.L5:
    .align    2
.L4:
    .word    bf
    .size    long_var_if, .-long_var_if
    .align    2
    .global    var_if
    .thumb
    .thumb_func
    .type    var_if, %function
var_if:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    uxtb    r1, r1
    uxtb    r0, r0
    cmp    r0, #0
    ite    ne
    movne    r0, r1
    moveq    r0, #0
    bx    lr
    .size    var_if, .-var_if
    .comm    bf,8,4
    .ident    "GCC: (GNU) 4.5.0 20091029 (experimental)"

 

из сего видно что GCC прекрасно генерит BFI и IT команды. Другое дело что не ВСЕГДА КОГДА НАМ ЭТО ОЧЕВИДНО! полазив по исходникам компилера я увидел что допустим IT или переходы, например BNE являются для него алтернативами в зависимоти от ситуации принимается решение генерить то или иное. Если учесть что IT способно обойти и загрузить конвеер Cortex'а не более чем на 4 инструкции то становится понятным почему короткий if компиллер генерит через IT а более длинный с его точки зрения (n>4) генерится переход. Есть желание думать что скорость нада проверять а не наличие тех или иных команд.

 

собсно примерно так.

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


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

из сего видно что GCC прекрасно генерит BFI и IT команды. Другое дело что не ВСЕГДА КОГДА НАМ ЭТО ОЧЕВИДНО! полазив по исходникам компилера я увидел что допустим IT или переходы, например BNE ...

да это все известно и из описания архитектуры, для этого лезть в исходники компилера не надо!

в моем варианте функции var_if - GNUC явно полез оптимизировать нетуда! в итоге функция неимоверно больше по размеру, использует стек! и явно медленне работает!

Про BFI - я сразу говорил что гнусь это умеет! Вот что что а а все подобные иснтуркции гнусь всегда задействует. у IAR с BFI проблема пока.

 

По поводу Вашей функции var_if - вообще смешно!!!

 

RVCT - ее скомпилировал так

var_if PROC
        CMP      r0,#0
        IT       NE
        MOVNE    r0,r1
        BX       lr
        ENDP

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

 

листинг первой функции, выкладывать не буду, но он тоже оптимальнее и код красивее.

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


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

обычно компиляторы несравниваются по наличию ошибок, по тому какой суппорт удается получить и как быстро компилятор переползает на новую архитектуру, пока не начинаются подмены ресурсов на публичных сайтах public domain или близкие к нему GNU ресурсы обычно достаточно надежны, впрочем наверное начинать писать свой кроскомпилятор уже наверное можно, судя

по количеству несанкционированного доступа к инет реурсам.

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


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

Полуоффтоп:

Не, ну таки странности у gcc проскакивают ещё те.

Чистая культура кода:

uint8_t pos(uint8_t num) // num = 0..8
{
    uint8_t pos = 0x40;
    while(num >= 3) {
        pos += 0x40;
        num -= 3;
    }
    pos += num * 7;
    return pos;
}

avr-gcc 4.3.2 (WinAVR-20090313) умудрился это дело скомпилировать так, как если бы было написано

uint8_t pos(uint8_t num)
{
    return (num/3+1)*0x40 + (num % 3)*7;
}

Он, конечно, правильно понял, что происходит, но назачем в этой короткой функции два вызова __udivmodqi4 ???

Более ранние скомпилировали что написано.

Что интересно, эта 20090313 на объёме в около 5.5кБ несмотря на финт с делением таки дала код на сотню байт короче, чем 20071221.

И 4.4.0 (Klen-20090207 и 20090323) тоже обошлись без /%, в этой конкретной функции ещё и аккуратнее регистры разложили и общий код ещё почти на сотню байт уменьшили.

"но осадок остался"

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


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

Выкладываю несколько результатов бенчмарков. мож кому будет еще интересно... Проц STM32F105RC

 

gcc-4.7.1-RC -O2
sz=185024
MP3 Time: 41
TWF Time: 20
CRC32 Time: 12

gcc-4.7.1-RC -O3 -fno-tree-vectorize
sz=202516
MP3 Time: 41
TWF Time: 19
CRC32 Time: 6

gcc-4.6.4-PRE -O2
sz=186076
MP3 Time: 43
TWF Time: 25
CRC32 Time: 12

gcc-4.6.4-PRE -O3 -fno-tree-vectorize
sz=200312
MP3 Time: 43
TWF Time: 25
CRC32 Time: 7

rvct 4.1 894 -O2 -Otime
sz=180016
MP3 Time: 39
TWF Time: 23
CRC32 Time: 16

rvct 4.1 894 -O3 -Otime
sz=183440
MP3 Time: 38
TWF Time: 19
CRC32 Time: 11

armcc 5.01.64 -O2 -Otime
sz=180008
MP3 Time: 39
TWF Time: 23
CRC32 Time: 16

armcc 5.01.64 -O3 -Otime
sz=183432
MP3 Time: 38
TWF Time: 19
CRC32 Time: 10

gcc-4.6.1 (Sourcery CodeBench Lite 2011.09-69) -O2
sz=202708
MP3 Time: 41
TWF Time: 26
CRC32 Time: 7

gcc-4.6.1 (Sourcery CodeBench Lite 2011.09-69) -O3
sz=213940
MP3 Time: 38
TWF Time: 26
CRC32 Time: 7

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


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

Ни один компилятор не смог нормально соптимизировать простые очевидные вещи..

class tormoz{
public:
    void remove();
private:
    tormoz *next;
    tormoz *prev;
};

void tormoz::remove(){
    next->prev=prev;
    prev->next=next;
}

 

rvct -O3 -Otime

00000134 <_ZN6tormoz6removeEv>:
134:    e9d0 1200     ldrd    r1, r2, [r0]
138:    604a          str    r2, [r1, #4]
13a:    e9d0 1000     ldrd    r1, r0, [r0]        ;<<<---- Зачем??
13e:    6001          str    r1, [r0, #0]
140:    4770          bx    lr

 

gcc-4.7.1 -O3 -Otime

00000000 <_ZN6tormoz6removeEv>:
   0:    6803          ldr    r3, [r0, #0]
   2:    6842          ldr    r2, [r0, #4]
   4:    605a          str    r2, [r3, #4]
   6:    6842          ldr    r2, [r0, #4]        ;<<<---- Зачем??
   8:    6013          str    r3, [r2, #0]
   a:    4770          bx    lr

 

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

void tormoz::remove(){
    tormoz *n=next,*p=prev;
    n->prev=p;
    p->next=n;
}

   0:    e890 000c     ldmia.w    r0, {r2, r3}
   4:    6053          str    r3, [r2, #4]
   6:    601a          str    r2, [r3, #0]
   8:    4770          bx    lr

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


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

Ни один компилятор не смог нормально соптимизировать простые очевидные вещи..

Очевидно же:

После выполнения

    next->prev=next;

Следующаяя строка кода может работать с совсем ДРУГОЙ памятью, нежели ДО выполнения предыдущей строчки. Оптимизатор просто не имеет права ничего удалять.

    prev->next=prev;

 

В данном случае всё указано однозначно.

void tormoz::remove(){
    tormoz *n=next,*p=prev;
    n->prev=p;
    p->next=n;
}

 

 

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


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

Следующаяя строка кода может работать с совсем ДРУГОЙ памятью, нежели ДО выполнения предыдущей строчки. Оптимизатор просто не имеет права ничего удалять.

Интересно с какой?

Если будет next==prev всеравно поля next->prev,prev->prev и next->next,prev->next будут иметь разные адреса.

Если next==prev==this тогда this->prev=this; this->next=this;

Чет никак не могу придумать, как можно добится разного поведения от этих двух разных реализаций...

 

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

    next->prev=prev;
    prev->next=next;

11a:    e9d0 1200     ldrd    r1, r2, [r0]
11e:    604a          str    r2, [r1, #4]
120:    e9d0 1000     ldrd    r1, r0, [r0]
124:    6001          str    r1, [r0, #0]

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


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

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

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

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

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

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

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

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

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

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