# C: Отчёт по второй лабораторной работе
## Оглавление.
[[_TOC_]]
## Задача 1.
### Условие.
Найти среднее геометрическое положительных элементов массива.
### Решение.
Обратился к [википедии](https://en.wikipedia.org/wiki/Geometric_mean) для ознакомления с этой операцией.
$`(\prod^n_{i=1}x_i)^{1 \over n}`$, где $`n`$ -количество элементов.
Разбил задачу на следующие функции:
- `input_array` - ввод массива
- `filter_positive_array` - создание нового массива, содержащего только положительные элементы
- `geometric_mean` - нахождение среднего геометрического элементов массива
- `print_result` - вывод результата
### Тестироание.
Написал тесты и произвёл тестирование программы. Проверяю покрытие тестами.
```
$ gcov main.c
File 'main.c'
Lines executed:100.00% of 19
Creating 'main.c.gcov'
$ gcov array.c
File 'array.c'
Lines executed:90.63% of 32
Creating 'array.c.gcov'
```
Не удалось достичь 100% покрытия кода. Рассмотрим вывод утилиты `gcov`.
```
-: 0:Source:array.c
-: 0:Graph:array.gcno
-: 0:Data:array.gcda
-: 0:Runs:8
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:#include <math.h>
-: 3:
-: 4:#include "array.h"
-: 5:
8: 6:int input_array(int *array, int *lenght)
-: 7:{
8: 8: if (!array || !lenght)
#####: 9: return POINTER_ERROR;
-: 10:
8: 11: int err = INPUT_SUCCESS;
-: 12:
8: 13: printf("Input lenght of array: ");
8: 14: if (scanf("%d", lenght) != 1 || *lenght <= 0 || *lenght > MAX_ARRAY_SIZE)
3: 15: err = INPUT_ERROR;
8: 16: if (!err)
5: 17: printf("Input array elements: ");
-: 18:
39: 19: for (int i = 0; i < (*lenght) && !err; i++)
-: 20: {
31: 21: if (scanf("%d", array + i) != 1)
1: 22: err = INPUT_ERROR;
-: 23: }
-: 24:
8: 25: return err;
-: 26:}
-: 27:
4: 28:int filter_positive_array(int *array, int *lenght, int *filtered_array, int *filtered_lenght)
-: 29:{
4: 30: if (!array || !lenght || !filtered_array || !filtered_lenght)
#####: 31: return POINTER_ERROR;
-: 32:
4: 33: *filtered_lenght = 0;
32: 34: for (int i = 0; i < *lenght; i++)
28: 35: if (array[i] > 0)
-: 36: {
12: 37: filtered_array[*filtered_lenght] = array[i];
12: 38: (*filtered_lenght)++;
-: 39: }
-: 40:
4: 41: return 0;
-: 42:}
3: 43:double geometric_mean(int *array, int *lenght)
-: 44:{
3: 45: if (!array || !lenght)
#####: 46: return POINTER_ERROR;
-: 47:
3: 48: int product = 1;
-: 49:
15: 50: for (int i = 0; i < *lenght; i++)
12: 51: product *= array[i];
-: 52:
3: 53: return pow(product, 1.0 / (double)(*lenght));
-: 54:}
-: 55:
3: 56:void print_result(double result)
-: 57:{
3: 58: printf("Result is %lf", result);
3: 59:}
```
Не покрыты тестами только участки кода, отвечающие за проверку нулевых указателей.
## Задача 2.
### Условие.
Сформируйте новый массив из элементов исходного массива. При этом в новый массив
помещаются (копируются) элементы исходного массива, которые являются числами Армстронга.
### Решение.
Обратился к [википедии](https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%90%D1%80%D0%BC%D1%81%D1%82%D1%80%D0%BE%D0%BD%D0%B3%D0%B0) для ознакомления с данным понятием.
Любое число Армстронга можно представить в виде $`n=\sum _{{i=1}}^{k}{d_i}^{k}`$, где $`k`$ - количество разрядов числа, а $`d_i`$ - $`i`$-ый разряд числа.
Разбил задачу на следующие функции:
- `input_array` - ввод массива
- `output_array` - вывод массива
- `filter_armstrong_array` - создание нового массива, содержащего только числа Армстронга
- `is_armstrong_number` - проверка числа на число Армстронга
- `get_digits_of_number` - возращает количество разрядов числа
- `my_pow` - возведение в степень целого числа
### Тестирование.
Написал тесты для задачи и произвел тестирование. Провел покрытие кода решения тестами.
```
$ gcov main.c
File 'main.c'
Lines executed:100.00% of 16
Creating 'main.c.gcov'
$ gcov untils.c
File 'untils.c'
Lines executed:94.34% of 53
Creating 'untils.c.gcov'
```
Не удалось достичь 100% покрытия кода. Рассмотрим результаты работы `gcov`.
```
-: 0:Source:untils.c
-: 0:Graph:untils.gcno
-: 0:Data:untils.gcda
-: 0:Runs:8
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:#include "untils.h"
-: 3:
8: 4:int input_array(long long *array, int *lenght)
-: 5:{
8: 6: if (!array || !lenght)
#####: 7: return POINTER_ERROR;
-: 8:
8: 9: int err = INPUT_SUCCESS;
-: 10:
8: 11: printf("Input lenght of array: ");
8: 12: if (scanf("%d", lenght) != 1 || *lenght <= 0 || *lenght > MAX_ARRAY_SIZE)
3: 13: err = INPUT_ERROR;
8: 14: if (!err)
5: 15: printf("Input array elements: ");
-: 16:
36: 17: for (int i = 0; i < (*lenght) && !err; i++)
-: 18: {
28: 19: if (scanf("%lld", array + i) != 1)
1: 20: err = INPUT_ERROR;
-: 21: }
-: 22:
8: 23: return err;
-: 24:}
-: 25:
3: 26:int output_array(long long *array, int lenght)
-: 27:{
3: 28: if (!array)
#####: 29: return POINTER_ERROR;
-: 30:
3: 31: printf("Your array is \n");
17: 32: for (int i = 0; i < lenght; i++)
14: 33: printf("%lld ", array[i]);
3: 34: printf("\n");
-: 35:
3: 36: return 0;
-: 37:}
-: 38:
4: 39:int filter_armstrong_array(long long *array, int *lenght, long long *filtered_array, int *filtered_lenght)
-: 40:{
4: 41: if (!array || !lenght || !filtered_array || !filtered_lenght)
#####: 42: return POINTER_ERROR;
-: 43:
4: 44: *filtered_lenght = 0;
29: 45: for (int i = 0; i < *lenght; i++)
25: 46: if (is_armstrong_number(array[i]))
-: 47: {
14: 48: filtered_array[*filtered_lenght] = array[i];
14: 49: (*filtered_lenght)++;
-: 50: }
-: 51:
4: 52: return 0;
-: 53:}
-: 54:
25: 55:int is_armstrong_number(long long number)
-: 56:{
25: 57: int result = number >= 0;
-: 58:
25: 59: int digits = get_digits_of_number(number);
-: 60:
25: 61: long long temp_result = 0;
25: 62: long long temp_number = number;
-: 63:
25: 64: if (result)
-: 65: {
123: 66: while (temp_number > 0 && temp_result <= number)
-: 67: {
91: 68: temp_result += my_pow(temp_number % 10, digits);
91: 69: temp_number /= 10;
-: 70: }
16: 71: result = temp_result == number;
-: 72: }
-: 73:
25: 74: return result;
-: 75:}
-: 76:
91: 77:long long my_pow(const long long number, const int power)
-: 78:{
91: 79: long long result = 1;
816: 80: for (int i = 0; i < power; i++)
725: 81: result *= number;
91: 82: return result;
-: 83:}
-: 84:
25: 85:int get_digits_of_number(const long long number)
-: 86:{
25: 87: int digits = 1;
25: 88: long long temp = 10;
126: 89: while (temp < number)
-: 90: {
76: 91: digits++;
76: 92: temp *= 10;
-: 93: }
25: 94: return digits;
-: 95:}
```
Не достигаюся только строки кода, отвечающие за обработку нулевых указателей.
## Задача 3.
### Условие.
Вставьте в исходный массив после каждого положительного элемента этот же элемент,
записанный наоборот.
### Решение.
Заметил, что размер массива может увеличиться максимум в два раза, поэтому принял решение выделять в два раза больше пямити для нового массива, для предотвращения ошибок выхода за пределы выделенной памяти.
Разбил решение на следующие функции:
- `input_array` - ввод массива
- `output_array` - вывод массива
- `generate_new_array` - обработка массива согласно условию
- `insert_array` - вставка элемента в определённую позицию массива со сдвигом всех элементов
- `copy_array` - копирование массива в новую область памяти
- `reverse_number` - функция возвращает обращенное число
### Тестирование.
Написал тесты для задачи и произвел тестирование. Провел покрытие кода решения тестами.
```
$ gcov main.c
File 'main.c'
Lines executed:100.00% of 16
Creating 'main.c.gcov'
$ gcov untils.c
File 'untils.c'
Lines executed:92.16% of 51
Creating 'untils.c.gcov'
```
Не удалось достичь 100% покрытия кода. Рассмотрим результаты работы `gcov`.
```
-: 0:Source:untils.c
-: 0:Graph:untils.gcno
-: 0:Data:untils.gcda
-: 0:Runs:7
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:#include "untils.h"
-: 3:
7: 4:int input_array(long long *array, int *lenght)
-: 5:{
7: 6: if (!array || !lenght)
#####: 7: return POINTER_ERROR;
-: 8:
7: 9: int err = INPUT_SUCCESS;
-: 10:
7: 11: printf("Input lenght of array: ");
7: 12: if (scanf("%d", lenght) != 1 || *lenght <= 0 || *lenght > MAX_ARRAY_SIZE)
3: 13: err = INPUT_ERROR;
7: 14: if (!err)
4: 15: printf("Input array elements: ");
-: 16:
28: 17: for (int i = 0; i < (*lenght) && !err; i++)
-: 18: {
21: 19: if (scanf("%lld", array + i) != 1)
1: 20: err = INPUT_ERROR;
-: 21: }
-: 22:
7: 23: return err;
-: 24:}
-: 25:
3: 26:int output_array(long long *array, int lenght)
-: 27:{
3: 28: if (!array)
#####: 29: return POINTER_ERROR;
-: 30:
3: 31: printf("Your array is \n");
28: 32: for (int i = 0; i < lenght; i++)
25: 33: printf("%lld ", array[i]);
3: 34: printf("\n");
-: 35:
3: 36: return 0;
-: 37:}
-: 38:
-: 39:
3: 40:int generate_new_array(const long long *array, const int lenght, long long *generated, int *generated_lenght)
-: 41:{
3: 42: int err = copy_array(generated, array, lenght);
-: 43:
3: 44: if (!err)
-: 45: {
3: 46: *generated_lenght = lenght;
21: 47: for (int i = 0; i < *generated_lenght; i++)
-: 48: {
18: 49: if (generated[i] > 0)
-: 50: {
7: 51: insert_array(reverse_number(generated[i]), i + 1, generated, *generated_lenght);
7: 52: (*generated_lenght)++;
7: 53: i++;
-: 54: }
-: 55: }
-: 56: }
-: 57:
3: 58: return err;
-: 59:}
-: 60:
7: 61:int insert_array(long long element, int position, long long *array, int lenght)
-: 62:{
7: 63: if (!array)
#####: 64: return POINTER_ERROR;
-: 65:
18: 66: for (int i = lenght; i > position; i--)
11: 67: array[i] = array[i - 1];
-: 68:
7: 69: array[position] = element;
-: 70:
7: 71: return 1;
-: 72:}
-: 73:
3: 74:int copy_array(long long *dest_array, const long long *source_array, const int lenght)
-: 75:{
3: 76: if (!dest_array || !source_array)
#####: 77: return POINTER_ERROR;
-: 78:
21: 79: for (int i = 0; i < lenght; i++)
18: 80: dest_array[i] = source_array[i];
-: 81:
3: 82: return 0;
-: 83:}
-: 84:
7: 85:long long reverse_number(long long number)
-: 86:{
7: 87: long long result = 0;
-: 88:
57: 89: while (number > 0)
-: 90: {
43: 91: result *= 10;
43: 92: result += number % 10;
43: 93: number /= 10;
-: 94: }
-: 95:
7: 96: return result;
-: 97:}
```
Не достигаюся только строки кода, отвечающие за обработку нулевых указателей.
## Задача 4.
### Условие.
Упорядочите исходный массив по возрастанию выбором.
### Решение.
С данным методом сортировки уже работал в рамках лабораторных работ и семинаров по программированию 1-го семестра. Кратко дам описание алгоритму, описав его шаги:
- находим номер минимального значения в текущем списке
- производим обмен этого значения со значением первой неотсортированной позиции (обмен не нужен, если минимальный элемент уже находится на данной позиции)
- теперь сортируем хвост списка, исключив из рассмотрения уже отсортированные элементы.
Нужно обратить внимание, что данная реализация является неустойчивой сортировкой, но в рамках данного задания это не появляет на результаты работы программы.
Разбил решение на следующие функции:
- `input_array` - ввод массива
- `output_array` - вывод массива
- `selection_sort` - сортировка массива
### Тестирование.
Написал тесты для задачи и произвел тестирование. Провел покрытие кода решения тестами.
```
$ gcov main.c
File 'main.c'
Lines executed:100.00% of 12
Creating 'main.c.gcov'
$ gcov untils.c
File 'untils.c'
Lines executed:91.18% of 34
Creating 'untils.c.gcov'
```
Не удалось достичь 100% покрытия кода. Рассмотрим результаты работы `gcov`.
```
-: 0:Source:untils.c
-: 0:Graph:untils.gcno
-: 0:Data:untils.gcda
-: 0:Runs:7
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:#include "untils.h"
-: 3:
7: 4:int input_array(long long *array, int *lenght)
-: 5:{
7: 6: if (!array || !lenght)
#####: 7: return POINTER_ERROR;
-: 8:
7: 9: int err = INPUT_SUCCESS;
-: 10:
7: 11: printf("Input lenght of array: ");
7: 12: if (scanf("%d", lenght) != 1 || *lenght <= 0 || *lenght > MAX_ARRAY_SIZE)
3: 13: err = INPUT_ERROR;
7: 14: if (!err)
4: 15: printf("Input array elements: ");
-: 16:
40: 17: for (int i = 0; i < (*lenght) && !err; i++)
-: 18: {
33: 19: if (scanf("%lld", array + i) != 1)
1: 20: err = INPUT_ERROR;
-: 21: }
-: 22:
7: 23: return err;
-: 24:}
-: 25:
3: 26:int output_array(long long *array, int lenght)
-: 27:{
3: 28: if (!array)
#####: 29: return POINTER_ERROR;
-: 30:
3: 31: printf("Your array is \n");
33: 32: for (int i = 0; i < lenght; i++)
30: 33: printf("%lld ", array[i]);
3: 34: printf("\n");
-: 35:
3: 36: return 0;
-: 37:}
-: 38:
3: 39:int selection_sort(long long *array, const int lenght)
-: 40:{
3: 41: if (!array)
#####: 42: return POINTER_ERROR;
-: 43:
30: 44: for (int i = 0; i < lenght - 1; i++)
-: 45: {
27: 46: long long min_element = array[i];
27: 47: int min_position = i;
-: 48:
162: 49: for (int j = i + 1; j < lenght; j++)
-: 50: {
135: 51: if (min_element > array[j])
-: 52: {
50: 53: min_element = array[j];
50: 54: min_position = j;
-: 55: }
-: 56: }
-: 57:
27: 58: array[min_position] = array[i];
27: 59: array[i] = min_element;
-: 60: }
-: 61:
3: 62: return 0;
-: 63:}
```
Не достигаюся только строки кода, отвечающие за обработку нулевых указателей.
## Задача 5.
### Условие.
Выполните обработку массива указанным образом:
Вычислить значение `x[0]*y[0] + x[1]*y[1] + ... + x[k]*y[k]`, где `x[i]` – отрицательные элементы массива `a` из `n` элементов, взятые в порядке их следования; `y[i]` – положительные элементы этого массива, взятые в обратном порядке; `k = min(p,q)`, где `p` – количество положительных элементов массива `a`, `q` – количество отрицательных элементов этого массива.
При решении пятой задачи в методических целях нельзя использовать выражение вида `a[i]` и вообще квадратные скобки. Вместо указанного выражения используется выражение `*pa`, где `pa` - указатель на `i`-ый элемент массива (именно на `i`-ый элемент, а не выражение вида `*(pa + i)`). Также нельзя передавать как аргумент размер массива в элементах. Вместо этого предлагается использовать пару указателей: на первый элемент массива и на элемент массива, расположенный за последним. Ситуация, когда эти указатели совпадают, означает пустоту обрабатываемого массива.
### Решение.
Опишу алгоритм обработки массива. Беру два указателя, один указывает на начало (далее `A`) массива, другой на конец (далее `B`). Смещаю `A` вправо пока не встречу отрицательный элемент, далее смещаю `B` влево, пока не встречу положительный элемент, повторяю данные смещения пока `A` или `B` не выйдут за границы массива.
Разбил решение на следующие функции:
- `input_array` - ввод массива
- `process_array` - обработка массива согласно условию
- `print_result` - вывод результата
### Тестирование.
Написал тесты для задачи и произвел тестирование. Провел проверку покрытия кода решения тестами.
```
$ gcov main.c
File 'main.c'
Lines executed:100.00% of 13
Creating 'main.c.gcov'
$ gcov untils.c
File 'untils.c'
Lines executed:94.12% of 34
Creating 'untils.c.gcov'
```
Не удалось достичь 100% покрытия кода. Рассмотрим результаты работы `gcov`.
```
-: 0:Source:untils.c
-: 0:Graph:untils.gcno
-: 0:Data:untils.gcda
-: 0:Runs:8
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:#include "untils.h"
-: 3:
8: 4:int input_array(long long *array, long long **end)
-: 5:{
8: 6: if (!array || !end)
#####: 7: return POINTER_ERROR;
-: 8:
8: 9: int err = INPUT_SUCCESS;
-: 10:
8: 11: int lenght = 0;
-: 12:
8: 13: printf("Input lenght of array: ");
-: 14:
8: 15: if (scanf("%d", &lenght) != 1 || lenght <= 0 || lenght > MAX_ARRAY_SIZE)
3: 16: err = INPUT_ERROR;
-: 17:
8: 18: if (!err)
5: 19: printf("Input array elements: ");
-: 20:
8: 21: *end = array + lenght;
-: 22:
38: 23: for (long long *i = array; i != *end && !err; i++)
-: 24: {
30: 25: if (scanf("%lld", i) != 1)
1: 26: err = INPUT_ERROR;
-: 27: }
-: 28:
8: 29: return err;
-: 30:}
-: 31:
4: 32:int process_array(long long *array, long long *end, long long *result)
-: 33:{
4: 34: if (!array || !end || !result)
#####: 35: return POINTER_ERROR;
-: 36:
4: 37: *result = 0;
-: 38:
4: 39: long long *i = array;
4: 40: long long *j = end - 1;
-: 41:
22: 42: while (i < end && j > array)
-: 43: {
45: 44: while (*i >= 0 && i < end)
17: 45: i++;
39: 46: while (*j <= 0 && j > array)
11: 47: j--;
-: 48:
14: 49: if (i < end && j > array)
10: 50: *result += (*i) * (*j);
-: 51:
14: 52: i++;
14: 53: j--;
-: 54: }
-: 55:
4: 56: return *result == 0;
-: 57:}
-: 58:
3: 59:void print_result(long long result)
-: 60:{
3: 61: printf("Result is %lld\n", result);
3: 62:}
```
Не достигаюся только строки кода, отвечающие за обработку нулевых указателей.
### Возникшие проблемы.
Не понял из условия задания, что массив нулевых элементов является исключительной ситуацей. Исправил решение в ревизии 8380141069d5c1b4bc82d8498f1284ea18d12489 и тесты в ревизии 1f2b3f934b1941f0508d675f819580c9f9117989.
## Задача 6.
### Условие.
На основе задачи 5 проведите сравнение производительности разных способов работы с
элементами массива
- использование операции индексации `a[i]`;
- формальная замена операции индексации на выражение `*(a + i)`;
- использование указателей для работы с массивом.
### Решение.
Ознакомился с работой функции `gettimeofday` из библиотеки `<sys/time.h>`. Повторил стандарт оформления таблиц в Markdown для их автоматической генерации. Ознакомился функциями `rand`, `srand` библиотеки `<stdlib.h>` для генерации псевдослучайных массивов.
Переписал функцию, изменив способ обращения к массиву. Написал функции тестирования каждого способа. Решение разбито на следующие функции:
- Первый метод идексации с помощью `a[i]`
- `process_1` - нахождение результата
- `test_process_1` - тестирование функции
- Второй метод идексации с помощью `*(a + i)`
- `process_2` - нахождение результата
- `test_process_2` - тестирование функции
- Третий метод идексации, использую указатели.
- `process_3` - нахождение результата
- `test_process_3` - тестирование функции
- Вывод таблицы
- `print_table_header` - печать заголовка таблицы
- `print_table_row` - печать строки таблицы
- Работа с генератором псевдослучайных чисел
- `init_random_generator` - сброс генератора
- `random_array` - заполнения массива случайными значениями
- Работа с массивами
- `copy_array` - копирования массива в другую область памяти
- `output_array` - вывод содержимого массива
### Тестирование.
Для достижения более объективных результатов тестирования, использовал виртуальную машину со следующими характеристиками:
- CPU: 1 виртуальное ядро Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
- RAM: 512 MB DDR3 1600 MHz
- Виртулизация: Hyper-V
- ОС: Ubuntu 19.10 (GNU/Linux 5.3.0-18-generic x86_64)
> В следущей задаче буду ссылаться на данную сборку.
Провел тестирование, привожу результаты с включенной оптимизацией компиляции и выключенной.
**Таблица 1. Тестирование с включенной оптимизацией компилятора `gcc`**
|Repeats| Size| a[i]| *(a+i)|Pointer|
|-------|-------|-------|-------|-------|
| 10| 10| 50| 54| 52|
| 10| 100| 67| 60| 64|
| 10| 500| 90| 83| 81|
| 10| 1000| 272| 120| 110|
| 100| 10| 683| 564| 545|
| 100| 100| 703| 620| 594|
| 100| 500| 1142| 978| 968|
| 100| 1000| 2073| 1216| 1169|
| 500| 10| 3669| 3809| 3630|
| 500| 100| 3400| 2910| 3246|
| 500| 500| 7689| 4976| 4755|
| 500| 1000| 10208| 6701| 6793|
| 1000| 10| 6123| 6554| 6011|
| 1000| 100| 7350| 6468| 10068|
| 1000| 500| 14422| 10400| 11466|
| 1000| 1000| 19898| 13782| 13574|
| 10000| 10| 66971| 60695| 65082|
| 10000| 100| 68703| 79796| 64733|
| 10000| 500| 121940| 99124| 109348|
| 10000| 1000| 178541| 143954| 130838|
| 100000| 10| 604342| 598117| 597620|
| 100000| 100| 727283| 668063| 667098|
| 100000| 500|1118172| 987837| 933543|
| 100000| 1000|1692489|1433616|1285605|
**Таблица 2. Тестирование без оптимизаций компилятора**
|Repeats| Size| a[i]| *(a+i)|Pointer|
|-------|-------|-------|-------|-------|
| 10| 10| 66| 65| 67|
| 10| 100| 66| 66| 63|
| 10| 500| 295| 98| 84|
| 10| 1000| 147| 148| 126|
| 100| 10| 609| 522| 514|
| 100| 100| 737| 814| 954|
| 100| 500| 998| 1033| 955|
| 100| 1000| 3187| 1687| 1414|
| 500| 10| 3291| 3415| 2922|
| 500| 100| 3896| 3155| 3331|
| 500| 500| 6852| 5061| 5217|
| 500| 1000| 9676| 8137| 7013|
| 1000| 10| 6478| 6427| 6074|
| 1000| 100| 8543| 7179| 6560|
| 1000| 500| 15227| 10956| 10276|
| 1000| 1000| 20738| 17018| 14030|
| 10000| 10| 62789| 63287| 63125|
| 10000| 100| 76243| 67858| 67356|
| 10000| 500| 125126| 106430| 95977|
| 10000| 1000| 170570| 151215| 127776|
| 100000| 10| 606538| 592828| 591332|
| 100000| 100| 714777| 685026| 658594|
| 100000| 500|1234805|1094469| 964170|
| 100000| 1000|2252282|1733109|1472254|
### Ответы на вопросы.
1. На что влияет размер массива?
Так как данный алгоритм линейный, время выполнения программы линейно зависимо от размер массива. С бо́льшим количеством элементов массива более явно видны различия методов.
2. Почему приходится выполнять не один замер, а несколько?
На малых количествах повторений разница между разнымими способами работы с массивом минимальна (особенно с включенной оптимизацией), чтобы эта разница была больше погрешности измерения, необходимо произвести бо́льшее количество измерений.
3. Какой способ работы с элементами массива оказался самым производительным? Как вы объясняете этот результат?
Способ работы с указателями оказался самым производительным, потому что в нём опускаются арифметические операции по нахождению адреса памяти для нужного элемента массива. При хранении номера элемента, а не его адреса, выполняется сложение адреса начала массива и номера нужного элемента, умножего на размер одной ячейки.
## Задача 7.
### Условие.
Проведите профилирование программы, написанной для решения задачи 4. Проанализируйте полученные данные профилирования. Часть этих данных приведите в отчете.
### Внесенные правки и особенности решения.
Изменил количество выделяемой памяти до 20 000 элементов. (исправил константу)
Тестированиее проводил на виртуальной машине, характеристики которой описал в задаче 6.
### Ответы на вопросы.
1. **Совпадают ли ваши представления о времени работы той или иной функции с тем, что вы получили на практике? Если нет, то для какой функции и почему?**
Мои представления совпадают.
2. **Увеличьте количество элементов в массиве до 10000, выполните профилирование. Что
изменилось? В ответе приведите как сами данные, так и объяснение полученных
результатов.**
```Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
100.85 0.71 0.71 1 705.95 705.95 selection_sort
0.00 0.71 0.00 1 0.00 0.00 input_array
0.00 0.71 0.00 1 0.00 0.00 output_array
```
Ввод и вывод массива выполняются практически мгновенно, из-за того, что их сложность линейна относительно размера массива $`O(n)`$. Сортировка занимает бо́льшую часть времени, так как её сложность, как минимум $`O(n!)`$.
3. **Уменьшите количество элементов до 10, выполните профилирование. Что изменилось?
В ответе приведите как сами данные, так и объяснение полученных результатов.**
```
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
0.00 0.00 0.00 1 0.00 0.00 input_array
0.00 0.00 0.00 1 0.00 0.00 output_array
0.00 0.00 0.00 1 0.00 0.00 selection_sort
```
Произвести профилирование не удалось из-за очень быстрой работы алгоритма.
4. **Изучите, что делают ключи “-O1”, “-O2”, “-O3”. Выполните профилирование вашей
программы, увеличив размер массива до 10000 элементов, для каждого из этих ключей.
Проанализируйте полученные результаты, сделайте выводы.**
Тестирование произвожу на 20 000 элементов. Хочу приложить отрывок [статьи](https://wiki.gentoo.org/wiki/GCC_optimization/ru#-O)
>>>
Давайте исследуем каждый уровень оптимизации:
- `-O0`: Этот уровень (буква "O" и ноль за ней) отключает оптимизацию полностью и является уровнем по умолчанию, если никакого уровня с префиксом `-O` не указано в переменных `CFLAGS` или `CXXFLAGS`. Это сокращает время компиляции и может улучшить данные для отладки, но некоторые приложения не будут работать должным образом без оптимизации. Эта опция не рекомендуется, за исключением использования в целях отладки.
- `-O1`: Это наиболее простой уровень оптимизации. Компилятор попытается сгенерировать быстрый, занимающий меньше объема код, без затрачивания наибольшего времени компиляции. Он достаточно простой, но должен всегда выполнять свою работу.
- `-O2`: Шаг вперед от `-O1`. *Рекомендуемый* уровень оптимизации, до тех пор пока не понадобится что-то особенное. `-O2` активирует несколько дополнительных флагов вдобавок к флагам, активированных `-O1`. С параметром `-O2`, компилятор попытается увеличить производительность кода без нарушения размера, и без затрачивания большого количества времени компиляции. На этом уровне могут быть использованы SSE и AVX, но YMM-регистры не будут использоваться пока не будет включена опция `-ftree-vectorize`.
- `-O3`: Это наибольший возможный уровень оптимизации. Включает оптимизации, являющейся дорогостоящей с точки зрения времени компиляции и потребления памяти. Компиляция с `-O3` не является гарантированным способом повышения производительности, и на самом деле во многих случаях может привести к замедлению системы из-за больших двоичных файлов и увеличения потребления памяти. `-O3` известен также тем, что ломает несколько пакетов. Использование `-O3` не рекомендуется. Хотя, это также включает `-ftree-vectorize`, и циклы в коде векторизируются и используют регистры AVX YMM.
- `-Os`: На этом уровне код будет оптимизирован по объему. Он активирует все параметры `-O2`, которые не приводят к увеличению размера генерируемого кода. Он может быть полезным на компьютерах, которые обладают чрезвычайно ограниченным пространством жесткого диска и/или процессоры с небольшим размером кэша.
- `-Og`: В GCC 4.8 был введен новый общий уровень оптимизации `-Og`. Он удовлетворяет потребность в быстрой компиляции и имеет превосходные возможности для отладки, обеспечивая при этом приемлемый уровень производительности во время выполнения. Общий опыт разработки должен быть лучше, чем с уровнем оптимизации по умолчанию `-O0`. Обратите внимание, что `-Og` не означает `-g`, он просто отключает оптимизацию кода, которая может помешать отладке.
- `-Ofast`: Новое в GCC 4.7, состоит из `-O3` плюс `-ffast-math`, `-fno-protect-parens`, и `-fstack-arrays`. Этот параметр нарушает строгое соответствие стандарту, и не рекомендуется для использования.
Как упомянуто ранее, параметр `-O2` - рекомендуемый уровень оптимизации. Если компиляция пакета выдает сообщение об ошибке и не используется параметр `-O2`, то попробуйте перекопилировать с этой опцией. В качестве выхода, попробуйте установить переменные `CFLAGS` и `CXXFLAGS` на наименьший уровень оптимизации, такой как `-O1`, или даже `-O0 -g2 -ggdb` (для сообщения об ошибках и проверки возможных проблем).
>>>
0. Без оптимизации (Ключ компиляции `-O0`)
```
% cumulative self self total
time seconds seconds calls s/call s/call name
100.85 2.71 2.71 1 2.71 2.71 selection_sort
0.00 2.71 0.00 1 0.00 0.00 input_array
0.00 2.71 0.00 1 0.00 0.00 output_array
```
1. Оптимизация (Ключ компиляции `-O1`)
```
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
100.97 0.97 0.97 1 969.32 969.32 selection_sort
0.00 0.97 0.00 1 0.00 0.00 input_array
0.00 0.97 0.00 1 0.00 0.00 output_array
```
2. Оптимизация (Ключ компиляции `-O2`)
```
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
101.17 1.90 1.90 selection_sort
```
3. Оптимизации с применением эксперементальных методов (Ключ компиляции `-O3`)
```
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
101.17 1.88 1.88 selection_sort
```
### Некоторые выводы.
Из результатов сборки программы с разными параметрами оптимизации можем сделать некоторые выводы:
- опитимизация может сильно увеличить скорость программы (в наших примерах на 170% быстрее с ключем `-O1`, по сравнению с отключенной оптимизацией)
- применения избыточных оптимизаций компилятора может наоборот снизить скорость работы программы, особенно для реализации стандартных алгоритмов
- применение некоторых оптимизаций может повлечь за собой не работоспособность программы