\documentclass[a4paper,fontsize=14bp]{article}

\input{settings/common-preamble}
\input{settings/fancy-listings-preamble}
\input{settings/bmstu-preamble}
\numerationTop

\def\makeyear{2021}
\def\grpNum{11М}

\begin{document}
\setlength{\columnsep}{22pt}
\thispagestyle{empty}
\makeBMSTUHeader

\makeReportTitle{лабораторной}{2}{Разработка программного обеспечения. \\Отладка и профилирование кода}{Программное обеспечение встроенных систем}{}{доцент кафедры ИУ-3 \\ Федоров С.В.}
\newpage
\pagestyle{fancy}
%\blankpage
\tableofcontents
\newpage
\section*{Цель работы}
\addcontentsline{toc}{section}{Цель работы}
В лабораторной работе необходимо осуществить отладку программы, её профилирование, выполнить требования по быстродействию и объему для отдельных функций ПО. Изучить влияние ручных оптимизаций циклов на быстродействие программы. Изучить оптимизации компилятора на уровне формируемого ассемблерного кода.

\section*{Задания}
\addcontentsline{toc}{section}{Задания}
\begin{itemize}
\item Общее задание части I
  \begin{enumerate}
  \item Исходный код программы обработки прерывания от кнопок с замером времени выполнения обработчика прерывания.
  \item Результаты измерения времени выполнения первого и последующих вызовов обработчика прерывания для разных уровней оптимизации.
  \item Прокомментированный ассемблерный код обработчика прерывания для оптимизации Level3 и для отключенной оптимизации.
  \item Сравнение результатов и выводы.
  \end{enumerate}
\item Индивидуальное задание части I
  \begin{enumerate}
  \item Текст индивидуального задания.
  \item Исходный код программы, в которой реализована инициализация и обработка массива данных с помощью двух разработанных по заданию функций: одна без развертывания цикла, а другая с развертыванием цикла.
  \item Результаты измерения времени выполнения разработанных функций для оптимизации Level3 и для отключенной оптимизации.
  \item Результаты измерения количества тактов, затрачиваемых на одну итерацию неразвернутого цикла при включенной оптимизации Level3 при разных объемах массивов данных.
  \item Прокомментированный ассемблерный код разработанных функций для оптимизации Level3 и для отключенной оптимизации.
  \item Сравнение результатов и выводы.
  \end{enumerate}
\item Общее задание части II
\end{itemize}

\section*{Общее задание части I.}
\addcontentsline{toc}{section}{Общее задание части I.}
Для подсчёта времени был модифицирован код из первой лабораторной работы (добавлен таймер и вывод информации с него). Код представлен в листинге \hrf{lst:btnISR} внесённые изменения отмечены \lh{red}{красным}, функции, директивы и комментарии, оставшиеся без изменений опущены:

\begin{lstlisting}[language=C, style=CCodeStyle, caption=Performance counter в обработчике прерываний, label={lst:btnISR}]
// includes
// defines

volatile int edge_capture;
void handle_button_interrupts(void* context, alt_u32 id) {
  <@\lh{red}{PERF\_BEGIN (PERFORMANCE\_COUNTER\_0\_BASE, 1);}@>

  volatile int* edge_capture_ptr = (volatile int*) context;
  *edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE, 0x0);
  IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTONS_BASE);

  <@\lh{red}{PERF\_END (PERFORMANCE\_COUNTER\_0\_BASE, 1);}@>
}

void init_button_pio() {
  // edge capture ptr, PIO_IRQ_MASK, PIO_EDGE_CAP, alt_ic_isr_register
}

int main(void) {
  int led = 0x01;
  printf("Simple\n");

  IOWR_ALTERA_AVALON_PIO_DATA(GREEN_LED_BASE,led);
  // Performance counter reset and initialize
  <@\lh{red}{PERF\_RESET (PERFORMANCE\_COUNTER\_0\_BASE);}@>
  <@\lh{red}{PERF\_START\_MEASURING (PERFORMANCE\_COUNTER\_0\_BASE);}@>

  init_button_pio();
  edge_capture = 0;

  // local counter to control performance
  <@\lh{red}{int cntCounts = 0;}@>
  while (1) {
    if (edge_capture != 0) {
     <@\lh{red}{cntCounts++;}@>
      switch(edge_capture) {
      case KEY_3:
        led = 0xFF; // all bits 1
        break;
      case KEY_2:
        led &= led >> 1;
        break;
      case KEY_1:
        led &= led << 1;
        break;
      case KEY_0:
        led = 0x00; // all bits 0
        break;
      }
      IOWR_ALTERA_AVALON_PIO_DATA(GREEN_LED_BASE,led);
      edge_capture = 0;

      // recompile this condition to check one or ten interrupts
      // if (cntCounts == 1) {
      <@\lh{red}{if (cntCounts == 10) \{}@>
        <@\lh{red}{PERF\_STOP\_MEASURING (PERFORMANCE\_COUNTER\_0\_BASE);}@>
        <@\lh{red}{perf\_print\_formatted\_report (}@>
        <@\lh{red}{PERFORMANCE\_COUNTER\_0\_BASE,}@>
        <@\lh{red}{ALT\_CPU\_FREQ, 1,}@>
        <@\lh{red}{"Btn irq time",}@>
        <@\lh{red}{"Time of ten interrupts");}@>
        <@\lh{red}{PERF\_END (PERFORMANCE\_COUNTER\_0\_BASE, 0);}@>
      <@\lh{red}{\}}@>
    }
  }
}
\end{lstlisting}

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

\begin{lstlisting}[language=C,style=CCodeStyle]
  void handle_button_interrupts(void* context, alt_u32 id) __attribute__ ((section("irq")));
\end{lstlisting}

В результате выполнения данного кода были получены результаты, представленные в таблице \hrf{table:isrtime}. Скриншоты с этими же результатами представлены в приложении \hrf{appendix:isrtime}. После нескольких запусков с разными вариантами настроек можно однозначно сказать, что при расположении обработчика прерываний в тесносвязанной памяти очевидно ускорение работы функции даже без оптимизаций компилятора. Из результатов видно, что размещённый в тесносвязанной памяти код выполняется так, будто находится в кэше. При размещении в обычной памяти, первый вызов выполняется дольше, поскольку обработчик перемещается в кэш (далее он оттуда не вытесняется, поскольку в целом программа очень простая). Время работы при разных типах оптимизации не меняется так как обработчик очень простой.

\begin{table}[H]
  \centering
  \begin{tabular}{||l|l|c|c||}
    \hline
    Использование TCM & Оптимизация & Количество прерываний & Время (тактов) \\ [0.5ex] 
    \hline\hline
    нет & нет    & 1  & 62  \\
    нет & нет    & 10 & 314 \\
    нет & -О2    & 1  & 43  \\
    нет & -О2    & 10 & 223 \\
    нет & размер & 1  & 43  \\
    нет & размер & 10 & 223 \\
    да  & нет    & 1  & 28  \\
    да  & нет    & 10 & 280 \\
    да  & -О3    & 1  & 20  \\
    да  & -О3    & 10 & 200 \\
    да  & размер & 1  & 20  \\
    да  & размер & 10 & 200 \\ [1ex]
    \hline
  \end{tabular}
  \caption{Зависимость времени исполнения функции от количества прерываний}
  \label{table:isrtime}
\end{table}

Оптимизации процессора значительно повлияли на транслируемый код, скопмилировав в результате ассемблерный код, представленный в листингах \hrf{lst:noopt} и \hrf{lst:optlv2}. В неоптимизированном коде видно гораздо больше обращений к памяти (мнемоники \code{stw}, \code{ldw}), в то время, как оптимизированный код сразу работает только с периферийным модулем (мнемоники \code{stwio}, \code{ldwio}). Также неоптимизированная версия программы сразу выделяет место для своей работы на стеке (\code{sp,sp,-16}), сохраняет принятые в функцию параметры, несмотря на то, что один из них не используется вовсе, а второй используется только один раз (\code{r4}, \code{r5} записываются в -8 и -12 сдвиги от указателя на фрейм, соответственно, а читается только \code{r4}: \code{ldw r2,-8(fp)}) и активнее работает с локальными регистрами, сохраняя в них промежуточные значения, прочитанные из периферийных модулей. В то время, как оптимизированная функция работает с единственным, возвращающим, регистром \code{r2}.

\begin{lstlisting}[style=ASMStyle, caption=Код без оптимизаций, label={lst:noopt}]
  00000284 <handle_button_interrupts>:
  284:  defffc04    addi   sp,sp,-16
  288:  df000315    stw    fp,12(sp)
  28c:  df000304    addi   fp,sp,12
  290:  e13ffe15    stw    r4,-8(fp)
  294:  e17ffd15    stw    r5,-12(fp)
  298:  0007883a    mov    r3,zero
  29c:  00820034    movhi  r2,2048
  2a0:  10c40535    stwio  r3,4116(r2)
  2a4:  e0bffe17    ldw    r2,-8(fp)
  2a8:  e0bfff15    stw    r2,-4(fp)
  2ac:  00820034    movhi  r2,2048
  2b0:  10c42337    ldwio  r3,4236(r2)
  2b4:  e0bfff17    ldw    r2,-4(fp)
  2b8:  10c00015    stw    r3,0(r2)
  2bc:  0007883a    mov    r3,zero
  2c0:  00820034    movhi  r2,2048
  2c4:  10c42335    stwio  r3,4236(r2)
  2c8:  00820034    movhi  r2,2048
  2cc:  10842337    ldwio  r2,4236(r2)
  2d0:  0007883a    mov    r3,zero
  2d4:  00820034    movhi  r2,2048
  2d8:  10c40435    stwio  r3,4112(r2)
  2dc:  0001883a    nop
  2e0:  e037883a    mov    sp,fp
  2e4:  df000017    ldw    fp,0(sp)
  2e8:  dec00104    addi   sp,sp,4
  2ec:  f800283a    ret
\end{lstlisting}

\begin{lstlisting}[style=ASMStyle, caption=Код с оптимизацией -О2, label={lst:optlv2}]
  00000284 <handle_button_interrupts>:
  284:  00820034    movhi  r2,2048
  288:  10040535    stwio  zero,4116(r2)
  28c:  10842337    ldwio  r2,4236(r2)
  290:  20800015    stw    r2,0(r4)
  294:  00820034    movhi  r2,2048
  298:  10042335    stwio  zero,4236(r2)
  29c:  10842337    ldwio  r2,4236(r2)
  2a0:  00820034    movhi  r2,2048
  2a4:  10040435    stwio  zero,4112(r2)
  2a8:  f800283a    ret
\end{lstlisting}

\section*{Индивидуальное задание части I.}
\addcontentsline{toc}{section}{Индивидуальное задание части I.}
\subsection*{Развёрнутый цикл и оптимизации}
Индивидуальным заданием в первой части работы был расчёт контрольной суммы UDP для блока данных. Код программы, реализующей вызов функции расчёта контрольной суммы и подсчёт времени выполнения посредством Performance Counter представлен в листинге \hrf{lst:udpCode}. На строках \hrf{line:nounroll} и \hrf{line:unroll} представлены циклы, выполняющие одну и ту же задачу, но без применения развёртывания цикла (loop unrolling) и с применением, соответственно. Также важно, что цикл без развёртывания является циклом <<добора>>, то есть когда развёрнутый цикл (строка \hrf{line:unroll}) завершает свою работу в массиве данных могут остаться некратные данные, которые досчитываются циклом со строки \hrf{line:nounroll}.
\begin{lstlisting}[language=C, style=CCodeStyle, caption=Функция расчёта контрольной суммы UDP, label={lst:udpCode}]
#include <stdio.h>
#include <unistd.h>
#include "altera_avalon_pio_regs.h"
#include "altera_avalon_performance_counter.h"
#include "sys/alt_irq.h"
#include "system.h"

#define CACHE_CHECK 2048

unsigned short checkSum(unsigned short* addr, int size) __attribute__ ((section("irq")));
unsigned short checkSum(unsigned short* addr, int size) {
    register unsigned short checksum = 0;
    register int sum = 0;

// unrolled cycle
    while (size > 8) { <@\label{line:unroll}@>
        sum += *addr++;
        sum += *addr++;
        sum += *addr++;
        sum += *addr++;
        size -= 8;
    }

//  simple cycle (and)
    while (size > 1) { <@\label{line:nounroll}@>
        sum += *addr++;
        size -= 2;
    }

    if (size > 0) {
        sum += *(unsigned char *) addr;
    }

    while (sum >> 16) {
        sum = (sum & 0xffff) + (sum >> 16);
    }
    checksum = ~sum;
    return checksum;
}

int main(void) {
    printf("udp tcm 3 opt unroll cycle\n");
    PERF_RESET(PERFORMANCE_COUNTER_0_BASE);

    const int SIZE = CACHE_CHECK;
    unsigned short udpCheckSum = 0;
    short datagram[SIZE];
    const void *buf;

    // filling datagram
    short c1 = 0xf001;
    short c2 = 0x0e11;
    short c3 = 0xaad0;
    short c4 = 0xa00c;
    int i = 0;
    do {
        datagram[i + 0] = c1;
        datagram[i + 1] = c2;
        datagram[i + 2] = c3;
        datagram[i + 3] = c4;
        i += 4;
    } while (i < SIZE);
    buf = (const void*) datagram;

    PERF_START_MEASURING(PERFORMANCE_COUNTER_0_BASE);
    i = 100;
    while (0 < --i) {
        PERF_BEGIN (PERFORMANCE_COUNTER_0_BASE, 1);
        udpCheckSum = checkSum(buf, (SIZE + 1) * 2);
        PERF_END (PERFORMANCE_COUNTER_0_BASE, 1);
    }

    perf_print_formatted_report (
            PERFORMANCE_COUNTER_0_BASE,
            ALT_CPU_FREQ, 1,
            "UDP 2048",
            "Time");
    PERF_STOP_MEASURING (PERFORMANCE_COUNTER_0_BASE);
    printf("checksum = 0x%x\n", udpCheckSum);
    return 0;
}
\end{lstlisting}

Результаты замеров времени выполнения этих двух вариантов функции приведены в таблице \hrf{table:udpchecksum}. Все измерения проводились на 99ти повторениях, снимки экрана представлены в приложении \hrf{appendix:checksumTime}.

\begin{table}[H]
  \centering
  \begin{tabular}{||r|c|c||} 
    \hline
    Оптимизация & Цикл развёрнут & Время(тактов) \\ [0.5ex] 
    \hline\hline
    нет & нет & 24322152 \\ 
    нет & да  & 14470051 \\
    -О3 & нет & 6835626 \\
    -О3 & да  & 4174875 \\
    \hline
  \end{tabular}
  \caption{Применение развёртывания цикла при выполнении вычислений}
  \label{table:udpchecksum}
\end{table}

Для массивов длиной 8192 элементов типа \code{unsigned short}, циклически заполненных значениями \code{0xf001}, \code{0x0e11}, \code{0xaad0}, \code{0xa00c} получилось значение \code{0xbec8}, что является корректным значением контрольной суммы.

\subsection*{Оптимизация и заполнение памяти}
В процессе выполнения программы были произведены измерения количества тактов, затрачиваемых на одну итерацию неразвернутого цикла при включенной оптимизации -O3 при разных объемах массивов данных. Результаты измерений приведены в таблице \hrf{table:memory}. Подсчёт результатов был произведён без изменения положения Performace Counter (для всей функции целиком) согласно средней асимтотической сложности алгоритма получения контрольной суммы O(n), где n -- это размер входящего массива данных. Для системы было выделено 16384 байта, соответственно, для заполнения её согласно задания необходимо создать массивы на 16384 ($200\%$), 8192 ($100\%$), 4096 ($50\%$), 2048 ($25\%$) элементов типа \code{unsigned short} (шестнадцатиразрядные беззнаковые целые числа). Снимки экрана с результатами работы Performance counter представлены в приложении \hrf{appendix:udpMemory}.

\begin{table}[H]
  \centering
  \begin{tabular}{||r|c|c|c||} 
    \hline
    Цикл развёрнут & \thead{Расчётное\\заполнение памяти\\(процентов)} & \thead{Время\\функции\\(секунд)} & \thead{Расчётное время\\одной итерации\\(мксек)} \\ [0.5ex] 
    \hline\hline
    нет & 25  & 0.03427 & 0.169024 \\ 
    нет & 50  & 0.06843 & 0.168753 \\
    нет & 100 & 0.13671 & 0.168568 \\
    нет & 200 & 0.27328 & 0.168482 \\
    да  & 25  & 0.02097 & 0.413707 \\ 
    да  & 50  & 0.04181 & 0.412425 \\
    да  & 100 & 0.08350 & 0.411833 \\
    да  & 200 & 0.16676 & 0.411241 \\
    \hline
  \end{tabular}
  \caption{Скорость исполнения одной итерации относительно заполнения памяти}
  \label{table:memory}
\end{table}

В таблице видно, что каждая итерация развёрнутого цикла выполняется дольше, но не в четыре раза, поэтому за счёт уменьшения количества итераций в четыре раза получается довольно значительный прирост в скорости исполнения программы. Такой прирост обусловлен снижением накладных расходов на каждую итерацию цикла. В листингах \hrf{lst:nooptsim} и \hrf{lst:nooptunf} представлен ассемблерный код функции подсчёта контрольной суммы без применения развёртывания основного цикла и с применением, соответственно, а в листингах \hrf{lst:optsim} и \hrf{lst:optunf} такой же код, но с оптимизацией компилятора -О3.

В листинге \hrf{lst:nooptsim} на строках с \hrf{line:simbody} по \hrf{line:simcond} виден цикл, обрабатывающий значения из массива. Аналогичный цикл виден как <<цикл добора>> в листинге \hrf{lst:nooptunf} на строках с \hrf{line:unfsimbody} по \hrf{line:unfsimcond}. Адреса начала и конца тел циклов выделены \lh{blue}{синим}.

В листинге с развёртыванием цикла на каждой итерации осуществляется четыре идентичных действия по чтению значения из исходного массива (\hrf{line:unfit1b}, \hrf{line:unfit2b}, \hrf{line:unfit3b}, \hrf{line:unfit4b}), выделены \lh{dkgreen}{зелёным}; прибавления этого значения в регистр суммы и инкремент указателя на массив (\hrf{line:unfit1e}, \hrf{line:unfit2e}, \hrf{line:unfit3e}, \hrf{line:unfit4e}), выделены \lh{red}{красным}. Далее в обеих функциях следует условие добора нечётности исходного массива и цикл преобразования суммы к 16-разрядному значению.

\begin{lstlisting}[style=ASMStyle, caption=Код без развёртывания, label={lst:nooptsim}]
// <@\lh{dkgreen}{выделить для стека функции 16 байт}@>
10000000: defffc04 addi   sp,sp,-16
// <@\lh{dkgreen}{сохранить в указатель на фрейм 12й байт (первый элемент) стека}@>
10000004: df000315 stw    fp,12(sp)
// <@\lh{dkgreen}{что в sp(8)?}@>
10000008: dc000215 stw    r16,8(sp)
// <@\lh{dkgreen}{fp = sp+12}@>
1000000c: df000304 addi   fp,sp,12
// <@\lh{dkgreen}{указатель на переданный массив в }@>fp-8
10000010: e13ffe15 stw    r4,-8(fp)
// <@\lh{dkgreen}{переданная длина массива в }@>fp-12
10000014: e17ffd15 stw    r5,-12(fp)
// <@\lh{dkgreen}{сумма = 0}@>
10000018: 0021883a mov    r16,zero
// <@\lh{dkgreen}{переходим по адресу }@>0x44
<@\lh{blue}{1000001c: 00000906 br}@>     10000044<checkSum+0x44> <@\label{line:simbody}@>
// <@\lh{dkgreen}{r2 = массив}@>
10000020: e0bffe17 ldw    r2,-8(fp)
// <@\lh{dkgreen}{r3 = массив + 2 байта}@>
10000024: 10c00084 addi   r3,r2,2
// <@\lh{dkgreen}{сохраняем новый адрес в }@>r3
10000028: e0fffe15 stw    r3,-8(fp)
// <@\lh{dkgreen}{загружаем полуслово по старому указателю на массив}@>
1000002c: 1080000b ldhu   r2,0(r2)
// <@\lh{dkgreen}{маск\'{и}руем младшие 16 битов}@>
10000030: 10bfffcc andi   r2,r2,65535
// <@\lh{dkgreen}{сумма += r2}@>
10000034: 80a1883a add    r16,r16,r2
// <@\lh{dkgreen}{кладём в r2 (оставшуюся) длину массива}@>
10000038: e0bffd17 ldw    r2,-12(fp)
// <@\lh{dkgreen}{длина -= 2}@>
1000003c: 10bfff84 addi   r2,r2,-2
// <@\lh{dkgreen}{сохраняем оставшуюся длину обратно в }@>fp-12
10000040: e0bffd15 stw    r2,-12(fp)
// <@\lh{dkgreen}{загружаем из fp-12 (длина массива) в }@>r2
 <@\lh{blue}{10000044: e0bffd17 ldw}@>     r2,-12(fp) <@\label{line:simcond}@>
// <@\lh{dkgreen}{r2 = длина > 2}@>
10000048: 10800088 cmpgei r2,r2,2
// <@\lh{dkgreen}{((длина > 2) != false)? переходим 0x20}@>
1000004c: 103ff41e bne    r2,zero,10000020<checkSum+0x20>
// <@\lh{dkgreen}{((длина > 2) == false) ? r2 = длина}@>
10000050: e0bffd17 ldw    r2,-12(fp)
// <@\lh{dkgreen}{r2 >= 0 ? переходим 0x78}@>
10000054: 0080080e bge    zero,r2,10000078<checkSum+0x78>
// <@\lh{dkgreen}{r2 < 0 ? r2 = массив}@>
10000058: e0bffe17 ldw    r2,-8(fp)
// <@\lh{dkgreen}{r2 = значение массива}@>
1000005c: 1080000b ldhu   r2,0(r2)
// <@\lh{dkgreen}{r2 = r2 \& 0xffff}@>
10000060: 10bfffcc andi   r2,r2,65535
// <@\lh{dkgreen}{сумма += r2}@>
10000064: 80a1883a add    r16,r16,r2
// <@\lh{dkgreen}{переходим 0x78}@>
10000068: 00000306 br     10000078<checkSum+0x78>
// <@\lh{dkgreen}{r3 = сумма \& 0xffff}@>
1000006c: 80ffffcc andi   r3,r16,65535
// <@\lh{dkgreen}{r2 = сумма >> 16}@>
10000070: 8005d43a srai   r2,r16,16
// <@\lh{dkgreen}{сумма = r2 + r3}@>
10000074: 18a1883a add    r16,r3,r2
// <@\lh{dkgreen}{сумма >> 16}@>
10000078: 8005d43a srai   r2,r16,16
// <@\lh{dkgreen}{r2 != 0 ? переходим 0x6c}@>
1000007c: 103ffb1e bne    r2,zero,1000006c<checkSum+0x6c>
// <@\lh{dkgreen}{r2 == 0 ? r2 = сумма}@>
10000080: 8005883a mov    r2,r16
// <@\lh{dkgreen}{r2 = \textasciitilde(r2|0)}@>
10000084: 0084303a nor    r2,zero,r2
// <@\lh{dkgreen}{сумма = r2}@>
10000088: 1021883a mov    r16,r2
// <@\lh{dkgreen}{r2 = сумма}@>
1000008c: 8005883a mov    r2,r16
// <@\lh{dkgreen}{завершаем выполнение функции, перемещаем обратно указатели}@>
10000090: e6ffff04 addi   sp,fp,-4
10000094: df000117 ldw    fp,4(sp)
10000098: dc000017 ldw    r16,0(sp)
1000009c: dec00204 addi   sp,sp,8
// return
100000a0: f800283a ret
\end{lstlisting}

\begin{lstlisting}[style=ASMStyle, caption=Код с развёртыванием, label={lst:nooptunf}]
// <@\lh{dkgreen}{инициализация}@>
10000000: defffc04 addi   sp,sp,-16
10000004: df000315 stw    fp,12(sp)
10000008: dc000215 stw    r16,8(sp)
1000000c: df000304 addi   fp,sp,12
// <@\lh{dkgreen}{адрес массива}@>
10000010: e13ffe15 stw    r4,-8(fp)
// <@\lh{dkgreen}{длина массива}@>
10000014: e17ffd15 stw    r5,-12(fp)
// <@\lh{dkgreen}{сумма = 0}@>
10000018: 0021883a mov    r16,zero
// <@\lh{dkgreen}{переход 0x8c}@>
1000001c: 00001b06 br     1000008c<checkSum+0x8c>
<@\lh{dkgreen}{10000020: e0bffe17 ldw}@>    r2,-8(fp) <@\label{line:unfit1b}@>
10000024: 10c00084 addi   r3,r2,2
10000028: e0fffe15 stw    r3,-8(fp)
1000002c: 1080000b ldhu   r2,0(r2)
10000030: 10bfffcc andi   r2,r2,65535
<@\lh{red}{10000034: 80a1883a add}@>    r16,r16,r2 <@\label{line:unfit1e}@>
<@\lh{dkgreen}{10000038: e0bffe17 ldw}@>    r2,-8(fp) <@\label{line:unfit2b}@>
1000003c: 10c00084 addi   r3,r2,2
10000040: e0fffe15 stw    r3,-8(fp)
10000044: 1080000b ldhu   r2,0(r2)
10000048: 10bfffcc andi   r2,r2,65535
<@\lh{red}{1000004c: 80a1883a add}@>    r16,r16,r2 <@\label{line:unfit2e}@>
<@\lh{dkgreen}{10000050: e0bffe17 ldw}@>    r2,-8(fp)<@\label{line:unfit3b}@>
10000054: 10c00084 addi   r3,r2,2
10000058: e0fffe15 stw    r3,-8(fp)
1000005c: 1080000b ldhu   r2,0(r2)
10000060: 10bfffcc andi   r2,r2,65535
<@\lh{red}{10000064: 80a1883a add}@>   r16,r16,r2<@\label{line:unfit3e}@>
<@\lh{dkgreen}{10000068: e0bffe17 ldw}@>    r2,-8(fp)<@\label{line:unfit4b}@>
1000006c: 10c00084 addi   r3,r2,2
10000070: e0fffe15 stw    r3,-8(fp)
10000074: 1080000b ldhu   r2,0(r2)
10000078: 10bfffcc andi   r2,r2,65535
<@\lh{red}{1000007c: 80a1883a add}@>    r16,r16,r2<@\label{line:unfit4e}@>
10000080: e0bffd17 ldw    r2,-12(fp)
10000084: 10bffe04 addi   r2,r2,-8
10000088: e0bffd15 stw    r2,-12(fp)
1000008c: e0bffd17 ldw    r2,-12(fp)
10000090: 10800248 cmpgei r2,r2,9
10000094: 103fe21e bne    r2,zero,10000020 <checkSum+0x20>
<@\lh{blue}{10000098: 00000906 br}@>     100000c0 <checkSum+0xc0> <@\label{line:unfsimbody}@>
1000009c: e0bffe17 ldw    r2,-8(fp)
100000a0: 10c00084 addi   r3,r2,2
100000a4: e0fffe15 stw    r3,-8(fp)
100000a8: 1080000b ldhu   r2,0(r2)
100000ac: 10bfffcc andi   r2,r2,65535
100000b0: 80a1883a add    r16,r16,r2
100000b4: e0bffd17 ldw    r2,-12(fp)
100000b8: 10bfff84 addi   r2,r2,-2
100000bc: e0bffd15 stw    r2,-12(fp)
<@\lh{blue}{100000c0: e0bffd17 ldw}@>    r2,-12(fp) <@\label{line:unfsimcond}@>
100000c4: 10800088 cmpgei r2,r2,2
100000c8: 103ff41e bne    r2,zero,1000009c <checkSum+0x9c>
100000cc: e0bffd17 ldw    r2,-12(fp)
100000d0: 0080080e bge    zero,r2,100000f4 <checkSum+0xf4>
100000d4: e0bffe17 ldw    r2,-8(fp)
100000d8: 1080000b ldhu   r2,0(r2)
100000dc: 10bfffcc andi   r2,r2,65535
100000e0: 80a1883a add    r16,r16,r2
100000e4: 00000306 br     100000f4 <checkSum+0xf4>
100000e8: 80ffffcc andi   r3,r16,65535
100000ec: 8005d43a srai   r2,r16,16
100000f0: 18a1883a add    r16,r3,r2
100000f4: 8005d43a srai   r2,r16,16
100000f8: 103ffb1e bne    r2,zero,100000e8 <checkSum+0xe8>
100000fc: 8005883a mov    r2,r16
10000100: 0084303a nor    r2,zero,r2
10000104: 1021883a mov    r16,r2
10000108: 8005883a mov    r2,r16
1000010c: e6ffff04 addi   sp,fp,-4
10000110: df000117 ldw    fp,4(sp)
10000114: dc000017 ldw    r16,0(sp)
10000118: dec00204 addi   sp,sp,8
1000011c: f800283a ret
\end{lstlisting}

В оптимизированной версии кода в листингах \hrf{lst:optsim} и \hrf{lst:optunf} также есть общая часть -- условие добора нечётности массива и цикл преобразования к 16-разрядному значению, начало и конец выделены \lh{red}{красным}.

\begin{lstlisting}[style=ASMStyle, caption=Код -О3 без развёртывания, label={lst:optsim}]
//   unsigned short checkSum(unsigned short* addr, int size) {
//     register unsigned short checksum = 0;
//     register int sum = 0;
// <@\lh{dkgreen}{параметры функции передаются в регистрах r4 (addr) и r5 (size)}@>
// <@\lh{dkgreen}{r2 = длина < 2}@>
10000000: 28800090 cmplti r2,r5,2
// <@\lh{dkgreen}{((длина < 2) != false) ? переходим 0x78}@>
10000004: 10001c1e bne    r2,zero,10000078 <checkSum+0x78>
// <@\lh{dkgreen}{попадаем сюда, если (длина < 2) == false}@>
// <@\lh{dkgreen}{idky записываем во временный регистр r6 значение -2}@>
10000008: 01bfff84 movi   r6,-2
// <@\lh{dkgreen}{временный регистр r7 = длина массива - 2}@>
1000000c: 29ffff84 addi   r7,r5,-2
// <@\lh{dkgreen}{r2 = r7 \& r6, то есть (длина - 2) \& (-2)}@>
// <@\lh{dkgreen}{предполагаю, что это как-то гарантирует чётность длины}@>
10000010: 3984703a and    r2,r7,r6
// <@\lh{dkgreen}{r6 = r4 (указатель на массив) + 2}@>
10000014: 21800084 addi   r6,r4,2
// <@\lh{dkgreen}{r6 = r2 (результат операции с 0х0с) + r6 (указатель на массив +2)}@>
// <@\lh{dkgreen}{предполагаю, что это адрес последнего элемента массива}@>
// <@\lh{dkgreen}{для сравнения в цикле 0х024 - 0х030}@>
10000018: 118d883a add    r6,r2,r6
// <@\lh{dkgreen}{r3 = r4 (указатель на массив)}@>
1000001c: 2007883a mov    r3,r4
// <@\lh{dkgreen}{r2 = 0}@>
10000020: 0005883a mov    r2,zero
// <@\lh{dkgreen}{здесь начался цикл аккумулирования значений из массива}@>
// <@\lh{dkgreen}{цикл перевёрнут, то есть реализован как do {} while();}@>
//   while (size > 1) {
//       sum += *addr++;
//       size -= 2;
//   }
// <@\lh{dkgreen}{в r5 записался 0-й байт массива беззнаково}@>
<@\lh{blue}{10000024: 1940000b ldhu}@>  r5,0(r3)
// <@\lh{dkgreen}{сместили временный указатель на начало массива}@>
10000028: 18c00084 addi   r3,r3,2
// <@\lh{dkgreen}{аккумулируем сумму}@>
1000002c: 1145883a add    r2,r2,r5
// <@\lh{dkgreen}{проверяем выход за пределы (r6 != r3)}@>
<@\lh{blue}{10000030: 30fffc1e bne}@>   r6,r3,10000024 <checkSum+0x24>
// <@\lh{dkgreen}{когда закончили с основным суммирующим циклом}@>
// <@\lh{dkgreen}{во временный регистр r3 записали (((длину массива) - 2) / 2)??!!}@>
// <@\lh{dkgreen}{(r6 == r3) ? r3 = r7 >> 1 (логическое)}@>
10000034: 3806d07a srli   r3,r7,1
// <@\lh{dkgreen}{странная манипуляция длиной массива и адресом, видимо,}@>
// <@\lh{dkgreen}{это связано с тем, что стек растёт отрицательно, поэтому}@>
// <@\lh{dkgreen}{адреса последних элементов имеют отрицательное смещение}@>
// <@\lh{dkgreen}{новый размер массива получаем из половины старого * -2}@>
// <@\lh{dkgreen}{r5 = r3 * (-2)}@>
10000038: 197fffa4 muli   r5,r3,-2
// <@\lh{dkgreen}{r3 += 1 (прибавили к размеру (половины массива) единицу)}@>
1000003c: 18c00044 addi   r3,r3,1
// <@\lh{dkgreen}{судя по всему, получаем реальный размер оставшегося массива}@>
// <@\lh{dkgreen}{r3 += r3}@>
10000040: 18c7883a add    r3,r3,r3
// <@\lh{dkgreen}{прибавляем к начальному адресу массива расчётный размер}@>
// <@\lh{dkgreen}{r7 = r5 + r7 (длина - 2)}@>
10000044: 29cf883a add    r7,r5,r7
// <@\lh{dkgreen}{сравниваем получившийся результат с единицей}@>
// <@\lh{dkgreen}{то есть, проверяем, дошли ли мы до конца массива)}@>
// <@\lh{dkgreen}{r7 = r7 != 1}@>
10000048: 39c00058 cmpnei r7,r7,1
// <@\lh{dkgreen}{r4 += r3}@>
1000004c: 20c9883a add    r4,r4,r3
// <@\lh{dkgreen}{((r7 != 1) != false) ? переходим 0x68}@>
10000050: 3800051e bne    r7,zero,10000068 <checkSum+0x68>
// <@\lh{dkgreen}{((r7 != 1) == false) ? производим финальные действия:}@>
// <@\lh{dkgreen}{приводим полученную сумму к размеру и битовую инверсию}@>
//   sum += *(unsigned char *) addr;
// <@\lh{dkgreen}{в r3 записываем нулевой элемент массива r4 (полуслово, младшие 8 бит)}@>
<@\lh{red}{10000054: 20c0000b ldhu}@>  r3,0(r4)
// <@\lh{dkgreen}{r2 += r3 (нулевой элемент r4)}@>
10000058: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{переходим 0x68}@>
1000005c: 00000206 br     10000068 <checkSum+0x68>
//   while (sum >> 16) {
//       sum = (sum \& 0xffff) + (sum >> 16);
//   }
// <@\lh{dkgreen}{маск\'{и}руем младшие 16 битов}@>
10000060: 10bfffcc andi   r2,r2,65535
// <@\lh{dkgreen}{r2 += r3}@>
10000064: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{r3 = r2 >> 16 (арифметическое)}@>
10000068: 1007d43a srai   r3,r2,16
// <@\lh{dkgreen}{(r3 != 0) ? переходим 0x60}@>
<@\lh{red}{1000006c: 183ffc1e bne}@>   r3,zero,10000060 <checkSum+0x60>
// <@\lh{dkgreen}{(r3 == 0) ? r2 = \textasciitilde(r2|0)}@>
//   checksum = ~sum;
10000070: 0084303a nor    r2,zero,r2
// <@\lh{dkgreen}{Выйти из функции}@>
10000074: f800283a ret
// <@\lh{dkgreen}{r5 = длина == 1}@>
10000078: 29400060 cmpeqi r5,r5,1
// <@\lh{dkgreen}{((длина == 1) == false) ? переходим 0x88}@>
1000007c: 28000226 beq    r5,zero,10000088 <checkSum+0x88>
// <@\lh{dkgreen}{в противном случае, то есть, когда}@>
// <@\lh{dkgreen}{((длина == 1) != false) ? r2 = 0}@>
10000080: 0005883a mov    r2,zero
// <@\lh{dkgreen}{переходим 0x54}@>
10000084: 003ff306 br     10000054 <checkSum+0x54>
// <@\lh{dkgreen}{вернуть из функции -1}@>
10000088: 00bfffc4 movi   r2,-1
// <@\lh{dkgreen}{Выйти из функции}@>
1000008c: f800283a ret
\end{lstlisting}

\begin{lstlisting}[style=ASMStyle, caption=Код -О3 с развёртыванием, label={lst:optunf}]
// <@\lh{dkgreen}{r4 = \&addr, r5 = size}@>
//   unsigned short checkSum(unsigned short* addr, int size) {
       register unsigned short checksum = 0;
       register int sum = 0;
// <@\lh{dkgreen}{Первый, развёрнутый цикл работает по условию size > 8,}@>
// <@\lh{dkgreen}{следовательно, первая проверка - не нужно ли его пропустить}@>
// <@\lh{dkgreen}{r2 = длина < 9}@>
10000000: 28800250 cmplti r2,r5,9
// <@\lh{dkgreen}{если массив не требует обработки развёрнутым циклом}@>
// <@\lh{dkgreen}{((длина < 9) != false) ? переходим 0xe0}@>
10000004: 1000361e bne    r2,zero,100000e0 <checkSum+0xe0>
// <@\lh{dkgreen}{вычисляем, где закончится массив}@>
// <@\lh{dkgreen}{((длина < 9) == false) ? r9 = r5 (длина) - 9}@>
10000008: 2a7ffdc4 addi   r9,r5,-9
// <@\lh{dkgreen}{r9 = r9 (длина - 9) >> 3 (логическое)}@>
1000000c: 4812d0fa srli   r9,r9,3
// <@\lh{dkgreen}{инициализируем переменную куда будем накапливать результат}@>
// <@\lh{dkgreen}{r2 = 0}@>
10000010: 0005883a mov    r2,zero
// <@\lh{dkgreen}{r8 = r9 (изменённая на 0х0с длина массива) + 1}@>
10000014: 4a000044 addi   r8,r9,1
// <@\lh{dkgreen}{r8 = r8 * 8 (логическим сдвигом)}@>
10000018: 401090fa slli   r8,r8,3
// <@\lh{dkgreen}{иными словами считаем, где мы окажемся, когда закончим}@>
// <@\lh{dkgreen}{выполнение нижеследующего цикла}@>
// <@\lh{dkgreen}{r8 = r4 (указатель на массив) + r8}@>
1000001c: 2211883a add    r8,r4,r8
//   while (size > 8) { <@\label{line:unroll}@>
//     sum += *addr++;
//     sum += *addr++;
//     sum += *addr++;
//     sum += *addr++;
//     size -= 8;
//   }
// <@\lh{dkgreen}{далее начинается развёрнутый явно в коде цикл, в котором}@>
// <@\lh{dkgreen}{происходит последовательное чтение данных из массива}@>
// <@\lh{dkgreen}{со сдвигом относительно адреса на +0, +2, +4, +6 байт,}@>
// <@\lh{dkgreen}{аккумулируя прочитанные значения (в результате) в регистр r2}@>
<@\lh{dkgreen}{10000020: 21c0000b ldhu}@>  r7,0(r4)
10000024: 2180008b ldhu   r6,2(r4)
10000028: 20c0010b ldhu   r3,4(r4)
1000002c: 3885883a add    r2,r7,r2
10000030: 308d883a add    r6,r6,r2
10000034: 2080018b ldhu   r2,6(r4)
10000038: 1987883a add    r3,r3,r6
1000003c: 21000204 addi   r4,r4,8
10000040: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{не дошли до конца массива? зацикливаемся, переходим 0x20}@>
<@\lh{dkgreen}{10000044: 413ff61e bne}@>   r8,r4,10000020 <checkSum+0x20>
// <@\lh{dkgreen}{r9 = изменённая на 0х0с длина массива * -8}@>
10000048: 4a7ffe24 muli   r9,r9,-8
// <@\lh{dkgreen}{r5 = реальная переданная длина массива - 8}@>
1000004c: 297ffe04 addi   r5,r5,-8
// <@\lh{dkgreen}{r5 = r9 + r5}@>
10000050: 494b883a add    r5,r9,r5
// <@\lh{dkgreen}{на адрес 0х54 мы попадаем, когда размер исходного массива}@>
// <@\lh{dkgreen}{меньше 9 то есть или сразу при вызове функции, или}@>
// <@\lh{dkgreen}{после прохода по явно развёрнутому циклу}@>
// <@\lh{dkgreen}{во временный регистр r3 записываем результат сравнения}@>
// <@\lh{dkgreen}{r3 = переданный размер массива < 2}@>
10000054: 28c00090 cmplti r3,r5,2
// <@\lh{dkgreen}{((r3 < 2) != false) ? переходим 0xb4}@>
// <@\lh{dkgreen}{то есть если размер массива < 2, то переходим}@>
// <@\lh{dkgreen}{к завершению функции, значит код далее (не совершая переход)}@>
// <@\lh{dkgreen}{это код цикла добора. Последовательно забирая элементы}@>
// <@\lh{dkgreen}{массива мы сравниваем получившийся адрес с}@>
// <@\lh{dkgreen}{исходным +0, +2, +4, и выходим по адресу 0х9с, из чего}@>
// <@\lh{dkgreen}{делаем вывод, что произошло развёртывание компилятором}@>
10000058: 1800161e bne    r3,zero,100000b4 <checkSum+0xb4>
// <@\lh{dkgreen}{((r3 < 2) == false) ? r7 = array[0]}@>
<@\lh{blue}{1000005c: 21c0000b ldhu}@>  r7,0(r4) <@\label{line:unfoptldw0}@>
//<@\lh{dkgreen}{r6 =  размера массива - 2}@>
10000060: 29bfff84 addi   r6,r5,-2
// <@\lh{dkgreen}{r3 = r6 < 2}@>
10000064: 30c00090 cmplti r3,r6,2
// <@\lh{dkgreen}{накапливаем полученную сумму}@>
// <@\lh{dkgreen}{r2 (сумма) += r7}@>
10000068: 11c5883a add    r2,r2,r7
// <@\lh{dkgreen}{((r6 < 2) != false) ? переходим 0x9c}@>
1000006c: 18000b1e bne    r3,zero,1000009c <checkSum+0x9c>
// <@\lh{dkgreen}{((r6 < 2) == false) ? r7 = array[1]}@>
<@\lh{blue}{10000070: 21c0008b ldhu}@>  r7,2(r4) <@\label{line:unfoptldw2}@>
// <@\lh{dkgreen}{уменьшаем размер массива на 4}@>
// <@\lh{dkgreen}{r5 -= 4}@>
10000074: 297fff04 addi   r5,r5,-4
// <@\lh{dkgreen}{временный регистр r3 = r5 (изменённый размер) < 2}@>
10000078: 28c00090 cmplti r3,r5,2
// <@\lh{dkgreen}{накапливаем прочитанный результат}@>
1000007c: 11c5883a add    r2,r2,r7
// <@\lh{dkgreen}{((изменённый размер < 2) != false) ? переходим 0x9c}@>
10000080: 1800061e bne    r3,zero,1000009c <checkSum+0x9c>
// <@\lh{dkgreen}{((изменённый размер < 2) == false) ? r3 = array[2]}@>
<@\lh{blue}{10000084: 20c0010b ldhu}@>  r3,4(r4) <@\label{line:unfoptldw4}@>
// <@\lh{dkgreen}{r5 = изменённый на 0х74 размер массива != 4}@>
10000088: 29400118 cmpnei r5,r5,4
// <@\lh{dkgreen}{накапливаем прочитанный результат}@>
1000008c: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{((r5 != 4) != false) ? переходим 0x9c}@>
10000090: 2800021e bne    r5,zero,1000009c <checkSum+0x9c>
// <@\lh{dkgreen}{((r5 != 4) == false) ? r3 = array[3]}@>
<@\lh{blue}{10000094: 20c0018b ldhu}@>  r3,6(r4) <@\label{line:unfoptldw6}@>
10000098: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{аналогичные простому варианту функции манипуляции}@>
// <@\lh{dkgreen}{с проверкой адреса массива, длины массива с учётом}@>
// <@\lh{dkgreen}{роста стека функции в отрицательные адреса}@>
// <@\lh{dkgreen}{когда закончили с основным суммирующим циклом}@>
// <@\lh{dkgreen}{временный регистр r3 = (((длина массива) - 2) / 2)??!!}@>
// <@\lh{dkgreen}{r3 = r6 >> 1 (логическое)}@>
1000009c: 3006d07a srli   r3,r6,1
// <@\lh{dkgreen}{странная манипуляция длиной массива и адресом, видимо,}@>
// <@\lh{dkgreen}{это связано с тем, что стек растёт отрицательно, поэтому}@>
// <@\lh{dkgreen}{адреса последних элементов имеют отрицательное смещение}@>
// <@\lh{dkgreen}{новый размер массива получаем из половины старого * -2}@>
// <@\lh{dkgreen}{r5 (длина) = r3 * (-2)}@>
100000a0: 197fffa4 muli   r5,r3,-2
// <@\lh{dkgreen}{r3 += 1 (прибавили к размеру (половины массива) единицу)}@>
100000a4: 18c00044 addi   r3,r3,1
// <@\lh{dkgreen}{судя по всему, получаем реальный размер оставшегося массива}@>
// <@\lh{dkgreen}{r3 += r3}@>
100000a8: 18c7883a add    r3,r3,r3
// <@\lh{dkgreen}{к исходному адресу массива прибавляем его расчётный размер}@>
// <@\lh{dkgreen}{r4 += r3}@>
100000ac: 20c9883a add    r4,r4,r3
// <@\lh{dkgreen}{r5 += r6}@>
100000b0: 298b883a add    r5,r5,r6
// <@\lh{dkgreen}{r5 = r5 != 1}@>
100000b4: 29400058 cmpnei r5,r5,1
// <@\lh{dkgreen}{((r5 != 1) != false) ? переходим 0xd0}@>
100000b8: 2800051e bne    r5,zero,100000d0 <checkSum+0xd0>
// <@\lh{dkgreen}{((r5 != 1) == false) ? производим финальные действия:}@>
// <@\lh{dkgreen}{приводим полученную сумму к размеру и битовую инверсию}@>
//   sum += *(unsigned char *) addr;
// <@\lh{dkgreen}{в r3 записываем нулевой элемент массива r4 (полуслово, младшие 8 бит)}@>
<@\lh{red}{100000bc: 20c0000b ldhu}@>  r3,0(r4)
// <@\lh{dkgreen}{накапливаем загруженный результат}@>
// <@\lh{dkgreen}{r2 += r3}@>
100000c0: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{переходим на 0х68}@>
100000c4: 00000206 br     100000d0 <checkSum+0xd0>
//   while (sum >> 16) {
//      sum = (sum & 0xffff) + (sum >> 16);
//   }
// <@\lh{dkgreen}{маскируем младшие 16 битов}@>
100000c8: 10bfffcc andi   r2,r2,65535
// <@\lh{dkgreen}{r2 += r3}@>
100000cc: 10c5883a add    r2,r2,r3
// <@\lh{dkgreen}{r3 = r2 >> 16 (арифметическое)}@>
100000d0: 1007d43a srai   r3,r2,16
// <@\lh{dkgreen}{((r2 >> 16) != false) ? переходим 0xc8}@>
<@\lh{red}{100000d4: 183ffc1e bne}@>   r3,zero,100000c8 <checkSum+0xc8>
// <@\lh{dkgreen}{((r2 >> 16) == false) ? r2 = \textasciitilde(0 | r2)}@>
100000d8: 0084303a nor    r2,zero,r2
// <@\lh{dkgreen}{выход из функции}@>
100000dc: f800283a ret
// <@\lh{dkgreen}{обнуляем регистр r2}@>
100000e0: 0005883a mov    r2,zero
// <@\lh{dkgreen}{переходим на 0х54}@>
100000e4: 003fdb06 br     10000054 <checkSum+0x54>
\end{lstlisting}

В листинге \hrf{lst:optunf} развёрнутый цикл, описанный явно выделен \lh{dkgreen}{зелёным}. В развёрнутом цикле видно обращение по адресу массива со смещением 0, 2, 4 и 6 байт, соответственно. Основной цикл (для задания с явным развёртыванием цикла - это <<цикл добора>>) выделен в листингах \lh{blue}{синим}, причём в листинге \hrf{lst:optunf} очевидна развёртывающая оптимизация компилятора на строках \hrf{line:unfoptldw0}, \hrf{line:unfoptldw2}, \hrf{line:unfoptldw4}, \hrf{line:unfoptldw6} (выделены \lh{blue}{синим}) следует загрузка значений из массива с последующим сдвигом указателя на исходный массив и суммированием прочитанных значений с промежуточным значением суммы (регистр \code{r2}), дополнительные комментарии представлены в листинге.

Дополнительно были отслежены изменения вспомогательных регистров (для мгновенного расчёта адреса чтения из массива по окончании работы развёрнутого цикла)
\begin{lstlisting}[language=C,style=CCodeStyle]
r4=array&  1000
r5=size    100

00 r2=r5<9 false
04         r2==0
08 r9=20-9 91
0c r9>>=3  11
10 r2=0    0
14 r8=r9+1 12
18 r8<<=3  96
1c r8+=r4  1096
20 c=================
24 c
28 l
2c e
30 
34 u
38 n
3c f
40 l
44 d=================
48 r9*=-8  -88
4c r5-=8   92
50 r5+=r9  4
\end{lstlisting}


\section*{Часть II.}
\addcontentsline{toc}{section}{Часть II.}
\subsection*{Задание}
Во второй части работы требовалось изучить отчёт профилировщика, сохранить иерархию вызовов функции main и начало таблицы <<плоского>> профиля. На примере реализованной функции обработки данных в части I определить с помощью Performance Counter, какой оверхед (накладные расходы) в циклах вносит вызов функции \code{mcount} в выполнение функции. Изучите ассемблерный код и найдите в нем вызов функции \code{mcount}. Результаты сохраните в отчет.

\subsection*{Проделанная работа}
Согласно показаний Performance Counter (рис. \hrf{pic:pcpt2}) функция подсчёта контрольной суммы без подключенного профайлера выполнялась в среднем 843.434 микросекунд, а с вызовом \code{mcount} 863.816, то есть на $\approx 20$ мкс больше. 
\begin{figure}[H]
  \centering
  \includegraphics[width=9cm]{01-ess-lab-02-counter.png}
  \caption{Показания Performance Counter для вызовов функции со включенным профилировщиком}
  \label{pic:pcpt2}
\end{figure}

В листинге \hrf{lst:gprofrpt} приведены первые строки плоского профиля выполнения программы, демонстрирующие, что программа находилась 93\% времени в функции \code{main}, то есть, выполняла работу основного цикла (прямые вызовы функции подсчёта контрольной суммы).

\begin{lstlisting}[frame=single, basicstyle={\tiny\ttfamily}, caption=Начало отчёта плоского профиля, label={lst:gprofrpt}, numbers=left, numberstyle=\tiny\color{gray}]
Flat profile:

Each sample counts as 0.001 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
 93.26     13.78    13.78        1    13.78    14.14  main
  6.47     14.74     0.96        4     0.24     0.24  altera_avalon_jtag_uart_close
  0.21     14.77     0.03        1     0.03     0.03  _exit
  0.01     14.77     0.00       14     0.00     0.00  __sfvwrite_r
  0.01     14.77     0.00        5     0.00     0.00  altera_avalon_jtag_uart_write
  0.01     14.77     0.00       28     0.00     0.00  __muldf3
  0.01     14.77     0.00        3     0.00     0.00  _fflush_r
\end{lstlisting}

В листинге \hrf{lst:gprofassemble} отмечен \lh{red}{красным} вызов функции \code{mcount}. Интересно, что адрес функции (\code{0000ddfc <_mcount>:}) считается налету с применением временного регистра.

\begin{lstlisting}[frame=single, basicstyle={\tiny\ttfamily}, caption=Начало отчёта плоского профиля, label={lst:gprofassemble}, numbers=left, numberstyle=\tiny\color{gray}]
10000000 <checkSum>:
10000000:	f811883a 	mov	r8,ra
//<@\lh{black}{уходим считать адрес вызова}@>
<@\lh{red}{10000004:	00000f40 	call}@> 100000f4 <checkSum+0xf4>
10000008:	403f883a 	mov	ra,r8
1000000c:	28800250 	cmplti	r2,r5,9

// ...
// ...
// ...

100000ec:	0005883a 	mov	r2,zero
100000f0:	003fdb06 	br	10000060 <checkSum+0x60>
// <@\lh{black}{записали 1 в старший разряд временного регистра (at = 65536)}@>
100000f4:	00400074 	movhi	at,1
// 65536 - 8708 = 0xDDFC
100000f8:	08777f04 	addi	at,at,-8708
// <@\lh{black}{перемещаемся по адресу 0xddfc}@>
<@\lh{red}{100000fc:	0800683a 	jmp	at}@>

// ...
// ...
// ...

    ddf8:	f800283a 	ret

<@\lh{red}{0000ddfc <\_mcount>:}@>
         * of values for bits 4:2 won't be even (aligning on cache line boundaries
         * will skew it).  Higher bits should be fairly random.

\end{lstlisting}


\newpage
\appendix
\section*{Приложения}
\addcontentsline{toc}{section}{Приложения}
\renewcommand{\thesubsection}{\Alph{subsection}}

\subsection{Снимки экрана с отчётом Performance Counter}
\label{appendix:isrtime}
\begin{figure}[H]
  \centering
  \begin{multicols}{2}
    \includegraphics[width=8cm]{01-ess-lab-02-notcmnoopt.png}

    \includegraphics[width=8cm]{01-ess-lab-02-notcmnoopt10.png}

    \includegraphics[width=8cm]{01-ess-lab-02-notcm2opt.png}

    \includegraphics[width=8cm]{01-ess-lab-02-notcm2opt10.png}

    \includegraphics[width=8cm]{01-ess-lab-02-notcmszopt.png}

    \includegraphics[width=8cm]{01-ess-lab-02-notcmszopt10.png}
    
    \columnbreak

    \includegraphics[width=8cm]{01-ess-lab-02-tcmnoopt.png}
    
    \includegraphics[width=8cm]{01-ess-lab-02-tcmnoopt10.png}

    \includegraphics[width=8cm]{01-ess-lab-02-tcm3opt.png}

    \includegraphics[width=8cm]{01-ess-lab-02-tcm3opt10.png}

    \includegraphics[width=8cm]{01-ess-lab-02-tcmszopt.png}

    \includegraphics[width=8cm]{01-ess-lab-02-tcmszopt10.png}
  \end{multicols}
  \caption{Оригинальные терминальные сообщения Performance Counter}
\end{figure}

\subsection{Показания Performance Counter для подсчёта контрольной суммы}
\label{appendix:checksumTime}
\begin{figure}[H]
  \centering
  \begin{multicols}{2}
    \includegraphics[width=8cm]{01-ess-lab-02-100udpnosim.png}

    \includegraphics[width=8cm]{01-ess-lab-02-100udpnounf.png}
    
    \columnbreak

    \includegraphics[width=8cm]{01-ess-lab-02-100udpo3sim.png}
    
    \includegraphics[width=8cm]{01-ess-lab-02-100udpo3unf.png}
  \end{multicols}
  \caption{Performance Counter для вызова функции checkSum}
\end{figure}

\subsection{Показания Performance Counter для подсчёта контрольной суммы массивов разного объёма}
\label{appendix:udpMemory}
\begin{figure}[H]
  \centering
  \begin{multicols}{2}
    \includegraphics[width=8cm]{01-ess-lab-02-025udpo3sim.png}

    \includegraphics[width=8cm]{01-ess-lab-02-050udpo3sim.png}
    
    \includegraphics[width=8cm]{01-ess-lab-02-100udpo3sim.png}

    \includegraphics[width=8cm]{01-ess-lab-02-200udpo3sim.png}
    
    \columnbreak

    \includegraphics[width=8cm]{01-ess-lab-02-025udpo3unf.png}
    
    \includegraphics[width=8cm]{01-ess-lab-02-050udpo3unf.png}

    \includegraphics[width=8cm]{01-ess-lab-02-100udpo3unf.png}
    
    \includegraphics[width=8cm]{01-ess-lab-02-200udpo3unf.png}
  \end{multicols}
  \caption{Performance Counter для подсчёта контрольной суммыразного объёма данных}
\end{figure}

\begin{lstlisting}[language=C,style=CCodeStyle,caption=Функция расчёта времени выполнения одной итерации цикла]
double count(int datagram_size, double total_time_spent, int actions_in_iteration) {
    return (total_time_spent
            / 99.0                   // function calls 
            / ((datagram_size * 2)   // typecast elements
            / actions_in_iteration)) // 1 for simple 4 for unfolded
            * 1000000;               // seconds to microseconds
}
\end{lstlisting}

\end{document}