К. Касперски - Техника оптимизации программ, Эффективное использование памяти (1127752), страница 68
Текст из файла (страница 68)
В противном случае процессор вынужден простаивать в ожидании выгрузки содержимого буферов и тогда запись с буферизацией ведет себя как обычная запись. Другая не менее значимая функциональная обязанность буферов — это упорядочивание записи. Данные, поступающие в буферы, неупорядочены и попадают в них по мере завершения команд микрокода. Здесь они сортируются и вытесняются с буферов уже в порядке следования оригинальных машинных команд программы. Отсюда: переполнение буферов автоматически влечет за собой блокировку кэш-памяти, до тех пор, пока не завершится выгрузка данных, вызвавших (пава 3 кэш-промах, все остальные попытки записи в память (независимо, завершаются ли они кэш-попаданием или нет) будут временно заблокированы. Не очень-то хорошая перспектива, не правда ли? Этой ситуации можно избежать, если планировать обработку данных так, чтобы скорость заполнения буферов не превышала скорости их выгрузки в кэш и/или оперативную память.
Процессоры Р-П и Р-П! содержат двенадцать 256-битовых буферов, каждый из которых может соответствовать любой 32-байтовой области памяти, выровненной по границе 32 байт. Другими словами, буферы записи представляют собой 384-байтовый полностью ассоциативный кэш, состоящий из 12 кэш-линеек по 32 байта каждая. Так, надеюсь, будет понятно? Процессор Р-4 содержит аж 24 буфера записи, каждый из которых, по всей видимости, вмещает 512 бит данных. Процессор АМР Ай)оп так же, как и Р-11/Р-1П содержит 12 буферов записи, совмещенных с буферами чтения. Об их длине в документации ничего не говорится (или просто я не нашел?), но можно предположить, что вместимость каждого из буферов составляет 64 байта.
Конечно, это вдвое меньше, чем у процессора Р-4, но АМО Агй(оп в отличие от своего конкурента, всегда выгружает содержимое буферов в кэш первого уровня, в то время как процессоры Рб и Р-4 при отсутствии записываемых данных в кэше первого уровня, выгружают их в кэш второго. Трудно сказать, чья политика лучше.
С одной стороны, при записи большого количества данных, обращений к которым в обозримом будущем не планируется, 1пге1 заметно выигрывает, поскольку не "засоряет" кэш первого уровня. С другой, выгрузка буферов на АМО АгЬ!оп происходит несколько быстрее, что уменьшает вероятность возникновения "заторов". Но, в то же время, 256-битовая шина Р-!П и Р-4, соединяющая кэш первого с кэшем второго уровня, практически стирает разницу в их производительности и буферы выгружаются даже с большей скоростью, чем на Агй)оп. Правда, Аг!з!оп, умеющий вытеснять содержимое каша первого уровня параллельно с записью в него новых данных, от такого расклада вещей ничуть не страдает и в общем, этот поединок "детей" двух гигантов процессорной индустрии можно продолжать бесконечно. Да и есть ли в этом смысл? Ведь при оптимизации программы все равно приходится закладываться на худшую конфигурацию, т, е, в данном случае на двенадцать 32- байтовых буферов записи.
Посмотрим (см, "1пге1® Агсййесшге Орг(пз(айаг!оп Ве(егепсе Манна( Огг)ег ХцпЬег; 245127-001"), что говорит !пге! по этому поводу. "Иг((е Ь((з сипло( разе вг((е т(ззез, за рег/огтапсе аГ спяса( (аарз сап Ье (тргагес( Ьу зсйес(и((пя Йе вп(ез (о тетагу. и%ел уои ехрес( (а зее вг((е ттез, зсйес(и(е (ье вп(е тз(гиспопз (п ягаирз па (агяег Йап гве(уе, апс( зсьес(и(е а(ьег тз(гис((апз ье(аге зсьес(и((паХиг(Ьег вгГ(е тетисцапз". Кзш 347 ("Оападанггя луа Игу '/ заггнси не преадалевают промахи записи, поэтому произвадшпельнасть критических циклов лгаэкепг ныть увеличена планированием записей в палгять.
Когда вы ажидаегпе промах запгюи, абьединяйте инструкции записи в группы не более челг па двенадцать и разделяйте их группами других инструкций ".) Эта рекомендация содержит по крайней мере три неточности. Первая и наименее сушественная из них: на самом леле, количество членов группы может быть и более двеналцати, вель выгрузка буферов осуществляется параллельно с их заполнением. Объединение 14 команд записи не несет никакого риска, во всяком случае при вьиолнении программы под процессоры Р-П/Р-П1/АМЕ) А11г(огг и уже тем более Р-4. Другой момент.
Под словом "двенадцатьу авторы документации подразумевали "лвеналцать команд записи с различными установочными адресами". На Р-П/Р-П1 в установочный адрес вхолят биты с 4 по 31 линейный адрес, а на Р-4/АМ 0 Абп1оп — с 5 по 31. Следовательно при последовательной записи 32-разрядных значений "штатная" численность каждой группы команд составляет не двеналцать, а девяноста шесть "душ". Наконец, третье и последнее. Поскольку все записываемые данные в обязательном порядке проходят через буферы, предыдущее замечание в не меньшей степени справелливо и лля модификации данных, уже находяшихся в кзш-памяти перового уровня.
С другой стороны, к данным, еше не вытесненным из буфера, можно обрашаться многократно и "записью" зто не считается. Боюсь, что своими уточнениями я вас окончательно запутал. Но "блуждание в трущобах непонимания все-таки лу цце столбовой дороги грубого заблуждения". Давайте рассмотрим следуюгций пример (листинг 3.1б). (Полный исходный текст программы читатель найлет в файле глвгс\131.сас(ге~ туг!ге з1гг.с, который находится на прилагаемом компакт-диске.) Как вы думайте, можно ли назвать его оптимальным? й!!йьс13(ги!г а,з 6;:::КвндгИдет'иа опте м ИЗВЦию посредотвоМ',, Сгеицеи!(Оййгягиомввусгт';.'!,;.',;,:1)~!1г ; 'зециоли" е,',вычилслительйыми операциями '..:.:„;: '!':.,'.;;,"".,:.',;.:;,;'",,:'г'.!:„~~л.,;;:,г(го ' з ' 6. ' ' '.".;,: неоптулвлзггровйглгьдл вдиелнт М гзг яяейюл тигле бябдб дают н суьеле гзб байт памяти, М ято вдвое превьляает емкость буферов зывлси на М процессорах рггл'р111. Где-то на половине исполнения М возникнет затор л дадьнейаие операмли змгиси будут Глава 3 З4В // исполняться гораздо медленнее, т.
е. им придется всякий раз дозидаться выгрузки буферов Гог(а = О( а < 192( а ь — — 8] // для устранения накладных раскодоа цикл // должен быть разаернут, иначе его О расиепление снизит произаодительность р(а ' О) р(а ь 1] ь О]/ (а (а — (а — (а (а (а (а — (а 1(7 2] ( ь 3]7 4]; 5] 7 6(( 7]; р(а + 2] р(а ь 3] р(а ь 4) р(а ь 5) р(а ь 6] р(а ь 7) О делаем некоторые аычисления Гог(Ь = О/ Ь < 667 Ьь") х ь= х/соз(х] Кстати, вычислительный цикл также целесообразно разбить на две половинки, поместив первую из них перед циклом записи, а вторую позади него.
Это гарантирует, что независимо от состояния буферов записи на момент Конечно же, цикл гог а на процессорах Р-11/Р-1П исполняется весьма не оптимально. Даже если предположить, что на момент его начала все буферы "девственно" чисты (что вовсе не факт!), не успеет он перевалить за половину, как свободные буферы действительно кончатся и начнутся сильные "тормоза", поскольку все последующие операции записи будут вынуждены ждать освобождения буферов.
Ввиду постоянной занятости шины ждать придется довольно долго. Зато потом, в вычислительном цикле, она (шина) будет простаивать. Экий нерационализм! Давайте реорганизуем наш цикл так, разбив его на несколько подциклов, каждый из которых заполнял бы не более двенадцати-четырнадцати буферов (листинг 3.17). В данном случае, как нетрудно рассчитать, потребуется всего два цикла (192/9б = 2). Между циклами записи памяти мы разместим вычислительный цикл.
Желательно спланировать его так, чтобы за время вычислений все буферы гарантированно успевали бы освободиться. Попутно отметим, что если время вычислений значительно превосходит время выгрузки буферов, то такую перегруппировку выполнять бессмысленно, т.
к. в этом случае производительность в основном определяется скоростью вычислений, но не записью памяти. Кзш начала выполнения записываюшего цикла, все буферы будут действительно пусть(. Между прочим, этот факт не отмечен ни в одной известной мне инструкции по оптимизации, включая оригинальные руководства от фирм !П(е! и АМВ. !Полный исходный текст программы (листинг 3.)7) читатель найдет в файле ~бгс~!3!.Оас!)е~(чг)ге гй.с, который находится на прилагаемом компакт-диске.) ОПТИИИЗИРОВАННЫЙ ВАРИАНТ О выполняем часть загланированных вычислений // с таким расчетом, чтобы буферам хватило времени О на сброс их текушего содерзимаго, ведь отнюдь // не факт, что на момент начала выполнения цикла // буферы пусты ГОГ(Ь =.
О( Ь < ЗЗ/ Ьтт) х '-= х/сов(х)( // выполняем зб записей ячеек типа ЬИОЯО, что // соответствует емкости буферов записи( к моменту // завершения цикла практически все буферы будут // заполнены. "Практически" — пегому что какая-то часть // из них уяе успеет выгрузиться Тот(а 0; а < 96( а т= В) р(а + 0) = (а т 0)( р(а т 1) = (а т 1)( р(а + 2) = (а т 2) ( р(а + 3) = (а т 3)( р(а + 4) = (а + я)/ р(а + 5) = (а + 5)/ Р(а + б) = (а Ь б)( р(а + т) = (а + т) ( // выполняем оставшуюся часть вычислений.
Будет О просто замечательно, если за зто время все буферы 350 Глава 3 // успеют выгрузиться, — и тогда ьы достигнем // полного параллелизма! Еог(Ь = 01 Ь < 331 Ььь) х ь= х/сов(х); О выполняеы оставкиеся 96 записев. Поскольку они // к етому моменту уже освободились, запись // протекает так быстро, как зто только возможно Еог(а = 9б; а < 192; а ь= 8) т 01 (а.ьс); р(а р(а р(а я 11 = (ае1) '- 2) (а 2)1 31 = (атз); р(а р(а р(а р(а (аья)) е 51 = (а+5) т б] = (аьб) р(а ь 7] =. (аь711 Рис.