SlideShare una empresa de Scribd logo
1 de 44
Descargar para leer sin conexión
Углубленное
программирование
на языке C++
Алексей Петров
Лекция №2.
Дополнительные вопросы
организации и
использования памяти в
программах на языке C
1. Многомерные массивы.
2. Выравнивание и упаковка
переменных составных типов.
3. Системные аспекты выделения и
освобождения памяти.
4. Взаимодействие с операционной
системой.
5. Оптимизация работы с кэш-памятью
ЦП ЭВМ.
6. Постановка индивидуальных задач к
практикуму №2.

2
Многомерные массивы
 Двумерный массив — объект данных T a[N][M], который:
 содержит N последовательно расположенных в памяти строк
по M элементов типа T в каждой;
 в общем и целом инициализируется аналогично одномерным массивам;
 по характеристикам выравнивания идентичен объекту T a[N * M], что
сводит его двумерный характер к удобному умозрительному приему,
упрощающему обсуждение и визуализацию порядка размещения
данных.

 Массивы размерности больше двух считаются многомерными,
при этом (N + 1)-мерные массивы индуктивно определяются как
линеаризованные массивы N-мерных массивов, для которых
справедливо все сказанное об одно- и двумерных массивах.
3
Многомерные массивы: пример

// определение двумерных массивов
int a[2][3] = {
{0, 1},

// частичная инициализация строки

{2, 3, 4}};

// полная инициализация строки

int b[2][3] = {0, 1, 2, 3, 4};
// результаты:
// a: {0, 1, 0, 2, 3, 4}; b: {0, 1, 2, 3, 4, 0}
// определение массивов размерности больше 2
double d[3][5][10];
int32_t k[5][4][3][2];

4
Эффективный обход двумерных
массивов
 Простейшим способом повышения эффективности работы с
двумерным массивом является отказ от его обхода по
столбцам в пользу обхода по строкам:
 для массивов, объем которых превышает размер (выделенной процессу)
кэш-памяти данных самого верхнего уровня (напр., L2d), время
инициализации по строкам приблизительно втрое меньше времени
инициализации по столбцам вне зависимости от того, ведется ли запись
в кэш-память или в оперативную память в обход нее (У. Дреппер, 2007).

 Дальнейшая оптимизация может быть связана с анализом и
переработкой решаемой задачи в целях снижения частоты кэшпромахов или использования векторных инструкций процессора
(SIMD — Single Instruction, Multiple Data).
5
Задача об умножении матриц (1 / 2)
 Классическим примером задачи, требующей неэффективного, с
точки зрения архитектуры ЭВМ, обхода массива по столбцам,
𝑁−1
является задача об умножении матриц: 𝐴𝐵 𝑖𝑗 = 𝑘=0 𝑎 𝑖𝑘 𝑏 𝑘𝑗
— имеющая следующее очевидное решение:
for(i = 0; i < N; i++)
for(j = 0; j < N; j++)
for(k = 0; k < N; k++)
res[i][j] +=
mul1[i][k] * mul2[k][j];

6
Задача об умножении матриц (2 / 2)
 Предварительное транспонирование второй матрицы повышает
эффективность решения в 4 раза (с 16,77 млн. до 3,92 млн.
циклов ЦП Intel Core 2 с внутренней частотой 2666МГц).
𝑇
𝑁−1
 Математически: 𝐴𝐵 𝑖𝑗 = 𝑘=0 𝑎 𝑖𝑘 𝑏 𝑗𝑘 .
 На языке C:
double tmp[N][N];
for(i = 0; i < N; i++)
for(j = 0; j < N; j++)
tmp[i][j] = mul2[j][i];
for(i = 0; i < N; i++)
for(j = 0; j < N; j++)
for(k = 0; k < N; k++)
res[i][j] += mul1[i][k] * tmp[j][k];
7
Двумерные массивы и векторы
векторов
 Двумерный массив следует отличать от вектора векторов,
работа с которым:
 предполагает двухступенчатую процедуру создания и удаления;

 гарантирует смежность хранения данных только в пределах одной
строки (аналогичная гарантия предоставляется и в отношении
указателей на строки);
 ведет к большей фрагментации памяти, но повышает вероятность
успешного выделения в памяти непрерывных фрагментов (требование
памяти объема ~𝑁 2 заменяется требованием памяти объема ~𝑁).

 Многомерные массивы и векторы векторов (векторов…)
являются различными структурами данных с разной
дисциплиной использования.
8
Двумерные массивы и векторы
векторов: пример
// создание вектора векторов
int **v = (int**)malloc(N * sizeof(int*));
for(int i = 0; i < N; i++)

// NB: в каждой строке значение M может быть разным
v[i] = (int*)malloc(M * sizeof(int));
// ...
// удаление вектора векторов
for(int i = 0; i < N; i++)
free(v[i]);
free(v);

9
Многомерные массивы и указатели
 Для многомерных массивов справедлив ряд тождеств,
отражающих эквивалентность соответствующих выражений
языка C. Так, для двумерного массива T a[N][M] справедливо:
 a == &a[0]; a + i == &a[i];
 *a = a[0] == &a[0][0];
 **a == *&a[0][0] == a[0][0];
 a[i][j] == *(*(a + i) + j).

 Использование операции разыменования * не имеет каких-либо
преимуществ перед доступом по индексу, и наоборот.
Трансляция и первой, и второй формы записи в объектный код
приводит в целом к одинаковым результатам.
10
Многомерные массивы и указатели:
пример
// указатели на массивы и массивы указателей
int k[3][5];
int (*pk)[5]; // указатель на массив int[5]

int *p[5];

// массив указателей (int*)[5]

// примеры использования (все — допустимы)

pk

= k; // аналогично: pk = &k[0];

pk[0][0] = 1; // аналогично: k[0][0] = 1;
*pk[0]

= 2; // аналогично: k[0][0] = 2;

**pk

= 3; // аналогично: k[0][0] = 3;

11
Совместимость указателей
 Общеизвестно, что для любого конкретного типа T,
не тождественного ему типа Y и указателей T *pt и Y *py
недопустимо присваивание:
pt = py; // T — не void

 Представленный пример является частным случаем более
общего правила совместимости указателей, расширением
которого является запрет на присваивание значения указателя
на константу «обычному» указателю.

12
Совместимость указателей: пример
// определения
double *pd, **ppd;
double (*pda)[2];
double dbl32[3][2];

double dbl23[2][3];
// допустимые примеры использования
pd

= &dbl32[0][0];

// double* -> double*

pd

= dbl32[1];

// double[] -> double*

pda = dbl32;

// double(*)[2] -> double(*)[2]

ppd = &pd;

// double** -> double**

// недопустимые примеры использования
pd

= dbl32;

pda = dbl23;

// double[][] -> double*
// double(*)[3] -> double(*)[2]

13
Указатели на константы и константные
указатели
 Различное положение квалификатора const в определении
указателя позволяет вводить в исходном коде программ
четыре разновидности указателей:
 «обычный» указатель (вариант 1) — изменяемый указатель на
изменяемую через него область памяти;
 указатель на константу (вариант 2) — изменяемый указатель на
неизменяемую через него область памяти;

 константный указатель (вариант 3) — неизменяемый указатель на
изменяемую через него область памяти;
 константный указатель на константу (вариант 4) — неизменяемый
указатель на неизменяемую через него область памяти.

14
Указатели на константы и константные
указатели: пример
// пример 1: определения
int *p1;

// обычный указатель

const int *pc2;

// указатель на константу

int *const cp3;

// константный указатель

const int *const cpc4; // конст. указатель на константу
// пример 2: совместимость T*, const T* и const T**

int *pi;
const int *pci;
const int **ppci;
pci

= pi;

// допустимо:

int* -> const int*

pi

= pci; // недопустимо: const int* -> int*

ppci = &pi; // недопустимо: int** -> const int**
15
Указатели и квалификатор restrict
 Использование ключевого слова restrict допустимо строго в
отношении указателей и…
 расширяет возможности компилятора по оптимизации некоторых видов
кода путем поиска сокращенных методов вычислений;

 означает, что указатель — единственное (других нет) исходное
(первоначальное) средство доступа к соответствующему объекту.

 В отсутствие квалификатора restrict компилятор вынужден
прибегать к «пессимистичной» стратегии оптимизации.
 Квалификатор restrict широко применяется в отношении
формальных параметров функций стандартной библиотеки
языка C. Например:
 void *memcpy(void *restrict s1, const void *restrict s2, size_t n);
 int sscanf(const char *restrict s, const char *restrict format, ... );
16
Указатели и квалификатор restrict:
пример
// restrict: оптимистичная стратегия
int *restrict a = (int*)malloc(N * sizeof(int));
a[k] *= 2;

// (1)

// здесь следует иной код без участия a[k]

a[k] *= 3;

// (2)

// строки (1) и (2) вкупе эквивалентны: a[k] *= 6;
// пессимистичная стратегия

double d[N];

// d — не единственное средство доступа

double *p = d;

// p — не исходное средство доступа (см. d)

d[k] *= 2;

// (3)

// здесь следует иной код без участия d[k]
d[k] *= 3;

// (4)

// строки (3) и (4) не могут быть объединены ввиду существ. p
17
Многомерные массивы и функции
 В общем случае для передачи функции двумерного массива
T a[N][M] используется формальный параметр T **pa. При этом
информация о размерности массива закреплена в типе (T**), а
размеры передаются как дополнительные параметры.
 Существование в языке объектов вида T (*pt) [M] открывает
возможность встраивания информации о длине строк массива
в тип данных и определения функций с прототипами вида:
 void foo(T a[][M], size_t N);
 void foo(T (*a)[M], size_t N); // комплект [] трактуется как указатель

 Для многомерных массивов аналогично имеем:
 void bar(T b[][SZ2][SZ3][SZ4], size_t sz1);
 void bar(T (*b)[SZ2][SZ3][SZ4], size_t sz1);
18
Массивы переменной длины (1 / 2)
 Массивы переменной длины — введенное в стандарте C99
языковое средство динамического (а не традиционно
статического) распределения локальной памяти функций.
Размеры таких массивов по каждому измерению остаются
неизвестными до момента исполнения кода функции:
 концепция массивов переменной длины (VLA, variable-length array) —
ответ разработчиков C99 на требование научного сообщества
обеспечить переносимость на язык C наработанной десятилетиями базы
на языке FORTRAN, более гибком в части работы функций с массивами.

 Размер массива переменной длины:
 определяется переменными (отсюда название);
 остается постоянным до уничтожения объекта.
19
Массивы переменной длины (2 / 2)
 С учетом ограничений C99 массивы переменной длины:
 предполагают использование класса автоматической памяти;
 определяются внутри блоков и прототипов функций;

 не допускают инициализации при создании.

 Идентификатор массива переменной длины, как и
традиционного массива, является указателем.

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

20
Массивы переменной длины: пример
// прототип функции
int foo(size_t rows, size_t cols, double a[rows][cols]);
int foo(size_t, size_t, double a[*][*]); // имена опущены
// определение функции

int foo(size_t rows, size_t cols, double a[rows][cols])
{ /* ... */ }
int bar()
{
int n = 3, m = 4;
int var[n][m];

// массив как локальная переменная

// ...
}

21
Выравнивание переменных
составных типов
 Объекты составных типов языка C выравниваются в
соответствии с характеристикой, наибольшей среди
характеристик выравнивания всех своих элементов, которая в
большинстве случаев не достигает длины линии кэш-памяти.
 Другими словами, даже при «подгоне» элементов структуры под линию
L1d размещенный в ОЗУ объект может не обладать требуемым
выравниванием.

 Принудительное выравнивание статически и динамически
размещаемых структур выполняется аналогично выравниванию
скалярных переменных и начальных элементов массивов:
 GCC-атрибут aligned в определении типа или объекта данных;
 функция posix_memalign.
22
Упаковка переменных
составных типов (1 / 2)
 Для структур данных актуален также не характерный для
массивов и скаляров вопрос упаковки данных, обусловленный
наличием у элементов структур индивидуальных характеристик
выравнивания.
 Проблема упаковки структур заключается в том, что смежные
(перечисленные подряд) элементы часто физически не
«примыкают» друг к другу в памяти.

23
Упаковка переменных
составных типов (2 / 2)
 Например (для x86):
typedef struct {
int

id;

//

char

name[15];

// 15 байт

double

amount;

//

8 байт

_Bool

active;

//

1 байт

} account;

4 байта

// 28 байт (не 32 байта!)

 Наличие лакун, аналогичных выявленным 4-байтовым (13%)
потерям в структуре account, вызывается совокупностью
факторов:
 архитектура процессора (напр., x86 или x86-64);
 оптимизирующие действия компилятора;
 выбранный программистом порядок следования элементов.
24
Утилита pahole
 Самый доступный способ изучить физическое размещение
элементов структуры в оперативной памяти и сопоставить их с
загрузкой линий кэш-памяти L1d — утилита pahole, входящая
в пакет dwarves.
 Использование утилиты pahole позволяет:
 проанализировать размещение элементов структур относительно линий
кэш-памяти данных;
 получить варианты реорганизации структуры (параметр --reorganize).

25
Реорганизация структур данных:
рекомендации
 Реорганизация структур для повышения эффективности
использования кэш-памяти должна идти по двум направлениям:
 декомпозиция тяжеловесных («божественных») структур на более мелкие,
узкоспециализированные структуры, которые при решении конкретной
задачи используются полностью либо не используются вообще;
 устранение в структурах лакун, обусловленных характеристиками
выравнивания типов их элементов (см. ранее).

 При прочих равных условиях крайне желательно:
 переносить наиболее востребованные элементы структуры к ее началу (при
загрузке в кэш-память такие элементы структуры могут становиться
«критическими словами», доступ к которым должен быть самым быстрым);

 обходить структуру в порядке определения элементов, если иное не
требуется задачей или прочими обстоятельствами.
26
Реорганизация структур данных:
рекомендации: пример
typedef struct { // вар. 1: 28/32 байт (x86: 13% потерь)
int

id;

//

4 байта

char

name[15];

// 15 байт

/* лакуна — 1 байт выравнивания */

double

amount;

//

8 байт

_Bool

active;

//

1 байт

/* лакуна — 3 байта выравнивания */
} account_1;

// 32 байта

typedef struct { // вар. 2: 28/28 байт (x86: 0% потерь)
int

id;

//

char

name[15];

// 15 байт

_Bool

active;

//

1 байт

double

amount;

//

8 байт

} account_2;

4 байта

// 28 байт
27
Реорганизация структур данных:
недостатки и альтернатива решения
 Недостатки реорганизации:
 снижение удобства чтения и сопровождения исходного кода;
 риск размещения совместно используемых элементов (напр., длины
вектора и адреса его начального элемента) на разных линиях кэшпамяти.

 Основная альтернатива реорганизации — замена стихийно
выбранных типов данных наиболее адекватными по размеру,
вплоть до использования битовых полей данных.

28
Оптимизация загрузки кэш-памяти
команд: асимметрия условий (1 / 2)
 Если условие в заголовке оператора ветвления часто
оказывается ложным, выполнение кода становится
нелинейным. Осуществляемая ЦП предвыборка инструкций
ведет к тому, что неиспользуемый код загрязняет кэш-память
команд L1i и вызывает проблемы с предсказанием переходов.
 Ложные предсказания переходов делают условные
выражения крайне неэффективными.
 Асимметричные (статистически смещенные в истинную или
ложную сторону) условные выражения становятся причиной
ложного предсказания переходов и «пузырей» (периодов
ожидания ресурсов) в конвейере инструкций ЦП.
 Реже исполняемый код должен быть вытеснен с основного
вычислительного пути.
29
Оптимизация загрузки кэш-памяти
команд: асимметрия условий (2 / 2)
 Первый и наиболее очевидный способ повышения
эффективности загрузки кэш-памяти L1i — явная
реорганизация блоков. Так, если условие 𝑃 𝑥 чаще
оказывается ложным, оператор вида:
 if(P(x)) statementA; else statementB;

должен быть преобразован к виду:
 if(!(P(x))) statementB; else statementA;

 Второй способ решения той же проблемы основан на
применении средств, предоставляемых GCC:
 перекомпиляция с учетом результатов профилирования кода;
 использование функции __builtin_expect.
30
Функция __builtin_expect (GCC)
 Функция __builtin_expect — одна из целого ряда встроенных
в GCC функций, предназначенных для целей оптимизации:
 long __builtin_expect(long exp, long c);

 снабжает компилятор информацией, связанной с предсказанием
переходов,
 сообщает компилятору, что наиболее вероятным значением выражения
exp является c, и возвращает exp.

 При использовании __builtin_expect с логическими операциями
имеет смысл ввести дополнительные макроопределения вида:
 #define unlikely(expr) __builtin_expect(!!(expr), 0)

 #define likely(expr) __builtin_expect(!!(expr), 1)

31
Функция __builtin_expect (GCC): пример

#define unlikely(expr) __builtin_expect(!!(expr), 0)
#define likely(expr) __builtin_expect(!!(expr), 1)

int a;
srand(time(NULL));
a = rand() % 10;
if(unlikely(a > 8)) // условие ложно в 80% случаев
foo();
else
bar();

32
Оптимизация загрузки кэш-памяти
команд: встраивание функций
 Эффективность встраивания функций объясняется
способностью компилятора одновременно оптимизировать
бóльшие кодовые фрагменты. Порождаемый же при этом
машинный код способен лучше задействовать конвейерную
архитектуру микропроцессора.
 Обратной стороной встраивания является увеличение объема
кода и бóльшая нагрузка на кэш-память команд всех уровней
(L1i, L2i, …), которая может привести к общему снижению
производительности.
 Функции, вызываемые однократно, подлежат обязательному
встраиванию.
 Функции, многократно вызываемые из разных точек программы,
не должны встраиваться независимо от размера.
33
GCC-атрибуты always_inline
и noinline: пример
// пример 1: принудительное встраивание
/* inline */ __attribute__((always_inline)) void foo()
{

// ...
}

// пример 2: принудительный запрет встраивания
__attribute__((noinline)) void bar()
{
// ...
}

34
Утилита pfunct. Формат DWARF
 Утилита pfunct позволяет анализировать объектный код на
уровне функций, в том числе определять:
 количество безусловных переходов на метку (goto), параметров и
размер функций;

 количество функций, определенные как рекомендуемые к
встраиванию (inline), но не встроенных компилятором, и наоборот.

 Работа pfunct (как и описанной ранее утилиты pahole) строится
на использовании расположенной в ELF-файле (Executable and
Linkage Format) отладочной информации, хранящейся в нем по
стандарту DWARF, который среди прочих компиляторов
использует GCC:
 отладочная информация хранится в разделе debug_info в виде иерархии
тегов и значений , представляющих переменные, параметры функций и
пр. См. также утилиты readelf (binutils) и eu-readelf (elfutils).
35
Системные аспекты выделения и
освобождения памяти
 Взаимодействие программы с ОС в плане работы с памятью
состоит в выделении и освобождении участков оперативной
памяти разной природы и различной длины, понимание
механизмов которого:
 упрощает создание и использование структур данных произвольной
длины;
 позволяет избегать «утечек» оперативной памяти;
 разрабатывать высокопроизводительный код.

 В частности, необходимо знать о существовании 4-частной
структуры памяти данных:
 область данных, сегмент BSS и куча (входят в сегмент данных);
 программный стек (не входит в сегмент данных).

36
Область данных и сегмент BSS
 Область данных (data area) — используется для переменных
со статической продолжительностью хранения, которые явно
получили значение при инициализации:
 делится на область констант (read-only area) и область чтения-записи
(read-write area);
 инициализируется при загрузке программы, но до входа в функцию main
на основании образа соответствующих переменных в объектном файле.

 Сегмент BSS (BSS segment, .bss) — предназначен для
переменных со статической продолжительностью хранения, не
получивших значение при инициализации (инициализированы
нулевыми битами):
 располагается «выше» области данных (занимает более старшие адреса);
 ОС по требованию загрузчика, но до входа в функцию main способна
эффективно заполнять его нулями в блочном режиме (zero-fill-ondemand).
37
Куча и программный стек
 Куча (heap) — контролируется программистом посредством
вызова, в том числе, функций malloc / free:
 располагается «выше» сегмента BSS;

 является общей для всех разделяемых библиотек и динамически
загружаемых объектов (DLO, dynamically loaded objects) процесса.

 Программный стек (stack) — содержит значения,
передаваемые функции при ее вызове (stack frame), и
автоматические переменные:
 следует дисциплине LIFO;
 растет «вниз» (противоположно куче);
 обычно занимает самые верхние (максимальные) адреса виртуального
адресного пространства.
38
Функция malloc
 Внутренняя работа POSIX-совместимых функций malloc / free
зависит от их реализации (в частности, дисциплины выделения
памяти):
 одним из самых известных распределителей памяти в ОС семейства UNIX
(BSD 4.3 и др.) является распределитель Мак-Кьюсика — Карелса
(McKusick–Karels allocator).

 malloc выделяет для нужд процесса непрерывный фрагмент
оперативной памяти, складывающийся из блока запрошенного
объема (адрес которого возвращается как результат функции)
и следующего перед ним блока служебной информации,
имеющего длину в несколько байт и содержащего, в том числе:
 размер выделенного блока;
 ссылку на следующий блок памяти в связанном списке блоков.
39
Функция free (1 / 2)
 free помечает ранее выделенный фрагмент памяти как
свободный, при этом использует значение своего
единственного параметра для доступа к расположенному в
начале выделенного фрагмента блоку служебной
(дополнительной) информации, поэтому при передаче любого
другого адреса поведение free не определено.
 Контракт программиста с функциями malloc / free состоит в
его обязанности передать free тот же адрес, который был
получен от malloc. Вызов free с некорректным в этом смысле
параметром способен привести к повреждению логической
карты памяти и краху программы.

40
Функция free (2 / 2)
 В ходе своей работы функция free:
 идентифицирует выделенный блок памяти;
 возвращает его в список свободных блоков;

 предпринимает попытку слияния смежных свободных блоков для
снижения фрагментации и повышения вероятности успешного
выделения в будущем фрагмента требуемого размера.

 Такая логика работы пары malloc / free избавляет от
необходимости передачи длины освобождаемого блока как
самостоятельного параметра.

41
Стандарт POSIX
 POSIX (Portable Operating System Interface for UNIX) — набор
стандартов, разработанных IEEE и Open Group для обеспечения
совместимости ОС через унификацию их интерфейсов с
прикладными программами (API), а также переносимость самих
прикладных программ на уровне исходного кода на языке C.
 Стандарт IEEE 1003 содержит четыре основных компонента:
 «Основные определения» (том XBD);
 «Системные интерфейсы» (том XSH) — в числе прочего описывает
работу с сигналами, потоками исполнения (threads), потоками В/В (I/O
streams), сокетами и пр., а также содержит информацию обо всех POSIXсовместимых системных подпрограммах и функциях;
 «Оболочка и утилиты» (том XCU);
 «Обоснование» (том XRAT).

 POSIX-совместимыми ОС являются IBM AIX, OpenSolaris, QNX и др.
42
Практикум №2
Постановка задачи
 Решить индивидуальные задачи №№3, 4 и 5 в соответствии с
формальными требованиями.
 Для этого в блоге дисциплины:
 узнать постановку задач.

43
Спасибо за внимание
Алексей Петров

Más contenido relacionado

La actualidad más candente

C++ осень 2013 лекция 3
C++ осень 2013 лекция 3C++ осень 2013 лекция 3
C++ осень 2013 лекция 3Technopark
 
C++ осень 2013 лекция 1
C++ осень 2013 лекция 1C++ осень 2013 лекция 1
C++ осень 2013 лекция 1Technopark
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6Technopark
 
C++ Базовый. Занятие 02.
C++ Базовый. Занятие 02.C++ Базовый. Занятие 02.
C++ Базовый. Занятие 02.Igor Shkulipa
 
Объектно-Ориентированное Программирование на C++, Лекции 1 и 2
Объектно-Ориентированное Программирование на C++, Лекции 1 и 2Объектно-Ориентированное Программирование на C++, Лекции 1 и 2
Объектно-Ориентированное Программирование на C++, Лекции 1 и 2Dima Dzuba
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Dima Dzuba
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Dima Dzuba
 
C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.Igor Shkulipa
 
C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.Igor Shkulipa
 
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
Объектно-Ориентированное Программирование на C++, Лекции  3 и 4 Объектно-Ориентированное Программирование на C++, Лекции  3 и 4
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4 Dima Dzuba
 
Cpp/cli types
Cpp/cli typesCpp/cli types
Cpp/cli typesmcroitor
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковSergey Platonov
 
Типы данных
Типы данныхТипы данных
Типы данныхMonsterXX
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Dima Dzuba
 
особенности программирования на с++
особенности программирования на с++особенности программирования на с++
особенности программирования на с++mcroitor
 
C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.Igor Shkulipa
 

La actualidad más candente (20)

C++ осень 2013 лекция 3
C++ осень 2013 лекция 3C++ осень 2013 лекция 3
C++ осень 2013 лекция 3
 
C++ осень 2013 лекция 1
C++ осень 2013 лекция 1C++ осень 2013 лекция 1
C++ осень 2013 лекция 1
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6
 
C++ Базовый. Занятие 02.
C++ Базовый. Занятие 02.C++ Базовый. Занятие 02.
C++ Базовый. Занятие 02.
 
Объектно-Ориентированное Программирование на C++, Лекции 1 и 2
Объектно-Ориентированное Программирование на C++, Лекции 1 и 2Объектно-Ориентированное Программирование на C++, Лекции 1 и 2
Объектно-Ориентированное Программирование на C++, Лекции 1 и 2
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8.
 
Document
DocumentDocument
Document
 
C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.
 
C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.C++ Базовый. Занятие 04.
C++ Базовый. Занятие 04.
 
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
Объектно-Ориентированное Программирование на C++, Лекции  3 и 4 Объектно-Ориентированное Программирование на C++, Лекции  3 и 4
Объектно-Ориентированное Программирование на C++, Лекции 3 и 4
 
лек5 6
лек5 6лек5 6
лек5 6
 
Cpp/cli types
Cpp/cli typesCpp/cli types
Cpp/cli types
 
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворковНикита Глушков, К вопросу о реализации кроссплатформенных фреймворков
Никита Глушков, К вопросу о реализации кроссплатформенных фреймворков
 
Типы данных
Типы данныхТипы данных
Типы данных
 
Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6Объектно-ориентированное программирование. Лекция 5 и 6
Объектно-ориентированное программирование. Лекция 5 и 6
 
особенности программирования на с++
особенности программирования на с++особенности программирования на с++
особенности программирования на с++
 
Java. Методы
Java. Методы Java. Методы
Java. Методы
 
Lecture 3
Lecture 3Lecture 3
Lecture 3
 
C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.C++ Базовый. Занятие 01.
C++ Базовый. Занятие 01.
 

Similar a C++ осень 2013 лекция 2

2.3 Указатели и массивы
2.3 Указатели и массивы2.3 Указатели и массивы
2.3 Указатели и массивыDEVTYPE
 
Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Mikhail Kurnosov
 
spolzovatelskie-strukturirovannye-tipy-dannyh.pptx
spolzovatelskie-strukturirovannye-tipy-dannyh.pptxspolzovatelskie-strukturirovannye-tipy-dannyh.pptx
spolzovatelskie-strukturirovannye-tipy-dannyh.pptxyanabondarieva
 
Введение в синтаксис C++, часть 2
Введение в синтаксис C++, часть 2Введение в синтаксис C++, часть 2
Введение в синтаксис C++, часть 2DEVTYPE
 
Введение в синтаксис C++, часть 1
Введение в синтаксис C++, часть 1Введение в синтаксис C++, часть 1
Введение в синтаксис C++, часть 1DEVTYPE
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаTatyanazaxarova
 
Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)Mikhail Kurnosov
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаTatyanazaxarova
 
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)Mikhail Kurnosov
 
!Predictive analytics part_2
!Predictive analytics part_2!Predictive analytics part_2
!Predictive analytics part_2Vladimir Krylov
 
Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Yandex
 
03
0303
03JIuc
 
Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...
Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...
Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...Nikolay Grebenshikov
 
Что такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_tЧто такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_tTatyanazaxarova
 
2.6 Динамическая память
2.6 Динамическая память2.6 Динамическая память
2.6 Динамическая памятьDEVTYPE
 
Советский суперкомпьютер К-340А и секретные вычисления
Советский суперкомпьютер К-340А и секретные вычисленияСоветский суперкомпьютер К-340А и секретные вычисления
Советский суперкомпьютер К-340А и секретные вычисленияPositive Hack Days
 

Similar a C++ осень 2013 лекция 2 (20)

2.3 Указатели и массивы
2.3 Указатели и массивы2.3 Указатели и массивы
2.3 Указатели и массивы
 
Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)
 
spolzovatelskie-strukturirovannye-tipy-dannyh.pptx
spolzovatelskie-strukturirovannye-tipy-dannyh.pptxspolzovatelskie-strukturirovannye-tipy-dannyh.pptx
spolzovatelskie-strukturirovannye-tipy-dannyh.pptx
 
Введение в синтаксис C++, часть 2
Введение в синтаксис C++, часть 2Введение в синтаксис C++, часть 2
Введение в синтаксис C++, часть 2
 
Введение в синтаксис C++, часть 1
Введение в синтаксис C++, часть 1Введение в синтаксис C++, часть 1
Введение в синтаксис C++, часть 1
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
 
Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)Лекция 7. Стандарт OpenMP (подолжение)
Лекция 7. Стандарт OpenMP (подолжение)
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
 
лекция 7
лекция 7лекция 7
лекция 7
 
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
 
!Predictive analytics part_2
!Predictive analytics part_2!Predictive analytics part_2
!Predictive analytics part_2
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
Rgsu04
Rgsu04Rgsu04
Rgsu04
 
Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11Дмитрий Прокопцев — R-ссылки в С++11
Дмитрий Прокопцев — R-ссылки в С++11
 
Step cpp0201
Step cpp0201Step cpp0201
Step cpp0201
 
03
0303
03
 
Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...
Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...
Лекция №2. Абстрактные типы данных. ООП. Предмет "Структуры и алгоритмы обраб...
 
Что такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_tЧто такое size_t и ptrdiff_t
Что такое size_t и ptrdiff_t
 
2.6 Динамическая память
2.6 Динамическая память2.6 Динамическая память
2.6 Динамическая память
 
Советский суперкомпьютер К-340А и секретные вычисления
Советский суперкомпьютер К-340А и секретные вычисленияСоветский суперкомпьютер К-340А и секретные вычисления
Советский суперкомпьютер К-340А и секретные вычисления
 

Más de Technopark

Лекция 11. Вычислительная модель Pregel
Лекция 11. Вычислительная модель PregelЛекция 11. Вычислительная модель Pregel
Лекция 11. Вычислительная модель PregelTechnopark
 
Лекция 14. Hadoop в Поиске Mail.Ru
Лекция 14. Hadoop в Поиске Mail.RuЛекция 14. Hadoop в Поиске Mail.Ru
Лекция 14. Hadoop в Поиске Mail.RuTechnopark
 
Лекция 13. YARN
Лекция 13. YARNЛекция 13. YARN
Лекция 13. YARNTechnopark
 
Лекция 12. Spark
Лекция 12. SparkЛекция 12. Spark
Лекция 12. SparkTechnopark
 
Лекция 10. Apache Mahout
Лекция 10. Apache MahoutЛекция 10. Apache Mahout
Лекция 10. Apache MahoutTechnopark
 
Лекция 9. ZooKeeper
Лекция 9. ZooKeeperЛекция 9. ZooKeeper
Лекция 9. ZooKeeperTechnopark
 
Лекция 7. Введение в Pig и Hive
Лекция 7. Введение в Pig и HiveЛекция 7. Введение в Pig и Hive
Лекция 7. Введение в Pig и HiveTechnopark
 
Лекция 6. MapReduce в Hadoop (графы)
Лекция 6. MapReduce в Hadoop (графы)Лекция 6. MapReduce в Hadoop (графы)
Лекция 6. MapReduce в Hadoop (графы)Technopark
 
Лекция 5. MapReduce в Hadoop (алгоритмы)
Лекция 5. MapReduce в Hadoop (алгоритмы)Лекция 5. MapReduce в Hadoop (алгоритмы)
Лекция 5. MapReduce в Hadoop (алгоритмы)Technopark
 
Лекция 4. MapReduce в Hadoop (введение)
Лекция 4. MapReduce в Hadoop (введение)Лекция 4. MapReduce в Hadoop (введение)
Лекция 4. MapReduce в Hadoop (введение)Technopark
 
Лекция 3. Распределённая файловая система HDFS
Лекция 3. Распределённая файловая система HDFSЛекция 3. Распределённая файловая система HDFS
Лекция 3. Распределённая файловая система HDFSTechnopark
 
Лекция 2. Основы Hadoop
Лекция 2. Основы HadoopЛекция 2. Основы Hadoop
Лекция 2. Основы HadoopTechnopark
 
Лекция 1. Введение в Big Data и MapReduce
Лекция 1. Введение в Big Data и MapReduceЛекция 1. Введение в Big Data и MapReduce
Лекция 1. Введение в Big Data и MapReduceTechnopark
 
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"Technopark
 
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...Technopark
 
СУБД 2013 Лекция №9 "Безопасность баз данных"
СУБД 2013 Лекция №9 "Безопасность баз данных"СУБД 2013 Лекция №9 "Безопасность баз данных"
СУБД 2013 Лекция №9 "Безопасность баз данных"Technopark
 
СУБД 2013 Лекция №8 "Конфигурирование базы данных"
СУБД 2013 Лекция №8 "Конфигурирование базы данных"СУБД 2013 Лекция №8 "Конфигурирование базы данных"
СУБД 2013 Лекция №8 "Конфигурирование базы данных"Technopark
 
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"Technopark
 
СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"Technopark
 
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...Technopark
 

Más de Technopark (20)

Лекция 11. Вычислительная модель Pregel
Лекция 11. Вычислительная модель PregelЛекция 11. Вычислительная модель Pregel
Лекция 11. Вычислительная модель Pregel
 
Лекция 14. Hadoop в Поиске Mail.Ru
Лекция 14. Hadoop в Поиске Mail.RuЛекция 14. Hadoop в Поиске Mail.Ru
Лекция 14. Hadoop в Поиске Mail.Ru
 
Лекция 13. YARN
Лекция 13. YARNЛекция 13. YARN
Лекция 13. YARN
 
Лекция 12. Spark
Лекция 12. SparkЛекция 12. Spark
Лекция 12. Spark
 
Лекция 10. Apache Mahout
Лекция 10. Apache MahoutЛекция 10. Apache Mahout
Лекция 10. Apache Mahout
 
Лекция 9. ZooKeeper
Лекция 9. ZooKeeperЛекция 9. ZooKeeper
Лекция 9. ZooKeeper
 
Лекция 7. Введение в Pig и Hive
Лекция 7. Введение в Pig и HiveЛекция 7. Введение в Pig и Hive
Лекция 7. Введение в Pig и Hive
 
Лекция 6. MapReduce в Hadoop (графы)
Лекция 6. MapReduce в Hadoop (графы)Лекция 6. MapReduce в Hadoop (графы)
Лекция 6. MapReduce в Hadoop (графы)
 
Лекция 5. MapReduce в Hadoop (алгоритмы)
Лекция 5. MapReduce в Hadoop (алгоритмы)Лекция 5. MapReduce в Hadoop (алгоритмы)
Лекция 5. MapReduce в Hadoop (алгоритмы)
 
Лекция 4. MapReduce в Hadoop (введение)
Лекция 4. MapReduce в Hadoop (введение)Лекция 4. MapReduce в Hadoop (введение)
Лекция 4. MapReduce в Hadoop (введение)
 
Лекция 3. Распределённая файловая система HDFS
Лекция 3. Распределённая файловая система HDFSЛекция 3. Распределённая файловая система HDFS
Лекция 3. Распределённая файловая система HDFS
 
Лекция 2. Основы Hadoop
Лекция 2. Основы HadoopЛекция 2. Основы Hadoop
Лекция 2. Основы Hadoop
 
Лекция 1. Введение в Big Data и MapReduce
Лекция 1. Введение в Big Data и MapReduceЛекция 1. Введение в Big Data и MapReduce
Лекция 1. Введение в Big Data и MapReduce
 
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL"
 
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...
СУБД 2013 Лекция №10 "Нереляционное решение в области баз данных — NoSQL" Час...
 
СУБД 2013 Лекция №9 "Безопасность баз данных"
СУБД 2013 Лекция №9 "Безопасность баз данных"СУБД 2013 Лекция №9 "Безопасность баз данных"
СУБД 2013 Лекция №9 "Безопасность баз данных"
 
СУБД 2013 Лекция №8 "Конфигурирование базы данных"
СУБД 2013 Лекция №8 "Конфигурирование базы данных"СУБД 2013 Лекция №8 "Конфигурирование базы данных"
СУБД 2013 Лекция №8 "Конфигурирование базы данных"
 
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
СУБД 2013 Лекция №7 "Оптимизация запросов и индексирование"
 
СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"СУБД 2013 Лекция №5 "Определение узких мест"
СУБД 2013 Лекция №5 "Определение узких мест"
 
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
СУБД 2013 Лекция №6 "Профилирование запросов. Сложноструктурированные SQL-зап...
 

C++ осень 2013 лекция 2

  • 2. Лекция №2. Дополнительные вопросы организации и использования памяти в программах на языке C 1. Многомерные массивы. 2. Выравнивание и упаковка переменных составных типов. 3. Системные аспекты выделения и освобождения памяти. 4. Взаимодействие с операционной системой. 5. Оптимизация работы с кэш-памятью ЦП ЭВМ. 6. Постановка индивидуальных задач к практикуму №2. 2
  • 3. Многомерные массивы  Двумерный массив — объект данных T a[N][M], который:  содержит N последовательно расположенных в памяти строк по M элементов типа T в каждой;  в общем и целом инициализируется аналогично одномерным массивам;  по характеристикам выравнивания идентичен объекту T a[N * M], что сводит его двумерный характер к удобному умозрительному приему, упрощающему обсуждение и визуализацию порядка размещения данных.  Массивы размерности больше двух считаются многомерными, при этом (N + 1)-мерные массивы индуктивно определяются как линеаризованные массивы N-мерных массивов, для которых справедливо все сказанное об одно- и двумерных массивах. 3
  • 4. Многомерные массивы: пример // определение двумерных массивов int a[2][3] = { {0, 1}, // частичная инициализация строки {2, 3, 4}}; // полная инициализация строки int b[2][3] = {0, 1, 2, 3, 4}; // результаты: // a: {0, 1, 0, 2, 3, 4}; b: {0, 1, 2, 3, 4, 0} // определение массивов размерности больше 2 double d[3][5][10]; int32_t k[5][4][3][2]; 4
  • 5. Эффективный обход двумерных массивов  Простейшим способом повышения эффективности работы с двумерным массивом является отказ от его обхода по столбцам в пользу обхода по строкам:  для массивов, объем которых превышает размер (выделенной процессу) кэш-памяти данных самого верхнего уровня (напр., L2d), время инициализации по строкам приблизительно втрое меньше времени инициализации по столбцам вне зависимости от того, ведется ли запись в кэш-память или в оперативную память в обход нее (У. Дреппер, 2007).  Дальнейшая оптимизация может быть связана с анализом и переработкой решаемой задачи в целях снижения частоты кэшпромахов или использования векторных инструкций процессора (SIMD — Single Instruction, Multiple Data). 5
  • 6. Задача об умножении матриц (1 / 2)  Классическим примером задачи, требующей неэффективного, с точки зрения архитектуры ЭВМ, обхода массива по столбцам, 𝑁−1 является задача об умножении матриц: 𝐴𝐵 𝑖𝑗 = 𝑘=0 𝑎 𝑖𝑘 𝑏 𝑘𝑗 — имеющая следующее очевидное решение: for(i = 0; i < N; i++) for(j = 0; j < N; j++) for(k = 0; k < N; k++) res[i][j] += mul1[i][k] * mul2[k][j]; 6
  • 7. Задача об умножении матриц (2 / 2)  Предварительное транспонирование второй матрицы повышает эффективность решения в 4 раза (с 16,77 млн. до 3,92 млн. циклов ЦП Intel Core 2 с внутренней частотой 2666МГц). 𝑇 𝑁−1  Математически: 𝐴𝐵 𝑖𝑗 = 𝑘=0 𝑎 𝑖𝑘 𝑏 𝑗𝑘 .  На языке C: double tmp[N][N]; for(i = 0; i < N; i++) for(j = 0; j < N; j++) tmp[i][j] = mul2[j][i]; for(i = 0; i < N; i++) for(j = 0; j < N; j++) for(k = 0; k < N; k++) res[i][j] += mul1[i][k] * tmp[j][k]; 7
  • 8. Двумерные массивы и векторы векторов  Двумерный массив следует отличать от вектора векторов, работа с которым:  предполагает двухступенчатую процедуру создания и удаления;  гарантирует смежность хранения данных только в пределах одной строки (аналогичная гарантия предоставляется и в отношении указателей на строки);  ведет к большей фрагментации памяти, но повышает вероятность успешного выделения в памяти непрерывных фрагментов (требование памяти объема ~𝑁 2 заменяется требованием памяти объема ~𝑁).  Многомерные массивы и векторы векторов (векторов…) являются различными структурами данных с разной дисциплиной использования. 8
  • 9. Двумерные массивы и векторы векторов: пример // создание вектора векторов int **v = (int**)malloc(N * sizeof(int*)); for(int i = 0; i < N; i++) // NB: в каждой строке значение M может быть разным v[i] = (int*)malloc(M * sizeof(int)); // ... // удаление вектора векторов for(int i = 0; i < N; i++) free(v[i]); free(v); 9
  • 10. Многомерные массивы и указатели  Для многомерных массивов справедлив ряд тождеств, отражающих эквивалентность соответствующих выражений языка C. Так, для двумерного массива T a[N][M] справедливо:  a == &a[0]; a + i == &a[i];  *a = a[0] == &a[0][0];  **a == *&a[0][0] == a[0][0];  a[i][j] == *(*(a + i) + j).  Использование операции разыменования * не имеет каких-либо преимуществ перед доступом по индексу, и наоборот. Трансляция и первой, и второй формы записи в объектный код приводит в целом к одинаковым результатам. 10
  • 11. Многомерные массивы и указатели: пример // указатели на массивы и массивы указателей int k[3][5]; int (*pk)[5]; // указатель на массив int[5] int *p[5]; // массив указателей (int*)[5] // примеры использования (все — допустимы) pk = k; // аналогично: pk = &k[0]; pk[0][0] = 1; // аналогично: k[0][0] = 1; *pk[0] = 2; // аналогично: k[0][0] = 2; **pk = 3; // аналогично: k[0][0] = 3; 11
  • 12. Совместимость указателей  Общеизвестно, что для любого конкретного типа T, не тождественного ему типа Y и указателей T *pt и Y *py недопустимо присваивание: pt = py; // T — не void  Представленный пример является частным случаем более общего правила совместимости указателей, расширением которого является запрет на присваивание значения указателя на константу «обычному» указателю. 12
  • 13. Совместимость указателей: пример // определения double *pd, **ppd; double (*pda)[2]; double dbl32[3][2]; double dbl23[2][3]; // допустимые примеры использования pd = &dbl32[0][0]; // double* -> double* pd = dbl32[1]; // double[] -> double* pda = dbl32; // double(*)[2] -> double(*)[2] ppd = &pd; // double** -> double** // недопустимые примеры использования pd = dbl32; pda = dbl23; // double[][] -> double* // double(*)[3] -> double(*)[2] 13
  • 14. Указатели на константы и константные указатели  Различное положение квалификатора const в определении указателя позволяет вводить в исходном коде программ четыре разновидности указателей:  «обычный» указатель (вариант 1) — изменяемый указатель на изменяемую через него область памяти;  указатель на константу (вариант 2) — изменяемый указатель на неизменяемую через него область памяти;  константный указатель (вариант 3) — неизменяемый указатель на изменяемую через него область памяти;  константный указатель на константу (вариант 4) — неизменяемый указатель на неизменяемую через него область памяти. 14
  • 15. Указатели на константы и константные указатели: пример // пример 1: определения int *p1; // обычный указатель const int *pc2; // указатель на константу int *const cp3; // константный указатель const int *const cpc4; // конст. указатель на константу // пример 2: совместимость T*, const T* и const T** int *pi; const int *pci; const int **ppci; pci = pi; // допустимо: int* -> const int* pi = pci; // недопустимо: const int* -> int* ppci = &pi; // недопустимо: int** -> const int** 15
  • 16. Указатели и квалификатор restrict  Использование ключевого слова restrict допустимо строго в отношении указателей и…  расширяет возможности компилятора по оптимизации некоторых видов кода путем поиска сокращенных методов вычислений;  означает, что указатель — единственное (других нет) исходное (первоначальное) средство доступа к соответствующему объекту.  В отсутствие квалификатора restrict компилятор вынужден прибегать к «пессимистичной» стратегии оптимизации.  Квалификатор restrict широко применяется в отношении формальных параметров функций стандартной библиотеки языка C. Например:  void *memcpy(void *restrict s1, const void *restrict s2, size_t n);  int sscanf(const char *restrict s, const char *restrict format, ... ); 16
  • 17. Указатели и квалификатор restrict: пример // restrict: оптимистичная стратегия int *restrict a = (int*)malloc(N * sizeof(int)); a[k] *= 2; // (1) // здесь следует иной код без участия a[k] a[k] *= 3; // (2) // строки (1) и (2) вкупе эквивалентны: a[k] *= 6; // пессимистичная стратегия double d[N]; // d — не единственное средство доступа double *p = d; // p — не исходное средство доступа (см. d) d[k] *= 2; // (3) // здесь следует иной код без участия d[k] d[k] *= 3; // (4) // строки (3) и (4) не могут быть объединены ввиду существ. p 17
  • 18. Многомерные массивы и функции  В общем случае для передачи функции двумерного массива T a[N][M] используется формальный параметр T **pa. При этом информация о размерности массива закреплена в типе (T**), а размеры передаются как дополнительные параметры.  Существование в языке объектов вида T (*pt) [M] открывает возможность встраивания информации о длине строк массива в тип данных и определения функций с прототипами вида:  void foo(T a[][M], size_t N);  void foo(T (*a)[M], size_t N); // комплект [] трактуется как указатель  Для многомерных массивов аналогично имеем:  void bar(T b[][SZ2][SZ3][SZ4], size_t sz1);  void bar(T (*b)[SZ2][SZ3][SZ4], size_t sz1); 18
  • 19. Массивы переменной длины (1 / 2)  Массивы переменной длины — введенное в стандарте C99 языковое средство динамического (а не традиционно статического) распределения локальной памяти функций. Размеры таких массивов по каждому измерению остаются неизвестными до момента исполнения кода функции:  концепция массивов переменной длины (VLA, variable-length array) — ответ разработчиков C99 на требование научного сообщества обеспечить переносимость на язык C наработанной десятилетиями базы на языке FORTRAN, более гибком в части работы функций с массивами.  Размер массива переменной длины:  определяется переменными (отсюда название);  остается постоянным до уничтожения объекта. 19
  • 20. Массивы переменной длины (2 / 2)  С учетом ограничений C99 массивы переменной длины:  предполагают использование класса автоматической памяти;  определяются внутри блоков и прототипов функций;  не допускают инициализации при создании.  Идентификатор массива переменной длины, как и традиционного массива, является указателем.  Использование массива переменной длины как формального параметра функции означает передачу данных по указателю. 20
  • 21. Массивы переменной длины: пример // прототип функции int foo(size_t rows, size_t cols, double a[rows][cols]); int foo(size_t, size_t, double a[*][*]); // имена опущены // определение функции int foo(size_t rows, size_t cols, double a[rows][cols]) { /* ... */ } int bar() { int n = 3, m = 4; int var[n][m]; // массив как локальная переменная // ... } 21
  • 22. Выравнивание переменных составных типов  Объекты составных типов языка C выравниваются в соответствии с характеристикой, наибольшей среди характеристик выравнивания всех своих элементов, которая в большинстве случаев не достигает длины линии кэш-памяти.  Другими словами, даже при «подгоне» элементов структуры под линию L1d размещенный в ОЗУ объект может не обладать требуемым выравниванием.  Принудительное выравнивание статически и динамически размещаемых структур выполняется аналогично выравниванию скалярных переменных и начальных элементов массивов:  GCC-атрибут aligned в определении типа или объекта данных;  функция posix_memalign. 22
  • 23. Упаковка переменных составных типов (1 / 2)  Для структур данных актуален также не характерный для массивов и скаляров вопрос упаковки данных, обусловленный наличием у элементов структур индивидуальных характеристик выравнивания.  Проблема упаковки структур заключается в том, что смежные (перечисленные подряд) элементы часто физически не «примыкают» друг к другу в памяти. 23
  • 24. Упаковка переменных составных типов (2 / 2)  Например (для x86): typedef struct { int id; // char name[15]; // 15 байт double amount; // 8 байт _Bool active; // 1 байт } account; 4 байта // 28 байт (не 32 байта!)  Наличие лакун, аналогичных выявленным 4-байтовым (13%) потерям в структуре account, вызывается совокупностью факторов:  архитектура процессора (напр., x86 или x86-64);  оптимизирующие действия компилятора;  выбранный программистом порядок следования элементов. 24
  • 25. Утилита pahole  Самый доступный способ изучить физическое размещение элементов структуры в оперативной памяти и сопоставить их с загрузкой линий кэш-памяти L1d — утилита pahole, входящая в пакет dwarves.  Использование утилиты pahole позволяет:  проанализировать размещение элементов структур относительно линий кэш-памяти данных;  получить варианты реорганизации структуры (параметр --reorganize). 25
  • 26. Реорганизация структур данных: рекомендации  Реорганизация структур для повышения эффективности использования кэш-памяти должна идти по двум направлениям:  декомпозиция тяжеловесных («божественных») структур на более мелкие, узкоспециализированные структуры, которые при решении конкретной задачи используются полностью либо не используются вообще;  устранение в структурах лакун, обусловленных характеристиками выравнивания типов их элементов (см. ранее).  При прочих равных условиях крайне желательно:  переносить наиболее востребованные элементы структуры к ее началу (при загрузке в кэш-память такие элементы структуры могут становиться «критическими словами», доступ к которым должен быть самым быстрым);  обходить структуру в порядке определения элементов, если иное не требуется задачей или прочими обстоятельствами. 26
  • 27. Реорганизация структур данных: рекомендации: пример typedef struct { // вар. 1: 28/32 байт (x86: 13% потерь) int id; // 4 байта char name[15]; // 15 байт /* лакуна — 1 байт выравнивания */ double amount; // 8 байт _Bool active; // 1 байт /* лакуна — 3 байта выравнивания */ } account_1; // 32 байта typedef struct { // вар. 2: 28/28 байт (x86: 0% потерь) int id; // char name[15]; // 15 байт _Bool active; // 1 байт double amount; // 8 байт } account_2; 4 байта // 28 байт 27
  • 28. Реорганизация структур данных: недостатки и альтернатива решения  Недостатки реорганизации:  снижение удобства чтения и сопровождения исходного кода;  риск размещения совместно используемых элементов (напр., длины вектора и адреса его начального элемента) на разных линиях кэшпамяти.  Основная альтернатива реорганизации — замена стихийно выбранных типов данных наиболее адекватными по размеру, вплоть до использования битовых полей данных. 28
  • 29. Оптимизация загрузки кэш-памяти команд: асимметрия условий (1 / 2)  Если условие в заголовке оператора ветвления часто оказывается ложным, выполнение кода становится нелинейным. Осуществляемая ЦП предвыборка инструкций ведет к тому, что неиспользуемый код загрязняет кэш-память команд L1i и вызывает проблемы с предсказанием переходов.  Ложные предсказания переходов делают условные выражения крайне неэффективными.  Асимметричные (статистически смещенные в истинную или ложную сторону) условные выражения становятся причиной ложного предсказания переходов и «пузырей» (периодов ожидания ресурсов) в конвейере инструкций ЦП.  Реже исполняемый код должен быть вытеснен с основного вычислительного пути. 29
  • 30. Оптимизация загрузки кэш-памяти команд: асимметрия условий (2 / 2)  Первый и наиболее очевидный способ повышения эффективности загрузки кэш-памяти L1i — явная реорганизация блоков. Так, если условие 𝑃 𝑥 чаще оказывается ложным, оператор вида:  if(P(x)) statementA; else statementB; должен быть преобразован к виду:  if(!(P(x))) statementB; else statementA;  Второй способ решения той же проблемы основан на применении средств, предоставляемых GCC:  перекомпиляция с учетом результатов профилирования кода;  использование функции __builtin_expect. 30
  • 31. Функция __builtin_expect (GCC)  Функция __builtin_expect — одна из целого ряда встроенных в GCC функций, предназначенных для целей оптимизации:  long __builtin_expect(long exp, long c);  снабжает компилятор информацией, связанной с предсказанием переходов,  сообщает компилятору, что наиболее вероятным значением выражения exp является c, и возвращает exp.  При использовании __builtin_expect с логическими операциями имеет смысл ввести дополнительные макроопределения вида:  #define unlikely(expr) __builtin_expect(!!(expr), 0)  #define likely(expr) __builtin_expect(!!(expr), 1) 31
  • 32. Функция __builtin_expect (GCC): пример #define unlikely(expr) __builtin_expect(!!(expr), 0) #define likely(expr) __builtin_expect(!!(expr), 1) int a; srand(time(NULL)); a = rand() % 10; if(unlikely(a > 8)) // условие ложно в 80% случаев foo(); else bar(); 32
  • 33. Оптимизация загрузки кэш-памяти команд: встраивание функций  Эффективность встраивания функций объясняется способностью компилятора одновременно оптимизировать бóльшие кодовые фрагменты. Порождаемый же при этом машинный код способен лучше задействовать конвейерную архитектуру микропроцессора.  Обратной стороной встраивания является увеличение объема кода и бóльшая нагрузка на кэш-память команд всех уровней (L1i, L2i, …), которая может привести к общему снижению производительности.  Функции, вызываемые однократно, подлежат обязательному встраиванию.  Функции, многократно вызываемые из разных точек программы, не должны встраиваться независимо от размера. 33
  • 34. GCC-атрибуты always_inline и noinline: пример // пример 1: принудительное встраивание /* inline */ __attribute__((always_inline)) void foo() { // ... } // пример 2: принудительный запрет встраивания __attribute__((noinline)) void bar() { // ... } 34
  • 35. Утилита pfunct. Формат DWARF  Утилита pfunct позволяет анализировать объектный код на уровне функций, в том числе определять:  количество безусловных переходов на метку (goto), параметров и размер функций;  количество функций, определенные как рекомендуемые к встраиванию (inline), но не встроенных компилятором, и наоборот.  Работа pfunct (как и описанной ранее утилиты pahole) строится на использовании расположенной в ELF-файле (Executable and Linkage Format) отладочной информации, хранящейся в нем по стандарту DWARF, который среди прочих компиляторов использует GCC:  отладочная информация хранится в разделе debug_info в виде иерархии тегов и значений , представляющих переменные, параметры функций и пр. См. также утилиты readelf (binutils) и eu-readelf (elfutils). 35
  • 36. Системные аспекты выделения и освобождения памяти  Взаимодействие программы с ОС в плане работы с памятью состоит в выделении и освобождении участков оперативной памяти разной природы и различной длины, понимание механизмов которого:  упрощает создание и использование структур данных произвольной длины;  позволяет избегать «утечек» оперативной памяти;  разрабатывать высокопроизводительный код.  В частности, необходимо знать о существовании 4-частной структуры памяти данных:  область данных, сегмент BSS и куча (входят в сегмент данных);  программный стек (не входит в сегмент данных). 36
  • 37. Область данных и сегмент BSS  Область данных (data area) — используется для переменных со статической продолжительностью хранения, которые явно получили значение при инициализации:  делится на область констант (read-only area) и область чтения-записи (read-write area);  инициализируется при загрузке программы, но до входа в функцию main на основании образа соответствующих переменных в объектном файле.  Сегмент BSS (BSS segment, .bss) — предназначен для переменных со статической продолжительностью хранения, не получивших значение при инициализации (инициализированы нулевыми битами):  располагается «выше» области данных (занимает более старшие адреса);  ОС по требованию загрузчика, но до входа в функцию main способна эффективно заполнять его нулями в блочном режиме (zero-fill-ondemand). 37
  • 38. Куча и программный стек  Куча (heap) — контролируется программистом посредством вызова, в том числе, функций malloc / free:  располагается «выше» сегмента BSS;  является общей для всех разделяемых библиотек и динамически загружаемых объектов (DLO, dynamically loaded objects) процесса.  Программный стек (stack) — содержит значения, передаваемые функции при ее вызове (stack frame), и автоматические переменные:  следует дисциплине LIFO;  растет «вниз» (противоположно куче);  обычно занимает самые верхние (максимальные) адреса виртуального адресного пространства. 38
  • 39. Функция malloc  Внутренняя работа POSIX-совместимых функций malloc / free зависит от их реализации (в частности, дисциплины выделения памяти):  одним из самых известных распределителей памяти в ОС семейства UNIX (BSD 4.3 и др.) является распределитель Мак-Кьюсика — Карелса (McKusick–Karels allocator).  malloc выделяет для нужд процесса непрерывный фрагмент оперативной памяти, складывающийся из блока запрошенного объема (адрес которого возвращается как результат функции) и следующего перед ним блока служебной информации, имеющего длину в несколько байт и содержащего, в том числе:  размер выделенного блока;  ссылку на следующий блок памяти в связанном списке блоков. 39
  • 40. Функция free (1 / 2)  free помечает ранее выделенный фрагмент памяти как свободный, при этом использует значение своего единственного параметра для доступа к расположенному в начале выделенного фрагмента блоку служебной (дополнительной) информации, поэтому при передаче любого другого адреса поведение free не определено.  Контракт программиста с функциями malloc / free состоит в его обязанности передать free тот же адрес, который был получен от malloc. Вызов free с некорректным в этом смысле параметром способен привести к повреждению логической карты памяти и краху программы. 40
  • 41. Функция free (2 / 2)  В ходе своей работы функция free:  идентифицирует выделенный блок памяти;  возвращает его в список свободных блоков;  предпринимает попытку слияния смежных свободных блоков для снижения фрагментации и повышения вероятности успешного выделения в будущем фрагмента требуемого размера.  Такая логика работы пары malloc / free избавляет от необходимости передачи длины освобождаемого блока как самостоятельного параметра. 41
  • 42. Стандарт POSIX  POSIX (Portable Operating System Interface for UNIX) — набор стандартов, разработанных IEEE и Open Group для обеспечения совместимости ОС через унификацию их интерфейсов с прикладными программами (API), а также переносимость самих прикладных программ на уровне исходного кода на языке C.  Стандарт IEEE 1003 содержит четыре основных компонента:  «Основные определения» (том XBD);  «Системные интерфейсы» (том XSH) — в числе прочего описывает работу с сигналами, потоками исполнения (threads), потоками В/В (I/O streams), сокетами и пр., а также содержит информацию обо всех POSIXсовместимых системных подпрограммах и функциях;  «Оболочка и утилиты» (том XCU);  «Обоснование» (том XRAT).  POSIX-совместимыми ОС являются IBM AIX, OpenSolaris, QNX и др. 42
  • 43. Практикум №2 Постановка задачи  Решить индивидуальные задачи №№3, 4 и 5 в соответствии с формальными требованиями.  Для этого в блоге дисциплины:  узнать постановку задач. 43