BMSTU/01-ess-lab-02-report.tex

963 lines
55 KiB
TeX
Raw Permalink Normal View History

\documentclass[a4paper]{article}
2023-10-24 15:47:15 +03:00
\input{settings/common-preamble}
\input{settings/fancy-listings-preamble}
\input{settings/bmstu-preamble}
\numerationTop
2023-10-24 15:47:15 +03:00
\def\makeyear{2021}
\def\grpNum{11М}
\begin{document}
\fontsize{14}{21}\selectfont
\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*{Оптимизация и заполнение памяти}
2023-10-24 15:47:15 +03:00
В процессе выполнения программы были произведены измерения количества тактов, затрачиваемых на одну итерацию неразвернутого цикла при включенной оптимизации -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}
2023-10-24 15:47:15 +03:00
В оптимизированной версии кода в листингах \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:unrollopt}@>
// 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*{Проделанная работа}
2023-10-24 15:47:15 +03:00
Согласно показаний 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}
2023-10-24 15:47:15 +03:00
В листинге \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}