958 lines
55 KiB
TeX
958 lines
55 KiB
TeX
|
\documentclass[a4paper,fontsize=14bp]{article}
|
|||
|
|
|||
|
\input{../common-preamble}
|
|||
|
\input{../fancy-listings-preamble}
|
|||
|
\input{../bmstu-preamble}
|
|||
|
\numerationTop
|
|||
|
\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}
|