К. Касперски - Техника оптимизации программ, Эффективное использование памяти (1127752), страница 33
Текст из файла (страница 33)
Глава к "7 "'"' вт»цс1 11ве от оЬЗ [ во»ост 11во от оь] *пехтн »и оЬЗ аьь» [14 ] 1 оьз Ьобу[НООО]; 1пт обработка классического списка // НЕОПТИИИЗИРОВНННЫЙ СПИСОК // указатель на слелухимя объект // атрибуты объекта /нечто компактное вт»пот Ывт ОГ Овг [ во»цсе Ывт ОГ ОВО *лехе; 1пт оЬЗ ате»[НТТН Я1НЕ]; 1пт оЬ1 Ьо»]у[ВОЬН ЗРЗЕ]; // тело объекта /нечто монстровое во»цст Ызт ОГ ОВО *авве от оЬЗ, *Сир ]ьвт от оЬЗ; // вывеление памяти тьвт от оЬЗ = [вт»цст Ызт ОГ ОНО*] вмшосэз[Н ЕЬГИ*в1»еот[вт»цст Ызт ОГ Оащ ] // инициализация списка »о» [а = О; а < Н НЬЕН; ан.] тьвт от оЬ1[а].пехт = 11вт от оЬ] + а т 1; 11ве от оЬ1[Н НЬЕН-1].пело = О; Имеет ли смысл помещать элементы такой структуры в раздельные массивы? Давайте подсчитаем.
Длина пакетного цикла обмена в подавляющем большинстве случаев составляет 32 (Кб/Р-И/Р-П1) или б4 байта (А[Ион). СуММа ДЛИН ПОЛЕЙ оЬЗ ать» И обо Ьобу — 60 байт. СЛЕданатЕЛЬНО, ВСЕГО ЛИШЬ 10% загруженных ячеек остаются невостребованными. Это весьма незначительная потеря и ей, по идее, можно безболезненно пренебречь. Но, прежде чем делать окончательный вывод, не поленимся и сравним оптимизированный и неоптимизированный варианты на практике. (Полный исходный текст программы читатель найдет в файле ~згс'](2!.[пеп]огу],!!Е[.ОЬЗ.С, который находится на прилагаемом компакт-диске.) Оперативная память // трассировка списка Сшр ) 15С ог оЬ] — 155С ос оЬ бо( сот(ассг = О( ассг < пттп Бтяк/ ассгь+) х ь.= сшр 1551 об оЬ)(0).оЬ) ассг(асгг] нп11е(гшР 1гвС ог оЬ] = Сл1Р 1151 ос аЬ](0).пехС) Обработка оптимизированного раздельного списка // Опт)таикирбяанньй списОк зггпсг ЫЯТ ОГ ОВО ОРТ1М12ЕО 5СГПС" Ь|БТ Ог ОВП ОРТ1М12ЕП *ПЕХС/ // указа~ель на следукший объект 511051 РКяяПН/К // указатель на атрибуты (зто плохо!) *об] аггг; Ре15е гвг оЬ] аССг(АТТК Б12Е)( // атрибуты обьекта (это хорошо!) ((епб11 *оЬ] Ьгх(у( // указатель на тело объекта гпс *1гвС ос оЬ.
орС1ло веб, 5СГПСС ЫБТ ОГ ОВО ОРТ1М12ЕО "Сгр 115С от оЬ] оргсшсхеб/ 0 вылеленпе паи.ти 1гзг от обя оРСглп сея = (зегпсв ЫЯТ ОГ ОВП ОРТТМ12КП*) шаЫос32(Н ЕЬЕМ*вгтео1(аггее 11БТ ОГ ОВП ОРТ1М1220))/ // иниииалпзапия сгиска сот ~а = 0; а Н ЕЬГМ са+ ) (15с ос оь; орс1кагеб!а),пехс -- 115~ ос оь] орсглпгеб + а с 1; Фсгбес РЕББ1М12Е 115С об оЬ] орггкпгеб(а].оЬ] агвг =.:ла11ос(всгеоб(гпС)*АТТИ Б12Е) Еепб " Глава 2 11вС от оЬ» орггиггеб[а).оь» Ьобу = ки11оо[в1гео1[ппт.) Весу 51ЕЕ) 1 яС о.
оЬ» орС ппгеб[Ч ЕЬЕН-1).пехС = О; уу трассировка списка Сир 1гвг от оЬ» оргмп1геб = 1гяС от оЬ» орС и1гебп ба[ тот[аког = Ог аССС < ЛТТЕ 51ЕЕ; аССга ) ха = скр 1г*с ое оЬ» оркгпп геб[О),оЬ» ассг[аосх]; иЛ11е[Спр 11вС об оЬ» орС1погеб = Сир 1гвС ат оЬ» орСгппгеб[О).пехС)г Ого! Оптимизированный вариант обгоняет своего "классического" коллегу в скорости на целых 60% (рнс.
2.23) — весьма суц[ественный прирост производительности, не так лн? Рис. 2.23. Демонстрация зффективносги различных подходов к оптимизации структуры (листинг 2.13). Учет особенностей страничной организации памяти (см. далее) позволяет более чем в два раза сократить время обработки списка К сожалению, оптимизированный вариант весьма капризен. Если заменить оптимизированную структуру (лнстннг 2.) 3): вгхосС 1гвС ос оЬ» орС1по.геб ясаооС 11вС о" оь» орсгпо геб *пехог Оперативная память 1о7 тпе оЬЗ атзл[аттн я1ЯЕ]! *оЬ] Ьобув впх на ее ближайший аналог: ввгпов 11вв от аЬ] ореьппхеб вьгиов 11ве от аЬ] орввшввеб впп *пахе; *оьз ае~ *оЬ] Ьобу] 1пь то разница между оптимизированным и неоптимизированным вариантом составит всего лишь 30уо]й (см.
рис. 2.23). Совершенно непонятно: чем же впе *оЬ] аеьг Хужс, ЧЕМ 1пт оЬ] агьг[йтта Я12Е]?! И ТЕМ бОЛЕЕ НЕПОНятНО, За счет чего в последнем случае достигается такая производительность. Мистика прямо какая-то! Ведь, исходя из самых общих соображений, ясно: количество загруженных ячеек памяти во всех случаях должно быть одинаково! // перебор различных значений шага чтения памяти тот]а ЯтЕР Гйетсш а < ИРЛ ЯтЕР Я12Е; а +- ЯтЕР ГКСТОК] 1 Р Гает ЯитЬВ О загрузка огранил з ТЬВ тот Ы=О;1<=ЯЬОСК Я1ЯЕ; хе=4*К] х += Нпа *] ] Нпт]р + в+32] Репбьг // < — начало замера времени выполнения 1 ЗЕЯТН]0] > 1=01 // чтение паввяти о шагом а гог]ь = 0; ь < нйх ттекв ьв+] Учитывайте станичную организацию памяти Ненадолго оторвемся от этой маленькой головоломки и исследуем зависимость времени трассировки списка от шага чтения памяти.
Казалось бы, какие тут могут быть сюрпризы? А вот попробуйте с холу объяснить форму кривой, полученной с помощью слелуюшей программы (листинг 2.)5). (Полный исходный текст программы читатель найдет в файле ~ягст,(21гпегпогу~ )]яг.Егер.с, который находится на прилагаемом компакт-диске.) У58 Глава 2 к е- *ылк *~ (гтпюр е ш; е= а; ь висюш // < — конец венера времени нкюслненин Кривая (рис. 2.24) и впрямь ведет себя очень интересно. Время трассировки — вопреки всем прогнозам — не остается постоянным, а увеличивается вместе с шагом! Правда, это увеличение не бесконечно — при достижении отметки в 32 Кбайт лля Р-П~Р-И! и б4 Кбайт для АМП Абйоп кривая достигает максимума насыщения и переходит в горизонтальное "плато", превышающее "подножье" по высоте более чети в пять раз! Это слишком большая величина, и мы не можем просто взять и проигнорировать ее, но чем же все-таки вызван рост времени обработки?! Конечно, оперативная память неоднородна и время доступа к ней непостоянно, но чтобы она была наслтолыго неоднородна.
Рис. 2.24. График, иллюстрирующий время обработки блока данных в зависимости от шага чтения. На Р-Н! насыщение наступает на шаге чтения данных, равном З2 Кбайт, а на АМП Азыоп — 64 Кбайт. Причем, на участке (О; 4 Кбайт) происходит резкий "взлет" кривой, особенно хорошо заметный на АМ0 Араон Оперативная память У59 Какой физической реальности соответствует отметка в 32 Кбайт (а на Ат)з)оп и вовсе в б4 Кбайт)".
Структур такого размера в памяти (насколько мы знаем память) заведомо нет. Между тем, время обработки при увеличении шага растет. Почему" .Можно конечно, отступиться от этой проблемы (" знаете, процессор — зглп такая сложная вещь" как ответили автору в службе технической поддержки), но ведь она так и останется "занозой" в теле и будет ныть. Нс г уж, разбираться — так разбираться! Если немного доработать программу, заставив ее выводить время доступа к каждой ячейке, то обнаружится (см. исходный текст программы ~згсц21пзептогу~пзепзогу,аау.с, которая находится на прилагаемом компактдиске), что через каждые четыре килобайта кривая внезапно изгибается в неприступный зубец (рис. 2.25), "отьедаюшии" десятки тысяч тактов процессора! Нет, это не типографская ошибка.
Все так и должно быть — ведь практически все современные операционные системы (и утптг)отуз с (3)кНХ в том числе) использую~ страничную организацию памяти. Рис. 2.25. График, иллюстрирующий зависимост~ времени доступа к ячейке от адреса втой ячейки при последовательном обращении к данным. Смотрите— при первом же обращении к странице возникает пауза в десятки тысяч тактов! Не останавливаясь подробно на этом вопросе (си. "!лге! Агсл!тесшге 5оу2ттаге !)ете!орегй Молва! Ко(игле 3 Бусгет Ргоратттд биЫе", у З.б РАб!уУО2, отме- 1ЕО Глава 2 тим лишь тот факт, что с каждой страницей связана специальная 32-битная структура данных, содержащая атрибуты страницы и ее базовый адрес.
При первом обращении к странице процессор считывает эти данные из физической памяти в свой внутренний буфер (именуемый П, — Тгапз!айоп воок аэ!де ВЫТег), и последующие обращения к той же самой станице происходят без задержек вплоть до вытеснения этой информации из ТЕВ. Так вот "где собака порылась"! Оказывается, минимальной порцией' обмена с памятью является отнюдь не 32-байтный' пакетный цикл, а целая страница! Время задержки, возникающей при первом обращении к странице, лишь в полтора-два раза уступает времени чтения всей этой страницы, — т.