leo Так я не на одну-две мерил, а 64-128-256-512-1024 байт. Только на 1024 его слегка попустило. (где-то до 200 тиков, что примерно соответствует латентности моей памяти) Насчет вперед-назад попробую, не пришло сразу в голову. А есть где-нибудь литература по оптимизации потока команд на современных процах? Потому что в мануалах только общие рекомендации А такие изуверские действия, как модификация кода вообще не рассмотрены, только рекомендуют их избегать - ну так это и ежу понятно. А из архитектуры сразу догадаться не получается - я совсем недавно эту тему копаю, еще не освоился.
Из "серьезной" литературы ИМХО только мануалы и Agner Fog (pentopt.pdf), ну и kaspersky - по части оптимизации работы с памятью и кэшем
leo Касперски я почитал, правда еще не все постиг, но в процессе Но там же только по данным, а по коду вроде обещалась 2-я книга серии, буду ждать. А Фог, он, конечно, крут, но его изыскания можно с небольшой натяжкой применить к AMD, а NetBurst, насколько я понимаю, значительно отличается, так что остаются только самотоятельные изыскания Хотя Касперски доказал, что этим тоже можно достичь результатов
Ustus > "его изыскания можно с небольшой натяжкой применить к AMD, а NetBurst, насколько я понимаю, значительно отличается" Чего-то я не понял - с небольшой натяжкой или с большой ? Ты ничего не путаешь ? В "свежем" издании pentopt.pdf Фог рассматривает все пентиумы, включая P4 (правда судя по году издания и отдельным незначительным нестыковочкам в латентностях его цифры относятся к Willamette, а не Northwood) А по атлонам см. AMD Optimization Guid'ы. Там к сожалению по архитектуре не очень много и подробно, но рекомендаций по оптимизации предостаточно. И главное - таблица латентностей ну очень подробная - у Интела одна крайность (маловато данных), у AMD другая устанешь искать нужную инструкцию
leo Ой... скачал Фога... у меня была конкретно устаревшая версия. Сенкс. так и есть... А я вот здесь продолжал мерить модифицируемый код... мистика. На атлоне при записи назад уже в соседнюю кэш-линейку задержка падает до латентности памяти и далее растет пропорционально количеству инструкций. А вот вперед почему-то тормоза держатся до расстояния где-то в 512 байт (восемь линеек?). Не пойму, почему так. И еще я обнаружил, что время выполнения цикла зависит от выравнивания адреса перехода (это общеизвестно), НО! Если построить график времени выполнения от сдвига относительно выравнивания на 16 байт, которое дает минимальное время (ПОЧЕМУ? в смысле, почему не 64 например?) получится нечто странное с несколькими максимумами (мамыму... мимума... фуф, еле набрал . Не постигаю
Ustus > "выравнивания на 16 байт, которое дает минимальное время (ПОЧЕМУ? в смысле, почему не 64 например?" Потому что в AMD K7,K8 как и в P6 инструкции считываются из L1 выравненными блоками по 16 байт. Соответственно, чем больше полезных инструкций содержится в 16-байтном блоке тем лучше, хотя смещение на несколько байт от границы как правило существенной роли не играет - главное было бы чем занять в следующем такте 3 декодера DirectPath. А худший вариант, это когда инструкция на которую происходит переход (таргет) разбивается по границе 16 байт - в этом сл. в загруженном блоке нет ни одной полезной инструкции и один такт пролетает впустую - вместо 3-х возможных DirectPath макроопов на выходе декодера имеем 0 (см. Chapter 4: Instruction Decoding Optimizations в Optimization Guid'ах для Athlon и\или Athlon 64) Насчет разных задержек при записи в код "вперед" и "назад". По видимому дело в хардвардном префетче, ведь на атлонах рулит преддекодирование (разметка) инструкций в L1 и чтобы исключить доп.задержки преддекодирование ИМХО должно выполняться с достаточно большим опережением. Поэтому для линейного кода защитный интервал "вперед", вроде как и должен быть больше, чем "назад". Но насчет 512 байт ничего не могу сказать - думал что меньше )
Курю мануалы... ох и нелегко там чего-то найти... leo, вот в посте Сдвиг влево для удаления символа ты говорил про зависимость по CF интрукции dec. Чего-то я не могу найти такого. Только у Интелов: Но это получается, что создается зависимость следующей инструкции от предыдущей, или я опять чего-то не понял?
Ustus > "получается, что создается зависимость следующей инструкции от предыдущей" Совершенно верно. Это называется зависимость по флагам и такую зависимость имеют все инструкции изменяющие флаги не целиком, а частично (dec,inc,clc,cld и т.п.). В частности sub ecx,1 вычисляет результат и переписывает eflags целиком, поэтому не зависит от предыдущих операций. А вот dec ecx не изменяет флаг CF, поэтому для установки eflags ей нужно взять CF от предыдущей операции - возникает зависимость. Конечно это не всегда приводит к потерям тиков - флаги могут быть уже готовы или следующие за dec операции могут не зависить от флагов (если за dec идет не jcc, а что-то другое). Тем не менее потенциальная зависимость есть, поэтому ИМХО inc\dec лучше использовать там, где действительно необходимо сохранить СF от предыдущей операции. PS: Не знаю почему AMD умалчивают о проблеме частичного изменения eflags - или просто не договаривают, или может у них какая хитрая продвинутая реализация установки и анализа флагов при jcc (по идее для выполнения dec+jnz флаг CF не нужен, но "объяснить" это каменным аллокаторам и планировщикам ИМХО не так просто И кстати в Athlon Optimization в примере разбора куска кода по блокам\тикам представлено так, что inc не ждет установки флагов пред.операций. Но это не 100% показатель крутости, т.к. например если верить Фогу, то в P4 inc\dec состоит из двух мопов, поэтому моп сложения\вычитания независим и может выполниться без ожидания, а вот второй моп - установки eflags может задержаться, поэтому тут возможна зависимость от следующей за dec инструкции, если ей нужны флаги (ИМХО)
leo А PII, похоже выравнивание переходов пофиг... Ну, не совсем... похоже он выгребает лишний тик на переходе через кэш-линейку. Ну и почему?
Ustus Возможно потому, что в P6 для декодирования считывается два последовательных блока по 16 байт (double buffer 32 байта). А вот в AMD буфер почему-то только 24 байта, да и обозначается странно как 8х3 (по 8 байт на 3 декодера ?). Поэтому на P6 возможна зависимость от размера тела цикла (если тело целиком умещается в буфере), ну и ес-но от времени исполнения - если исполнение идет медленнее декодирования, то у процессора есть время на подгрузку лишнего блока инструкций и выравнивание вроде как по барабану Вообще выравнивание циклов на P6 дело хитрое, т.к. выравняв метку цикла можно наткнуться на другую неприятность - инструкция перехода может разбиваться границей блока декодирования или быть первой в блоке. В этом сл.потери более ощутимы, особенно на быстрых операциях, т.к. 1) нужно считать доп.блок для декодирования jcc, 2) полезная инструкция в нем всего одна, поэтому декодер за такт выдаст всего 1 моп вместо 3-6, 3) тут же нужно загружать следующий блок по метке перехода. Поэтому по жизни бывают ситуации, когда выравнивание метки цикла на 16 дает на P6 несколько худшие результаты, чем без выравнивания или с выравниванием на 4 или 8
leo Фух... Описание исполнительных устройств Athlon'а занимает три с половиной страницы , но в мой мозг оно не помещается - что уж тут про NetBurst говорить Чувствую, надо его сначала на русский перевести, а потом читать. А как адекватно переводится predecode, early decoding и issue? Насчет: чего-то не нахожу. Там (AMD Athlon™ Processor x86 Code Optimization Guide, Appendix A) только вот: Но это уже после выборки, а про выборку - только что она делается из кэш-линейки 16-ти байтовым окном... Такое впечатление, что инструкции преддекодируются прямо из кэша.(?)
Ustus А ты лучше AMD Athlon™ Processor x86 Code Optimization Guide (22007.pdf) посмотри 1) Chapter 4: Instruction Decoding Optimizations "The instruction bytes are then merged into a 24-byte instruction queue." 2) Appendix B: Pipeline and Execution Unit Resources Overview. Там подробно с картиночками расписаны стадии фетча и декодирования. На картинке недвусмысслено изображена Quardword Queue 8x3 и дается описание стадии ALIGN1: "Because each 8-byte buffer (quadword queue) can contain up to three instructions, ALIGN1 can buffer up to a maximum of nine instructions, or 24 instruction bytes. ALIGN1 tries to send three instructions from an 8-byte buffer to ALIGN2 per cycle." А вот для Athlon 64 в начале Chapter 4 читаем: "The instruction bytes are then merged into a 32-byte pick window." Т.е. в К7 очередь (буфер) инструкций 24 байта, а в К8 32 байта. В P6 окно декодирования работает несколько иначе: в 32-байтный буфер считывается два выравненных блока по 16 байт (из одной или из двух соседних линеек L1) и затем из этого буфера выделяется 16-байтный блок декодирования (А.Фог называет его IFETCH-block) > "Чувствую, надо его сначала на русский перевести, а потом читать" Вот этого ИМХО делать не стоит, т.к., как ты верно заметил, возникают проблемы с адекватностью перевода - лучше называть вещи своими аглицкими именами, чем придумывать им трехэтажные русские эквиваленты. Суть-то ведь понятна. В AMD декодирование делится на три этапа 1) predecode осуществляется при загрузке кода в L1, фактически это просто декодер длин и разметка границ соседних инструкций, 2) early decoding - по сути это основное декодирование, т.к. большинство инструкций сразу раскладывается на опы (микроопы по терминологии Intel), но остаются неразложенными простые двухмоповые операции типа add r2,m32 (ALU + AGU), поэтому декодирование и называется early, 3) окончательное декодирование двухмоповых макроопов производится в планировщике Integer Sheduler. Кстати аналогичный подход использован и в Pentium M - там это называется microop-fusion: мопы ALU-операции и load\store хранятся вместе в виде одного "макроопа" и разделяются только на момент исполнения.
leo Вот это: я не совсем постигаю. Почему 8 байт это ДО трех инструкций? (может быть и меньше?) Почему не четыре? И что там находится - только опкод инструкции, насколько я понял? И откуда вообще берется 16 байт на predecode, ведь длина инструкции максимум 15?
Ustus Подробностей я не знаю. Но очевидно, что смысл predecode состоит именно в предварительной разметке границ инструкций, определении местоположения опкода и различении типов DirectPath\VectorPath. Эта дополнительная служебная инфа также хранится в L1 и занимает 3 бита на байт кода. А вот как она попадает в декодер - то ли дополнительно к 16 байтам кода, то ли вставляется внутрь этих 16 байт - я не знаю. Соответсвенно и не понятно окуда берется ограничение в 3 инструкции DirectPath на 8 байт - то ли это просто связано с быстродействием, то ли физически в этих 8 байтах может сидеть только 3 инструкции, а остальное служебные биты\байты. Но ясно, что 8 байтные блоки на стадии ALIGN1 это уже не чистые байты кода, а подготовленные и выравненные на стадии SCAN: "SCAN determines the start and end pointers of instructions. SCAN can send up to six aligned instructions...", т.е. 3 "выравненных" инструкций на 8 байт или 6 на 16. Но это ес-но максимум и для длинных инструкций может быть меньше. И с чего ты взял, что "там находится - только опкод инструкции", а остальное где ? ) Скорее наоборот - инструкции плюс байты выравнивания и служебные биты\байты > "откуда вообще берется 16 байт" Странный вопрос Просто число кратное размеру линейки L1, 8 - маловато, 32 - многовато ))
leo Тот самый случай путаницы в терминологии. Я имел ввиду, что префиксы, смещение, непосредственное значение туда не входят. А вот еще неясное местооттуда же) что значит "may prevent"? А may и не prevent??? Я-то считал, что VectorPath затыкает декодирование DirectPath по-любому... Вообще, чего я тут прицепился - у меня был безумный проект сделать что-то вроде CodeAnalyst'ового пайплайн симулятора, только с более тонкими настройками и для широкого спектра процов - в идеале от P5 до плюс бесконечности. Но я начинаю чувствовать что-то очень нехорошее
Ustus > "А вот еще неясное место" Просто неточное, т.к. в другом месте (Chapter 4) сказано четко: "VectorPath instructions block the decoding of DirectPath instructions."