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

Опять слишком "жесткое" утверждение. Поймите и Вы, я тоже не цепляюсь именно к этому примеру. Но Ваши категоричные утверждения  :blush:

Вы хоть знаете где Ваш компилятор (именно тот  с которым Вы работаете) делает оптимизацию (и какие приемы лучше при этом использовать). Ну хотя бы такую классику ++j или j++.  Или сравниваете C-программы и Asm-прог только по конечному результату?

 

Извините, если Вам показалось какое-то упорство с моей стороны (навеянное впрочем личным опытом, пусть и несколько уставревшим, и знания некоторых основ компилирования). Но ведь весь разговор в течении последнего часа - и поэтому выбранный тон - для лучшего взаимопонимания  :blush:

 

От своей реплики с условиями задачи не отказываюсь  :blush:

 

Поверьте, я прекрасно знаю, где и как работает оптимизация у моего компилятора. Когда-то угробил прилично времени на действия типа: модифицируем код->компилируем->дизассемблируем->смотрим, где накосячено.

 

А приведенная мной задача не решается на Ц оптимально.

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


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

Ничего себе -- " ARM начинающим"!

 

Я вот какой аспект хочу поднять:

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

 

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

 

Если заранее найти такие места и написать их на асме, то код станет гораздо "сопровождаемее".

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


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

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

 

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

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


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

Какая разница, сколько там флэш.

Эмпирическая оценка разбухания кода Си по сравнениею с асм 1,3-1,5 раз.

Быстродействия примерно столько же. Кочечно, есть и частные случаи.

Внесу и я свое замечание на 5 копеек.

Вообще-то как я замечал оценка разбухания кода и соответственно быстродействие оличается в 3-4 раза.

Это, мягко говоря, неправда.

 

Только сравнивать надо не отдельные функции, а модуль целиком, содержащий большое количество функций.

При увеличении количества функций все лишь нивелируется, стремясь к среднему значению. Если можно еще найти отдельные локальные места, где на асм можно выиграть 3-4 раза объема или быстродействия по сравнению с Ц, то в большом проекте эти места утонут в море среднестатистических показателей.

 

 

Судя по всему вы не писали достаточно большие проекты на ASM.

У меня, конечно, довольно редко воникает проект продублированный на С и ASM. Однако, например для кодека G.723.1 переписывание только базовых функций на ASM привело к уменьшению времени обработки одного фрейма с 200мс до 25, что соответствует примерно 10 кратному ускорению времени выполнения. Для обработчика обмена по USB выигрышь по скорости обработки выросло в 4 раза.

 

И как раз чем бльше проект, тем заметнее разница.

Если на отдельных простых функциях разница составляет 30% то на сложных функциях, а уж тем более больших проектах разница достигает 3-4 каратного превосходства.

 

И чтобы не быть совсем голословным:

 

Вы предлагали сравнить на реальном проекте.

 

У меня недавно был проект часть которого также была выполненый на С и ASM.

 

Это real FFT.

На котором также легко демонстрируется трехкратное превосходство ASM над С.

Итак:

Есть входной масив данных int16 Data[2048];

Так же есть входной оконный масив int16 WIN[2048];

Наебходимо выполнить следующее:

Первоначально поэлементно умножить елемент данных на елемент окна

 

Data=Data*WIN/65536;

 

Далее выполнить реальное преобразование фурье по 2048 точкам.

И получить квадкат абсолютного значения по получившимся комплексным отсчетам.

ABS= RE*RE+Im*Im;

 

Выходной массив это int32 Abs[1024] расположен на месте входного массива данных Data[2048].

 

пример на ASM прилагаю:

 

Он занимает 4К памяти данных, и во Flash 4К под окно, 4К под служебные таблицы и совсем чуть-чуть под программы.

Один цикл вычислений занимает примерно 500 000 цикло для 7TDMI.

На С аналогичная программ требовала примерно в 3 раза больше.

 

Если вы полагаете, что сможете получить аналогичные ( в приделах 30% ) результаты на С, покажите это.

 

В принципе дело достаточное простое для С.

 

Умножение на окно и авсолютное значение - функции достаточно простые.

А real FFT предлагается в довольно большом количестве библиотек если лень писать самому.

fft_sample.zip

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


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

Тогда еще раз условия задачи: процессор - ?, размер элемента массива - байт? и т.п.

 

Хорошо. Условия задачи:

1. Процессор: ARM7

2. Размер элемента массива: 4 байта (слово)

3. Количество элементов: 2048

4. Источник и приемник выравнены по границе слова (ложка дёгтя здесь

отсутствует, но можно добавить - 4 байта из 8192 картину не испортят)

5. Оптимизация на скорость

 

Попробуйте решить это на Ц и сравните с АСМ

 

Опубликуйте асм-код

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


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

Ничего себе  -- " ARM начинающим"!

так чтобы жизнь медом не казалась :)

 

Я вот какой аспект хочу поднять:

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

При правильном выборе процессора как правило все успевает. А то иногда встречаются люди, которые mp3 хотят программно делать на х51. Или USB или Ethernet генерить программно.

 

Поменяли компилятор (апгрейд версии,

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

 

заказчик вдруг решил перейти на другой,

Заказчику глубоко фиолетово на каком компиляторе решена его проблема.

Он, возможно даже не знает слова компилятор.

 

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

Никто не пишет программы в притык к 100% производительности процессора,

если это так, то тут ошибка в ДНК.

 

Если заранее найти такие места и написать их на асме, то код станет  гораздо "сопровождаемее".

какие места? как определить, что вот это место такое, а вот это место - не такое?

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


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

Судя по всему вы не писали достаточно большие проекты на ASM.

У меня, конечно,  довольно редко воникает проект продублированный на С и ASM. Однако, например для кодека G.723.1  переписывание только базовых функций  на ASM  привело к уменьшению времени обработки одного фрейма с 200мс до 25, что соответствует примерно 10 кратному ускорению времени выполнения. Для обработчика обмена по USB выигрышь по скорости обработки выросло в 4 раза.

Я не работаю в этих областях, и к сожалению не могу прокомментировать данные цифры. Но 200 мс и 25 вообще выглядит весьма странно.

 

Умножение на окно и авсолютное значение - функции достаточно простые.

Да, простые, поэтому и писание их заняло не более получаса, с учетом настройки опций этого тестового проекта и приведения ваших исходников к синтаксису IAR. Итак, IAR 4.30:

Функция window, ваш вариант:

;    GLOBAL    window
    PUBLIC    window
window
    stmfd    r13!,{r2-r3,r14}
    ldr  r14,=WINTABLE
    mov    r1,r1,lsl #1
win_l0
    ldrsh  r2,[r14],#2;WIN[i]
    ldrsh  r3,[r0]  ;Buf[i]
    mul  r3,r2,r3
    mov  r3,r3,asr #15
    strh  r3,[r0],#2;Buf[i]
    subs    r1,r1,#1
    bgt    win_l0

    ldmfd    r13!,{r2-r3,pc}

 

Функция window, мой вариант:

static const signed short Rfft_c_wintbl[2048] = {...}

void window_c  (signed short *Buf, int Len)
{
   const signed short *win = Rfft_c_wintbl;

   do
   {
       *Buf = (*Buf * *win) /65536;
       Buf++;
       win++;
   } while (--Len);
}

143          void window_c  (signed short *Buf, int Len)
   144          {
   145              const signed short *win = Rfft_c_wintbl;
  \                     window_c:
  \   00000000   24209FE5           LDR         R2,??window_c_0  ;; ??Rfft_c_wintbl
   146
   147              do
   148              {
   149                  *Buf = (*Buf * *win) /65536;
  \                     ??window_c_1:
  \   00000004   F030D0E1           LDRSH       R3,[R0, #+0]
  \   00000008   ........           LDRSH       R12,[R2], #+0x2
  \   0000000C   9C0303E0           MUL         R3,R12,R3
  \   00000010   C3C7A0E1           MOV         R12,R3, ASR #+0xF
  \   00000014   2C3883E0           ADD         R3,R3,R12, LSR #+0x10
  \   00000018   4338A0E1           MOV         R3,R3, ASR #+0x10
  \   0000001C   ........           STRH        R3,[R0], #+0x2
   150                  Buf++;
   151                  win++;
   152              } while (--Len);
  \   00000020   011051E2           SUBS        R1,R1,#+0x1
  \   00000024   F6FFFF1A           BNE         ??window_c_1
   153          }
  \   00000028   0EF0A0E1           MOV         PC,LR            ;; return
  \                     ??window_c_0:
  \   0000002C   ........           DC32        ??Rfft_c_wintbl

 

Все тело цикла практически одинаково, за исключением деления на 65536,

что в вашем варианте выглядит просто как арифметический сдвиг

    mov  r3,r3,asr #15

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

   \   00000010   C3C7A0E1           MOV         R12,R3, ASR #+0xF
  \   00000014   2C3883E0           ADD         R3,R3,R12, LSR #+0x10
  \   00000018   4338A0E1           MOV         R3,R3, ASR #+0x10

Что по сути правильно, поскольку в вашем варианте при делении отрицательного числа никогда не будет получен 0, максимум -1. В общем, округление результата будет несколько не туда и в разные стороны для положительных и отрицательных чисел. Возможно, в вашем приложении это качественно и не сказывается на окончательный результат, но тем не менее, в ТЗ было прописано деление.

 

Далее, функция abs, ваша

abs_l0
    ldrsh  r2,[r0];Buf[i]
    mul  r3,r2,r2
    ldrsh  r2,[r0,#2];Buf[i]
    mla  r3,r2,r2,r3
    mov  r3,r3,asr #14
    str  r3,[r0],#4;Buf[i]
    subs    r1,r1,#1
    bgt    abs_l0

моя

void absr_c    (short *Buf, int Len)
{
   unsigned int *p_out = (unsigned int *)Buf;

   do
   {
     int temp = *Buf * *Buf;
     Buf++;
     temp += *Buf * *Buf;
     Buf++;
     *p_out++ = temp;
   } while (--Len);
}

   155          void absr_c    (short *Buf, int Len)
   156          {
   157              unsigned int *p_out = (unsigned int *)Buf;
  \                     absr_c:
  \   00000000   0020A0E1           MOV         R2,R0
   158          
   159              do
   160              {
   161                int temp = *Buf * *Buf;
  \                     ??absr_c_0:
  \   00000004   F030D0E1           LDRSH       R3,[R0, #+0]
  \   00000008   93030CE0           MUL         R12,R3,R3
   162                Buf++;
   163                temp += *Buf * *Buf;
  \   0000000C   F230F0E1           LDRSH       R3,[R0, #+0x2]!
  \   00000010   93C32CE0           MLA         R12,R3,R3,R12
   164                Buf++;
  \   00000014   020080E2           ADD         R0,R0,#+0x2
   165                *p_out++ = temp;
  \   00000018   ........           STR         R12,[R2], #+0x4
   166              } while (--Len);
  \   0000001C   011051E2           SUBS        R1,R1,#+0x1
  \   00000020   F7FFFF1A           BNE         ??absr_c_0
   167          }
  \   00000024   0EF0A0E1           MOV         PC,LR            ;; return

 

То есть издержки Ц - дополнительная команда ADD R0,R0,#+0x2.

Кроме того, у вас там есть масштабирование результата mov r3,r3,asr #14

хотя по ТЗ это описано не было. При добавлении масштабирования код для этой операции создается такой же, как и в верхнем примере.

 

А real FFT предлагается в довольно большом количестве библиотек если лень писать самому.

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

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


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

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

Ну и совершенно согласен с коллегами, что надо сначала выбрать процессор, которому данная задача по "зубам", благо сейчас есть из чего выбрать.

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


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

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

Давайте обождем самого вычисления Фурье и сравним итог.

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


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

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

Давайте обождем самого вычисления Фурье и сравним итог.

Зато эти функции - типовые для задач, решаемых на МК. В отличие от FFT, которая есть уже весьма специальная задача, кои часто решают на более специальных процессорах, нежели АРМ. И в типовом проекте на МК удельный вес функций типа FFT исчезающе мал. Есть подозрение, что большинству пользователей АРМ (да и других МК) не приходится с этим иметь дело. И если уж этот специальный случай не оправдал у Вас себя на С, то, как же сказали, его можно и на асме реализовать. Но это не повод и не причина писать на асме все. А эффективность кодогенерации С компилятора, ихмо, была продемонстрирована с блеском.

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


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

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

Давайте обождем самого вычисления Фурье и сравним итог.

Зато эти функции - типовые для задач, решаемых на МК. В отличие от FFT, которая есть уже весьма специальная задача, кои часто решают на более специальных процессорах, нежели АРМ. И в типовом проекте на МК удельный вес функций типа FFT исчезающе мал. Есть подозрение, что большинству пользователей АРМ (да и других МК) не приходится с этим иметь дело. И если уж этот специальный случай не оправдал у Вас себя на С, то, как же сказали, его можно и на асме реализовать. Но это не повод и не причина писать на асме все. А эффективность кодогенерации С компилятора, ихмо, была продемонстрирована с блеском.

 

Эти функции как раз не типовые.

Большинство функций которые я применяю значительно сложнее.

От вывода на экран и обработки кнопок до алгоритма работы.

Это очень простые функции занимающие несколько строк в исходном тексте.

В большинстве своем функции нанимают в среднем около сотни строк.

В качестве примера достаточно сложной функции и используется FFT. Для меня это пример который у меня реализован и на С и на ASM и разницу здесь я знаю.

А на любой достаточно сложной функции разница станет существенной.

 

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

Хотя это не так.

 

Можете посмотреть сами взяв функцию или модуль хотя бы в несколько сотен строк.

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


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

Эти функции как раз не типовые.

Большинство функций которые я применяю значительно сложнее.

От вывода на экран и обработки кнопок до алгоритма работы.

Это очень простые функции занимающие несколько строк в исходном тексте.

Каждая функция в конечном итоге состоит из каких-то элементарных действий. Если на элементарные действия Ц дает оверхед на 30% (к примеру), то с чего вдруг вы считаете, что при увеличении количества этих действий вдруг произойдет рост неэффективности Ц в геометрической прогресии?

 

В большинстве своем функции нанимают в среднем около сотни строк.

В качестве примера достаточно сложной функции и используется FFT. Для меня это пример который у меня реализован и на С и на ASM и разницу здесь я знаю.

Так приведите пример на Ц, я его хотя бы откомпилирую, чтобы не писать самому. Хотя бы посмотреть, откуда там у оверхеда ноги растут.

 

А на любой достаточно сложной функции разница станет существенной.

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

 

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

Хотя это не так.

Можете посмотреть сами взяв функцию или модуль хотя бы в несколько сотен строк.

Брали, смотрели листинг после компилятора. Да, где-то можно сделать чуть эффективнее, но как правило не намного. А некоторые виды оптимизации, которые выполняет компилятор - руками делать на ассемблере, свихнуться можно.

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


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

Опубликуйте асм-код

 

Пожалуйста:

copy0
    stmfd    sp!, {r4-r9, r14}
    mov  r2, #0x80
copy0_0
    ldmia    r0!, {r3-r9, r12}
    stmia    r1!, {r3-r9, r12}
    ldmia    r0!, {r3-r9, r12}
    stmia    r1!, {r3-r9, r12}
    ldmia    r0!, {r3-r9, r12}
    stmia    r1!, {r3-r9, r12}
    ldmia    r0!, {r3-r9, r12}
    stmia    r1!, {r3-r9, r12}
    subs    r2, r2, #0x01
    bne  copy0_0
    ldmfd    sp!, {r4-r9, pc}

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

такой ситуации по-разному: некоторые маразматично, а некоторые вполне ничего.

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


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

To aaarrr

Весь день I-neta не было :( (и IARa в том числе). Поэтому пример на подвернувшемся GNU-шном Си (из тех, что вполне ничего). В общем то это только идея, которую можно и дальше продвинуть (соопитимизировать). Кстати, у Andy Mozzhevilov тоже можно дальше сооптимизировать, если уж это так необходимо.

Итак множественная загрузка:

struct m8 { unsigned long w[8]; };

 

struct m8 a[128];

struct m8 b[128];

 

void copy0(register struct m8 *pa, register struct m8 *pb)

{register int i;

for(i=128; i!=0; --i)

  *pb++=*pa++;

}

 

 

void main()

{

  copy0(a,B);       

}

 

и полностью выход на асме (чтоб без обмана)

 

.file "prim.c"

.text

.align 2

.global copy0

.type copy0, %function

copy0:

@ args = 0, pretend = 0, frame = 0

@ frame_needed = 0, uses_anonymous_args = 0

stmfd sp!, {r4, r5, r6, lr}

mov r5, r0

mov r4, r1

mov r6, #128

.L6:

mov lr, r4

mov ip, r5

ldmia ip!, {r0, r1, r2, r3}

stmia lr!, {r0, r1, r2, r3}

ldmia ip, {r0, r1, r2, r3}

stmia lr, {r0, r1, r2, r3}

add r5, r5, #32

add r4, r4, #32

subs r6, r6, #1

ldmeqfd sp!, {r4, r5, r6, pc}

b .L6

.size copy0, .-copy0

.align 2

.global main

.type main, %function

main:

@ args = 0, pretend = 0, frame = 0

@ frame_needed = 1, uses_anonymous_args = 0

mov ip, sp

stmfd sp!, {fp, ip, lr, pc}

sub fp, ip, #4

ldr r0, .L10

ldr r1, .L10+4

bl copy0

ldmea fp, {fp, sp, pc}

.L11:

.align 2

.L10:

.word a

.word b

.size main, .-main

.comm a, 4096, 32

.comm b, 4096, 32

.ident "GCC: (GNU) 3.3.1"

 

командная строка компилятора

arm-uclibc-gcc.exe -S -mcpu=arm7tdmi -O -o oops.s prim.c

 

Прощаюсь до завтра :blush:

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


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

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...