Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ARM7 сравнение компиляторов
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
KRS
компиляторы -
IAR V5.40.2.51604/W32
RVCT4.0 [Build 650]
(Sourcery G++ Lite 2009q1-161) 4.3.3

Задача:
распаковка LZMA из внутренней Flash, только распаковка, данные никуда не отсылаются.
запакованный размер - 244625
распакованный - 605992
размер словаря 8 кб
данные - прошивка от блекфина.
оптимизация максимальная по скорости

Платформа:
ARM - LPC2138
частота 50 Mhz MAM включен

размер кода (только декодер)
Код
       IAR    RVCT  GNUC
ARM    3032   3284  3844
THUMB  2372   2514  2844


Время выполнения в ms (измерял с помощью таймера с прескалером 0)
код выполнялся из памяти
Код
       IAR    RVCT  GNUC
ARM    1754   1737  2212
THUMB  2720   2761  2494


код из флеша
Код
       IAR    RVCT  GNUC
ARM    1954   1950  2323
THUMB  2870   2942  2738


В тумбе IAR генерит более быстрый и компактный код по сравнению с RVCT.
В ARM из flash разница в скорости ~0.2% что несущественно, а вот код ~8% компактнее.

С GNUC - интересно в тумбе код большой, но более быстрый. Посмотрел по листингу - это связано с тем что GNUC в тумбе использует r8, r9 ( r10,r11 не использовал)

PS - общий проект в IAR, разными компиляторами компилировал только функцию декодера, а т.к. все EABI, то IARовсикй линкер без проблем жрал. Поэтому измерение времени для разных компилеров корректно - именно время распаковки.
Sunder_RUS
Вот ещё тестирование.
http://www.phyton.ru/pages/page44.html
Rst7
Цитата
провел небольшое исследование


Голые цифры - это не интересно. Код в студию, тонкие моменты из листингов - тоже.

Хотя лично мне с гнусем все ясно - теперь это компилятор для x86, а жаль, задумка была отличная.
KRS
Цитата(Rst7 @ Oct 27 2009, 23:53) *
Голые цифры - это не интересно. Код в студию, тонкие моменты из листингов - тоже.

LZMA брал отсюда
http://www.7-zip.org/sdk.html версия 4.65
отлично компилится и под win и под arm
для распаковки нужны только 3 файла LzmaDecode.* и Types.h

реально используется функция LzmaDecodeReal ( потому что случай вырожденный есть все запакованные данные и распаковка сразу целиком по словарю блоками по 8 кб)
размеры приведены как раз именно функции LzmaDecodeReal

Цитата(Rst7 @ Oct 27 2009, 23:53) *
Хотя лично мне с гнусем все ясно - теперь это компилятор для x86, а жаль, задумка была отличная.

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


Если кому интересно, могу потом (надо доработать немного) выложить код обретки LzmaDecodeReal для вырожденного случая, когда все данные для распаковки доступны в адресном пространсве, а распаковка идет либо сразу целиком, либо кусками по размеру словаря.
размер кода виден выше, скорость тоже. Пакует лучше deflate на 30% примерно.
Harbour
Цитата(Rst7 @ Oct 27 2009, 22:53) *
Хотя лично мне с гнусем все ясно - теперь это компилятор для x86, а жаль, задумка была отличная.


Обоснуйте, с каких это пор ?
Rst7
Цитата
Обоснуйте, с каких это пор ?


С тех пор, как его бездумно начали точить под архитектуру x86, не учитывая наличие других. Наглядный пример - векторизация.
Harbour
Улучшение x86 не значит ухудшение embedded one. векторизацию точили для SMP Cell/PPC/x86_64, не думаю что она вообще имеет какой-либо смысл для avr и прочих embedded targets. В любом случае отключить ее если что можно всегда (-fno-tree-vectorize).
Начиная с версий 4.x.x, gcc показывает стабильный рост качества генерируемого кода, при этом оставаясь бесплатным , многоплатформенным, имея нехилый саппорт и базу юзеров. Как по мне - это лучший из компиляторов именно по этим причинам, невзирая на проценты, которые он может быть, местами, где-то, кому-то и проигрывает.

P.S. А узкие места все и так на асме пишут если припечет
Rst7
Цитата
векторизацию точили для SMP Cell/PPC/x86_64


Ошибаетесь. Для PowerPC векторизация не нужна. x86 only.

Цитата
В любом случае отключить ее если что можно всегда (-fno-tree-vectorize).


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

Вот это и есть "бездумное затачивание".

Цитата
имея нехилый саппорт


biggrin.gif biggrin.gif
Harbour
Цитата
Ошибаетесь. Для PowerPC векторизация не нужна. x86 only.


Векторизация использует SIMD инструкции, для PPC, в частности, используется набор Altivec, для x86 - SSE/3DNOW, вот пример векторизации для PPC:

gcc -O2 -ftree-vectorize -maltivec -ftree-vectorizer-verbose=5 ....

Цитата
На самом деле вопрос куда глубже, чем втыкание ключа. Вопрос заключается в том, что данная оптимизация была прикручена без обратной связи,


Обратная связь - это из другой оперы и к статическим методам оптимизации отношения не имеет. Называется такая оптимизация profile-directed optimization и управляется -fprofile-arcs / -fbranch-probabilities / -fprofile-use и т.д.

Цитата
т.е. производится независимо от того, лучше становится код, или хуже. Лучше он может становиться только для ядер с отсутствием пост/пред/инкрементной/декрементной адресации, т.е. x86. И таких "оптимизаций" там уже масса.

Вот это и есть "бездумное затачивание".


Откуда такая инфа ?
Rst7
Цитата
Векторизация использует SIMD инструкции, для PPC, в частности, используется набор Altivec, для x86 - SSE/3DNOW, вот пример векторизации для PPC:


Да нет. Я не об этом. Я о той векторизации, которая заменяет
Код
do
{
*d++=*s++;
}while(--i);

на
Код
j=0;
do
{
d[j]=s[j];
j++;
}while(--i);


Т.к. x86 имеет адресацию типа [ra+rb], но не имеет адресацию типа [ra++], результат должен быть ясен.

Цитата
Обратная связь - это из другой оперы


Опера как раз та. Векторизация выполняется на этапе генерации RTL, а обратная связь нужна от кодегенератора. Тогда бы генератору RTL надавали по рукам за счет штрафов за выходной код. А этого нет.
Harbour
Цитата
Да нет. Я не об этом. Я о той векторизации, которая заменяет


Это не совсем векторизация - это банальная оптимизация циклов, просто имеет она несколько уровней. Сначала выкидываются лишние переменные и присваивания, перестраивается и упрощается тело цикла, счетчик по мере возможности строиться в сторону уменьшения, и т.д. А один из последних уровеней - instruction scheduling использует именно те инструкции, которые эффективны на конкретной платформе.
Также не нужно путать cost estimation для векторизации и оптимизацию с использованием обратной связи. Первая имеет смысл только для CPU с SIMD инструкциями, толку от нее на embedded, вот почему ее и реализовали только для PPC/Cell/x86. Вторая - конает всегда, правда меня всегда интересовал момент как получить эти profile данные на embedded платформе ?
Вопросы оптимизация циклов - это из третьей оперы, потому как там еще нужно учитывать конвейер, для платформ где он есть.
yuri_t
На моих проектах (RTOS,TCP/IP etc) для ARM7/9 самый быстрый компилятор - RVCT,
затем - IAR, затем - GCC, но разница в скорости - в предела 5-10%.

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

Размер кода у RVCT и IAR примерно одинаков, а у GCC - намного больше(иногда на 40% и это не
библиотеки).
Rst7
Цитата
Это не совсем векторизация - это банальная оптимизация циклов, просто имеет она несколько уровней. Сначала выкидываются лишние переменные и присваивания, перестраивается и упрощается тело цикла, счетчик по мере возможности строиться в сторону уменьшения, и т.д. А один из последних уровеней - instruction scheduling использует именно те инструкции, которые эффективны на конкретной платформе.


Это так должно быть. На самом деле в гнусе не так. Из-за прикрученной векторизации переменная заменяется на индекс и работа с указателями заменяется на array[index]. И это производится на этапе генерации RTL (т.е. гуано закладывается еще на машинно-независимом уровне). Но в правильном компиляторе за это должен дать по рукам генератор кода для таргета из RTL. А в гнусе этого нет.

Цитата
Также не нужно путать cost estimation для векторизации и оптимизацию с использованием обратной связи.


Я ничего не путаю. Бывают разные обратные связи - Вы про обратную связь через профайлер, я про обратную связь от следующих этапов генерации кода к предыдущим.
KRS
Цитата(yuri_t @ Oct 29 2009, 12:18) *
А вот для Cortex-M3 IAR 5.40 генерит очень медленный код (даже на максимальной оптимизации).

У кортекса получилась довольно сложная, с точки зрения компилирования архитектура - особенно если выполнять из медленной FLASH, потому что надо держать баланс между использованием только 16 битных команд и производительностью, особенно на STM32 - у которых медленная флеша. На IAR уже давно были нарекания на кортекс - он многие вещи не умел использовать, я уже тут как то выкладывал листинги работы с битовыми полями - код IAR вообще никуда не годился! Но теперь они постоянно пишут что код для кортекса стали намного лучше генерить, видно это не так sad.gif
etoja
Цитата(yuri_t @ Oct 29 2009, 12:18) *
самый быстрый компилятор - RVCT, затем - IAR, затем - GCC, но разница в скорости - в предела 5-10%.

Размер кода у GCC - намного больше(иногда на 40% и это не библиотеки).


Если разница в скорости 10%, а код больше на 40%, то это библиотеки или вы не отключили функции дебагера.
GCC позволяет компилировать программы под Linux и uCLinux. Плюс он бесплатен.
А быстродействие зависит от исходного текста программы: для сжатия по JPEG получите одно, для TCP/IP- другое.
Поэтому выбираешь компилятор из личных предпочтений, а не по бенчмаркам (так как все они одного класса).
KRS
Цитата(etoja @ Oct 29 2009, 14:27) *
Если разница в скорости 10%, а код больше на 40%, то это библиотеки или вы не отключили функции дебагера.

Нет дебагер и библиотеки здесь не причем! Я вот по map файлу смотрел, да и библиотеки у меня IARоввские были! GNU действительно генерирует большой код, но производительность от этого не так сильно страдает, а вот как видно в тумбе даже быстрее.
Rst7
Цитата
А вот для Cortex-M3 IAR 5.40 генерит очень медленный код (даже на максимальной оптимизации).


Листинг в студию.
yuri_t
Цитата(Rst7 @ Oct 29 2009, 16:49) *
Листинг в студию.


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)
KRS
На примере простого кода, работы с битовыми полями, я его уже давно как то выкладывал, для версий 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
Улучшений серьезных нет sad.gif
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
Rst7
Цитата


Это конечно очень интересно, но совершенно не то. Я имел в виду листинг узких мест (к которым возникают вопросы по быстродействию) после компилятора.
KRS
Цитата(KRS @ Oct 29 2009, 17:24) *
На примере простого кода, работы с битовыми полями, я его уже давно как то выкладывал, для версий 4.42, 5.11 и RVCT (уже не помню какого)


Еще раз посомтрел листинги, и заодно GNUC попробовал.
GNUC - BFI использует! А вот условное выполнение нет sad.gif.
А вот новый 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 битных команды.
AHTOXA
Цитата(KRS @ Oct 29 2009, 21:18) *
как видно еще GNUC по другому подходит к загрузке адреса в регистр!
Но IMHO это неоптимально, вместо одной 16 битной инструкции и 32 бит данных используется две 32 битных команды.


Это для скорости, так наверное конвейер не слетаетsmile.gif При -Os gcc грузит адрес одной инструкцией.
klen
Генерит 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) генерится переход. Есть желание думать что скорость нада проверять а не наличие тех или иных команд.

собсно примерно так.
KRS
Цитата(klen @ Nov 1 2009, 14:18) *
из сего видно что 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, дополнительно чистить его не надо, это должна делать вызывающая сторона! арифмитических опреаций с переменными здесь не проводится так что переполнения в старшие биты быть не может!

листинг первой функции, выкладывать не буду, но он тоже оптимальнее и код красивее.
dch
обычно компиляторы несравниваются по наличию ошибок, по тому какой суппорт удается получить и как быстро компилятор переползает на новую архитектуру, пока не начинаются подмены ресурсов на публичных сайтах public domain или близкие к нему GNU ресурсы обычно достаточно надежны, впрочем наверное начинать писать свой кроскомпилятор уже наверное можно, судя
по количеству несанкционированного доступа к инет реурсам.
ReAl
Полуоффтоп:
Не, ну таки странности у 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) тоже обошлись без /%, в этой конкретной функции ещё и аккуратнее регистры разложили и общий код ещё почти на сотню байт уменьшили.
"но осадок остался"
brag
Выкладываю несколько результатов бенчмарков. мож кому будет еще интересно... Проц STM32F105RC

CODE

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
brag
Ни один компилятор не смог нормально соптимизировать простые очевидные вещи..
Код
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
Petka
Цитата(brag @ Jul 6 2012, 09:57) *
Ни один компилятор не смог нормально соптимизировать простые очевидные вещи..

Очевидно же:
После выполнения
Код
    next->prev=next;

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


В данном случае всё указано однозначно.
Код
void tormoz::remove(){
    tormoz *n=next,*p=prev;
    n->prev=p;
    p->next=n;
}


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

Интересно с какой?
Если будет 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]
Petka
Цитата(brag @ Jul 6 2012, 11:18) *
Интересно с какой?
Если будет 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]


А если next->prev == &(prev->next) ? Компилятор не может знать что это в вашем случае это не так =)
brag
Цитата
А если next->prev == &(prev->next) ? Компилятор не может знать что это в вашем случае это не так =)

&(prev->next) Имеет тип tormoz**, а next->prev tormoz* . просто так оно не может быть присвоено.
Тогда уж и компилятор не должен ничего удалять в таком случаи:
Код
volatile int t;
void f(char *v){
    *v=3;
    t=4;
    *v=5;
}

Вдруг v=&t , он же не может этого знать. тем не менее:
gcc-4.7.1
Код
00000000 <_Z1fPc>:
   0:    f240 0300     movw    r3, #0
   4:    f2c0 0300     movt    r3, #0
   8:    2204          movs    r2, #4
   a:    601a          str    r2, [r3, #0]
   c:    2305          movs    r3, #5
   e:    7003          strb    r3, [r0, #0]
  10:    4770          bx    lr

armcc-4.1
Код
00000000 <_Z1fPc>:
   0:    4a51          ldr    r2, [pc, #324]; (148 <__sti___10_kernel_cpp+0x20>)
   2:    2103          movs    r1, #3;<<--- Хохма
   4:    2104          movs    r1, #4    
   6:    6011          str    r1, [r2, #0]
   8:    2105          movs    r1, #5
   a:    7001          strb    r1, [r0, #0]
   c:    4770          bx    lr
Petka
Цитата(brag @ Jul 6 2012, 11:54) *
&(prev->next) Имеет тип tormoz**, а next->prev tormoz* . просто так оно не может быть присвоено.

Это программистом в явном виде так присвоено не может быть. А через приведение типов может быть как угодно.
Цитата
Ну тогда уж и компилятор не должен ничего удалять в таком случаи:
Код
volatile int t;
void f(char *v){
    *v=3;
    t=4;
    *v=5;
}

Вдруг v=&t , он же не может этого знать. тем не менее:

Тут другой случай. v всегда указывает на одну и ту же ячейку памяти и запись в t ничего не может изменить. v может указывать куда угодно, хоть на t. Это ничего не меняет.
brag
Тоесть компилятор всегда перегружает переменную,если ранее была лябая запись в память?

Хотя вот:
Код
char b[5];
int f(long long *c){
    *c=1;
    b[2]=1;b[3]=2;
    return *c+b[3];
}

00000000 <_Z1fPx>:
   0:    2300          movs    r3, #0
   2:    2201          movs    r2, #1
   4:    f240 0100     movw    r1, #0
   8:    e9c0 2300     strd    r2, r3, [r0]
   c:    f2c0 0100     movt    r1, #0
  10:    2301          movs    r3, #1
  12:    708b          strb    r3, [r1, #2]
  14:    2302          movs    r3, #2
  16:    70cb          strb    r3, [r1, #3]
  18:    2003          movs    r0, #3
  1a:    4770          bx    lr

Возвращает всегда 3. А вдруг c пересекается с b? тогда результат будет далеко не 3

Код
ztest1.cpp:
extern char b[5];
int f(long long *c);
int main(){
    printf("%d\n",f((long long*)&b));
    return 0;
}

g++ -O1 -c ztest.cpp
g++ -O1 ztest1.cpp ztest.o
result=33619971

g++ -O2 -c ztest.cpp
g++ -O1 ztest1.cpp ztest.o
result=3
2 файла, абы компилятор не видел реализации функции f;
Petka
Цитата(brag @ Jul 6 2012, 12:52) *
Тоесть компилятор всегда перегружает переменную,если ранее была лябая запись в память?

Хотя вот:
Код
char b[5];
int f(long long *c){
    *c=1;
    b[2]=1;b[3]=2;
    return *c+b[3];
}

......

Тут компилятор волен оптимизировать. т.к. char b не обьявлен как volatile т.е. нет указания компилятору что содержимое b может измениться. Поэтому если с указывает на b, то это очевидная ошибка программиста.
Сергей Борщ
В "голых" Сях есть квалификатор restrict для указателей для подобных целей. В плюсах его нет.
brag
Цитата
нет указания компилятору что содержимое b может измениться. Поэтому если с указывает на b, то это очевидная ошибка программиста.

Тогда в случаи с next->prev=prev; prev->next=next; тоже нету явного указания компилятору, что prev может изменится. Ситуация аналогичная вроде? если next->prev == <cast...>&(prev->next) то это вероятно ошибка программиста.
В случаи с long logn/массивом можт быть и не ошибка, а модификация какого-то поля с флагами таким образом, хотя и не красивая. Вобщем мутно как-то. Тот же прикол, но вместо long long подсунуть short - перегружает short всегда после любой модификации памяти.

restrict вероде поддерживается(или заменяется на пробел, как и register sm.gif) почти всеми плюсовыми компилерами, правда да, в стандарте его нету
Petka
Цитата(brag @ Jul 6 2012, 14:21) *
Тогда в случаи с next->prev=prev; prev->next=next; тоже нету явного указания компилятору, что prev может изменится. Ситуация аналогичная вроде? если next->prev == <cast...>&(prev->next) то это вероятно ошибка программиста.
...

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