Нижегородский Государственный Университет Им. Н.И. Лобачевского Факультет Вычислительной Математики И Кибернетики Учебно-исследовательская Лаборатория "Информационные Технологии" Техника оптимизации программ Куратор: Мееров И.Б. Разработчики: Зебрин Д.А. Бадер А.А. 2003 Содержание 1. Введение. 2. Оптимизация использования оперативной памяти. 3. Оптимизация использования КЭШа. ННГУ 2 IT Lab Если вы собрались оптимизировать программу, подумайте... А ВАМ ЭТО НАДО? ННГУ 3 IT Lab 1.1 Виды оптимизации Современные компьютеры так мощны, что даже WINDOWS XP оказывается бессильна их затормозить! Крис Касперски • Оптимизация по времени. • Оптимизация по требуемой памяти. ННГУ 4 IT Lab 1.2 Требования к оптимизирующим алгоритмам • Максимальная машинная независимость и переносимость на другие платформы. • Трудоёмкость разработки (в т.ч. тестирование) не должна превышать 10%-15%. • Алгоритм должен давать выигрыш не менее чем на 20%-25% в скорости выполнения. • Оптимизация должна допускать безболезненное внесение изменений. ННГУ 5 IT Lab 1.3 Правила оптимизации 1. Прежде, чем оптимизировать код, следует иметь надёжно работающий не оптимизированный вариант. 2. Основной прирост оптимизации даёт не учёт особенностей системы, а алгоритмическая оптимизация. 3. Прежде чем порываться переписывать программу на ассемблер, изучите ассемблерный листинг компилятора на предмет оценки его совершенства. ННГУ 6 IT Lab 1.4 Проблемы оптимизации • Программное непостоянство, связанное с тем, что в многозадачных ОС приложение попадает под влияние чрезвычайно изменчивой окружающей среды. • Аппаратное непостоянство, вызванное внутренней ”многозадачностью” самого железа. ННГУ 7 IT Lab 1.5 Возможные решения проблем • Выбирать замер с наименьшим временем выполнения, т.к. прогон с минимальным временем исполнения и представляет собой измерение, в минимальной степени испорченное побочными эффектами. • Аппаратное непостоянство неустранимо принципиально. ННГУ 8 IT Lab 2.1.1 Ядро оперативной памяти Время работы программы определяется ее самой медленной частью. Закон Амдала • Ядро состоит из множества ячеек, каждая из которых хранит всего один бит информации. • На физическом уровне ячейки объединяются в прямоугольную матрицу, горизонтальные линейки которой называются строками (Row), а вертикальные – столбцами (Column) или страницами (Page). • Страница является минимальной порцией обмена с ядром динамической памяти. ННГУ 9 IT Lab 2.1.2 Интерфейс ядра • Выводы микросхемы памяти включают в себя линии адреса, линии данных и специальный вывод WE – Write Enable (Разрешение записи). • В случае квадратной матрицы количество адресных линий сокращается вдвое, но и выбор конкретной ячейки памяти отнимает вдвое больше тактов. • Причем, возникает неоднозначность, что именно в данный момент находится на адресной линии: номер строки или номер столбца? ННГУ 10 IT Lab 2.1.3 Интерфейс ядра • Решение этой проблемы потребовало двух дополнительных выводов, сигнализирующих о наличии столбца или строки на адресных линиях и окрещенных RAS (от row address strobe - строб адреса строки) и CAS (от column address strobe - строб адреса столбца) соответственно. • Задержка между подачей номера строки и номера столбца на техническом жаргоне называется "RAS to CAS delay" (на сухом официальном языке - tRCD). Задержка между подачей номера столбца и получением содержимого ячейки на выходе - "CAS delay" (или tCAC), а задержка между чтением последней ячейки и подачей номера новой строки - "RAS precharge" (tRP). ННГУ 11 IT Lab 2.2 Взаимодействие памяти и процессора • Процессор взаимодействует с оперативной памятью не на прямую, а через специальный контроллер. • Запросы процессора обрабатывает чипсет, который включает в себя : – Контроллер шины (BIU – Bus Interface Init) – Контроллер памяти (MCT – Memory Controller) – Планировщик запросов памяти (MRO – Memory Request Organizer). ННГУ 12 IT Lab 2.3 Отображение физических DRAM-адресов на логические. Оперативная память это. - Однородный массив данных , доступ к ячейкам которого осуществляется посредством 32-разрядных указателей. (с точки зрения процессора). - Физическое пространство, которое крайне не однородно и делится на банки, адреса страниц и номера столбцов. (на самом деле). Согласованием интерфейсов оперативной памяти и процессора занимается чипсет. Обеспечить эффективную обработку больших массивов данных без учёта архитектурных особенностей DRAM – невозможно. ННГУ 13 IT Lab 2.4 Оптимизация работы с памятью • Оперативная память является одним из самых узких мест, сдерживающих производительность всей системы. • Типовые алгоритмы обработки данных задействуют быстродействие оперативной памяти едва ли на треть, а зачастую и менее того! • Грамотно организованный обмен данными выполняется, как правило, в три – четыре раза быстрее, причём эффективное взаимодействие с памятью достижимо на любом языке (в том числе интерпретируемом!), а не ограничено лишь одним ассемблером. ННГУ 14 IT Lab 2.5.1 Разворачивание циклов Глубокая развёртка цикла сокращает время его выполнения более чем в 2 раза! Здесь примеры оптимизированного и не оптимизированного циклов. for (a=0; a<666; a++) // не оптимизированный x+=p[a]; // цикл for (a=0; a<666; a+=2) { x+=p[a]; // оптимизированный цикл x+=p[a+1]; // с двукратным разворотом } ННГУ 15 IT Lab 2.5.2 Разворачивание циклов (чтение) 100% 90% время обработки блока 80% 70% 60% 50% 40% 30% 100%100% 66% 93% 62% 98% 55% 50% 54% 40% 53% 48% 51% 38% 1:1 1:2 1:4 1:8 1:16 1:32 1:54 20% 10% 0% P-III 733/133/100/I815EP ННГУ AMD Athlon 1050/100/100/VIA KT133 16 IT Lab 2.5.3 Разворачивание циклов (запись) 120% время обработки блока 100% 80% 60% 100% 100% 102% 100% 102% 100% 102% 96% 102% 69% 102% 68% 102% 69% 1:1 1:2 1:4 1:8 1:16 1:32 1:64 40% 20% 0% P-III 733/133/100/I815EP ННГУ AMD Athlon 1050/100/100/VIA KT133 17 IT Lab 2.6.1 Устранение зависимостей по данным • Если запрашиваемые ячейки оперативной памяти имеют адресную зависимость по данным (т.е. попросту говоря, одна ячейка содержит адрес другой), процессор не может обрабатывать их параллельно и вынужден простаивать в ожидании поступления адресов. • В общем случае время загрузки N ячеек равно: – Зависимых t = N*(Tch + Tmem ), где Tch – латентность чипсета, а Tmem – латентность памяти – Независимых t =Tch+Tmem+N/C, где С – пропускная способность подсистемы памяти. ННГУ 18 IT Lab 2.6.2 Устранение зависимостей по данным Тест пропускной способности оперативной памяти 500 450 Пропускная способность (Мб/с) 400 350 300 250 200 150 100 50 184 155 458 268 0 P-III 733/133/100/I815EP Athlon 1050/100/100/VIA KT133 Чтение зав исимых данных ННГУ 19 Чтение не зав исимых данных IT Lab 2.7.1 Параллельная обработка данных • Линейное чтение независимых данных ещё не обеспечивает их параллельной обработки т.к. чтение двух смежных ячеек в большинстве случаев инициирует один, а не два запроса к подсистеме памяти. • Эффективный алгоритм обработки данных: при первом проходе цикла память читается с шагом 32 байта (64/128), что заставляет генерировать запросы чипсету при каждом обращении к памяти. ННГУ 20 IT Lab 2.7.2 Варианты чтения ячеек ННГУ 21 IT Lab Демонстрация эффективности параллельного чтения 700 2.7.3 Пропускная способность (Мбайт/c) 600 500 400 300 200 458 601 636 268 100 0 P-III 733/133/100/I815EP Athlon 1.050/100/100/VIA KT133 Последов ательное чтение ННГУ 22 Параллельное чтение IT Lab 2.8.1 Оптимизация ссылочных структур данных Как оптимизировать прохождение по списку, если адрес следующего элемента заранее неизвестен, а список сильно фрагментирован? Первое что приходит на ум: разбить один список на несколько независимых списков, обработка которых осуществляется параллельно. Наиболее оптимальная стратегия разбиения достигается при разбиении списка на шесть независимых частей. Это чётко видно из следующей диаграммы. Можно многократно увеличить производительность программы, если при добавлении в конец списка не трассировать весь список, в специальном поле сохранять указатель на последний элемент в списке. ННГУ 23 IT Lab 2.8.2 ННГУ 24 IT Lab 2.8.3 Уменьшение размера структур данных • При однократном обрабатывании фиксированного количества элементов структуры данных скорость обработки обратно пропорциональна шагу обработки(при условии что размер элемента списка меньше 32 байт). Следовательно данные в памяти нужно располагать так плотно, как только это возможно. • Производительность системы можно значительно повысить, если использовать раздельные (separated) структуры данных. ННГУ 25 IT Lab 2.9.1 Рассмотрим следующую структуру. Классическое представление списка – крайне не оптимально с точки зрения подсистемы памяти IBM PC ННГУ 26 IT Lab 2.9.2 Разделение списка и усечение разрядности указателей ННГУ 27 IT Lab Сравнение классического и оптимизированного списков 100% 2.10.4 90% Время трассировки 80% 70% 60% 50% 40% 30% 20% 30% 100% 100% 27% 10% 0% P-III 733 Athlon 1.400 Classic ННГУ 28 Optimized IT Lab 2.10.1 Группировка операций чтения с операциями записи • При обработке больших массивов данных, многократно превышающих емкость кэш-памяти всех уровней, необходимости избегать смешивания команд чтения памяти с командами записи в действительности нет. ННГУ 29 IT Lab время обработки 140% 130% 120% 110% 100% 90% 80% 70% 60% 50% 40% 30% 20% 10% 0% 2.10.2 60% 69% без компенсации разв орот а P-III/733/133/100/I815EP ННГУ 105% 126% с компенсацией разв орот а AMD Athlon 1050/100/100/VIA KT133 30 IT Lab 2.11.1 Оптимизация сортировки больших массивов данных • Одному из лучших алгоритмов сортировки – quick sort требуется О(n*ln n) операций в среднем и О(n²) в худшем случае. • Линейный алгоритм – О(n) операций в худшем случае. На компьютере AMD Athlon 1050 он упорядочивает десять миллионов чисел всего за 0,3 с., что в сто раз быстрее алгоритма quick sort! ННГУ 31 IT Lab 0,3 313,5 0,05 3,6 0,003 0,07 0,001 0,0047 0,00003 0,00035 2.11.2 0,00001 320 300 280 260 240 220 200 180 160 140 120 100 80 60 40 20 0 0,00002 время сортировки (сек) Сравнение времени сортировки разными алгоритмами 100 1.000 10.000 100.000 1.000.000 10.000.000 qsort 0,00002 0,00035 0,0047 0,07 3,6 313,5 linear sort 0,00001 0,00003 0,001 0,003 0,05 0,3 кол-во сортируемых чисел ННГУ 32 IT Lab превосходство линейной сортировки (крат) 270 260 250 240 230 220 210 200 190 180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 10 0 2.11.3 2,6 2,7 100 P-III 733/133/100/I815EP ННГУ 15 14 1.000 42 43 10.000 33 37 100.000 AMD Athlon 1050/100/100/VIA KT133 33 143 71 250 250 1.000.000 2.000.000 кол-во сортируемых чисел IT Lab 3.1 Оптимизация КЭШа Кэш-подсистема представляет собой сплошное скопление чудес, сюрпризов и сказок Крис Касперски Кэш (называемый также сверхоперативной памятью) представляет собой высокоскоростное запоминающее устройство небольшой емкости для временного хранения данных, значительно более быстродействующее, чем основная память, но, в отличии от оперативной памяти, не адресуемое и непосредственно “невидимое” для программиста. ННГУ 34 IT Lab 3.2 Цели и задачи КЭШ-памяти • Обеспечение быстрого доступа к интенсивно используемым данным. • Согласование интерфейсов процессора и контроллера памяти. • Упреждающая загрузка данных. • Отложенная запись данных. ННГУ 35 IT Lab 3.3 Расположение КЭШа в иерархии оперативной памяти ННГУ 36 IT Lab 3.4 Стратегия помещения данных в КЭШ-память Чтобы продать что-нибудь ненужное, надо сначала купить что-нибудь ненужное. кот Матроскин • LRU (Least Recently Used) – вытесняет то, к чему дольше всего не обращались. • FIFO (First Input First Output) - вытесняет то, что было загружено раньше всех. • Randomize-алгоритм – “кидает монетку”. ННГУ 37 IT Lab 3.5 Упреждающая загрузка данных • Загрузка по требованию (on demand). • Спекулятивная (speculative) – необходимы алгоритмы угадывания – неинтеллектуальные алгоритмы – интеллектуальные алгоритмы. ННГУ 38 IT Lab 3.6 Организация КЭШа • Понятие кэш-линейки (cash-line). • Понятие ассоциативности КЭШа (прямое отображение). • Наборно-ассоциативный КЭШ. • Политика записи – сквозная запись (WT – Write True write policy) – обратная политика записи (WB – Write Back write policy). ННГУ 39 IT Lab 3.7 Двухуровневая организация КЭШа ННГУ 40 IT Lab 3.8.1 КЭШ-подсистема современных процессоров • MOB (Memory Order Buffer – буфер упорядоченной записи в память). • L1-Cashe. • Буферы записи. • Блок интерфейсов с памятью (MIU). • Блок интерфейсов с шиной (BIU). • L2-Cashe. • Двойная независимая шина (DIB – Dual Independent Bus). ННГУ 41 IT Lab 3.8.2 Основные характеристики • Размер кэша первого уровня. • Степень ассоциативности и размер банков кэша. • Политика записи. • Длина кэш-линий. ННГУ 42 IT Lab 3.9 Влияние размера обрабатываемых данных на производительность (для AMD K6) 500 удельное время обработки одной ячейки 450 400 350 300 250 200 150 100 50 0 1 33 65 97 129 161 193 225 257 289 321 353 385 417 449 481 513 545 577 609 641 673 705 R ННГУ W RW WR 43 размер обрабатываемого блока (Кб) IT Lab 3.10.1 Влияние размера исполняемого кода на производительность При разработке программы стремитесь проектировать ее так, чтобы все часто используемые циклы вмещались в кэш первого или по крайней мере второго уровня. ННГУ 44 IT Lab 3.10.2 Изменение удельного времени выполнения команд 35 удельное время выполнения одной команды 30 25 20 15 10 5 0 размер исполняемого болока (Кб) 4 8 16 32 64 128 256 512 1024 2048 Athlon 1,18 1,14 1,2 1,1 1,09 3,79 3,8 30,9 32,65 32,65 P-III 2,6 2,5 2,5 3,3 3,3 3,3 3,3 14 14 14 ННГУ 45 IT Lab 3.11.1 Выравнивание данных Выравнивание данных необходимо, когда данные выходят за границу кэш-линейки и своим “хвостом” попадают в следующую кэш-линейку – операция чтения занимает уже 6-12 тактов, а данные называются расщепленными (англ. Line-splint). ННГУ 46 IT Lab 3.11.2 Виды выравнивания • Естественное выравнивание. • Выравнивание пользователем: #pragma pack. Пример “плохой” и “хорошей” программы: static int a; static char b; static int c; static char d; ННГУ static int a; static int c; static char b; static char d 47 IT Lab 3.12 Учет ограниченной ассоциативности КЭШа Обработка ячеек памяти с шагом, равным или кратным размеру КЭШбанка, крайне не производительна и этого любой ценой следует избегать. Пример “плохой” программы: for (a=0; a<googol; a++) { a1=bar[4096]; a2=bar[4096]; a3=bar[4096]; a4=bar[4096]; a5=bar[4096]; } ННГУ 48 IT Lab 3.13.1 Предвыборка • Предвыборка позволяет программисту заранее загружать в кэш ячейки памяти, к которым рассчитывает обращаться в будущем. • “Ручное” управление КЭШ-контроллером позволяет выбрать оптимальную стратегию упреждающей загрузки данных. ННГУ 49 IT Lab 3.13.2 Проблема разницы архитектуры • Реализация программной предвыборки различны для разных типов процессоров (Intel и AMD). • Приходится реализовывать функции, использующие предвыборку в 2 вариантах, что зачастую “съедает” выигрыш в производительности. ННГУ 50 IT Lab 3.13.3 Аппаратная предвыборка в микропроцессоре Pentium-4 • P-4 отслеживает регулярные шаблоны обращения к данным, что позволяет предугадывать , к каким КЭШ-линейкам в будущем произойдет обращение. • Алгоритм: распознавание арифметическую прогрессию и вычислять ее члены. • Упреждающая загрузка осуществляется только в пределах одного 4-килобайтового блока памяти, при выходе за его пределы отслеживание шаблонов начинается с начала. ННГУ 51 IT Lab 3.13.4 Предпочтительная КЭШиерархия • Для многократно используемых данных предпочтительно делать предвыборку в КЭШ-уровни всех иерархий. • Однократно используемые данные и данные гарантированно не вытесняемые из КЭШа первого уровня в КЭШ второго уровня загружать нецелесообразно. ННГУ 52 IT Lab 3.13.5 Практическое использование предвыборки • Смысл использовать предвыборку появляется тогда, когда мы можем предсказать адрес следующей обрабатываемой ячейки. • Максимальная производительность достигается в случаях: - предвыборка данных происходит в кэшиерархию, соответствующую их назначению. - запрашиваемые данные загружаются в точности к моменту обращения. - происходит предвыборка только тех данных, которые действительно необходимы. ННГУ 53 IT Lab 3.13.6 Увеличение эффективности • Предотвращение холостого хода. • Уменьшение количества инструкций предвыборки. ННГУ 54 IT Lab Заключение • Разворачивайте циклы, читающие память. • Отправляйте контроллеру памяти несколько запросов одновременно. • Группируйте операции чтения памяти с операциями записи. • Используйте все страницы памяти к которым обращались, целиком. • Комбинируйте вычисления с доступом к памяти. • Обращайтесь к памяти только тогда, когда это действительно необходимо. • Никогда не оптимизируйте программу на отдельно взятой машине. • Циклы, которые часто используются, должны умещаться в КЭШ • Следите за выравниванием данных при объявлении переменных • Используйте предвыборку данных, когда это возможно ННГУ 55 IT Lab Литература • Техника оптимизации программ. Эффективное использование памяти. Крис Касперски • Intel Processor Identification and the CPUID Instruction (www.intel.com) • Intel Architecture Software Developer’s Manual (www.intel.com) • Intel Architecture Optimization Manual (www.intel.com) ННГУ 56 IT Lab