Ответ 1
Эта тема очень близка моему сердцу и недавним исследованиям, поэтому я буду смотреть на нее с нескольких сторон: история, некоторые технические заметки (в основном академические), результаты тестов на моей коробке и, наконец, попытка ответить на ваш актуальный вопрос когда и где rep movsb
может иметь смысл.
Частично, это вызов для обмена результатами - если вы можете запустить Tinymembench и поделиться результатами вместе с подробной информацией о конфигурации вашего ЦП и ОЗУ, это было бы здорово. Особенно, если у вас есть 4-канальная установка, блок Ivy Bridge, серверный блок и т.д.
История и официальные советы
История выполнения инструкций по быстрому копированию строк была чем-то вроде ступеньки - то есть периоды неизменной производительности чередовались с большими обновлениями, которые приводили их в соответствие или даже быстрее, чем конкурирующие подходы. Например, произошел скачок производительности в Nehalem (в основном с целью увеличения накладных расходов при запуске) и снова в Ivy Bridge (большая часть была нацелена на общую пропускную способность для больших копий). Вы можете найти десятилетней давности представление о трудностях реализации rep movs
инструкции от инженера Intel в этой теме.
Например, в руководствах, предшествующих введению Ivy Bridge, типичный совет - избегать их или использовать их очень осторожно 1.
В текущем (ну, июнь 2016 г.) руководстве содержатся различные запутанные и несколько непоследовательные советы, например 2:
Конкретный вариант осуществления выбирается во время выполнения на основе расположения данных, выравнивания и значения счетчика (ECX). Например, MOVSB /STOSB с префиксом REP следует использовать со значением счетчика, меньшим или равным трем, для лучшей производительности.
Так для копий 3 или менее байтов? Вам не нужен rep
префикс, который в первую очередь, так как при заявленном запуске латентности ~ 9 циклов вы почти наверняка лучше с простым типом DWORD или QWORD mov
с небольшим количеством битого вертела, чтобы замаскировать неиспользованные байт (или, возможно, с 2 явными байтами, слово mov
если вы знаете, что размер равен трем).
Они продолжают говорить:
Строковые инструкции MOVE/STORE имеют несколько гранулярностей данных. Для эффективного перемещения данных предпочтительнее большая степень детализации данных. Это означает, что лучшая эффективность может быть достигнута путем разложения произвольного значения счетчика на количество двойных слов плюс однобайтовые перемещения со значением счетчика, меньшим или равным 3.
Это, конечно, кажется неправильным на современном оборудовании с ERMSB, где rep movsb
по крайней мере так же быстро или быстрее, чем варианты movd
или movq
для больших копий.
В целом, этот раздел (3.7.5) настоящего руководства содержит сочетание разумных и сильно устаревших советов. Это обычная пропускная способность руководств Intel, поскольку они обновляются поэтапно для каждой архитектуры (и имеют целью охватить архитектуры почти двух десятилетий даже в текущем руководстве), а старые разделы часто не обновляются, чтобы заменить или дать условную рекомендацию. это не относится к текущей архитектуре.
Затем они подробно освещают ERMSB в разделе 3.7.6.
Я не буду подробно останавливаться на оставшемся совете, но суммирую хорошие моменты в разделе "зачем его использовать" ниже.
Другие важные претензии из руководства заключаются в том, что в Haswell rep movsb
был расширен для использования 256-битных операций внутри.
Технические соображения
Это лишь краткий обзор основных преимуществ и недостатков, что rep
инструкция имеет с точки зрения его реализацией.
Преимущества для rep movs
-
Когда выдается команда
rep
movs, CPU знает, что должен быть передан весь блок известного размера. Это может помочь ему оптимизировать работу так, как это невозможно с дискретными инструкциями, например:- Избегание запроса RFO, когда он знает, что вся строка кэша будет перезаписана.
- Выдача запросов на предварительную выборку немедленно и точно. Аппаратная предварительная выборка хорошо справляется с обнаружением шаблонов
memcpy
-like, но для запуска все равно требуется несколько операций чтения, и она будет "перегружаться" перед многими строками кэша за пределами конца скопированной области.rep movsb
точно знает размер региона и может точноrep movsb
.
-
По-видимому, нет никакой гарантии упорядочения между магазинами в течение 3-х
rep movs
которые могут помочь упростить согласованный трафик и просто другие аспекты перемещения блока, по сравнению с простыми инструкциямиmov
которые должны подчиняться довольно строгому упорядочению памяти 4. -
В принципе, команда
rep movs
может использовать различные архитектурные приемы, которые не представлены в ISA. Например, в архитектурах могут быть более широкие внутренние пути данных, которые ISA предоставляет 5, иrep movs
могут использовать их внутри.
Недостатки
-
rep movsb
должен реализовывать определенную семантику, которая может быть сильнее, чем основное программное обеспечение. В частности,memcpy
запрещает перекрывающиеся области и поэтому может игнорировать эту возможность, ноrep movsb
позволяет им и должен давать ожидаемый результат. В текущих реализациях в основном влияет на загрузку, но, вероятно, не на пропускную способность большого блока. Аналогично,rep movsb
должен поддерживать байт-гранулярные копии, даже если вы на самом деле используете его для копирования больших блоков, кратных некоторой большой степени 2. -
Программное обеспечение может иметь информацию о выравнивании, размере копии и возможном псевдониме, которые не могут быть переданы аппаратному обеспечению при использовании
rep movsb
. Компиляторы часто могут определять выравнивание блоков памяти 6 и, таким образом, могут избежать значительной части работы при запуске, которуюrep movs
должен выполнять при каждом вызове.
Результаты теста
Вот результаты тестирования для множества различных методов копирования из tinymembench
на моем i7-6700HQ с тактовой частотой 2,6 ГГц (очень плохо, что у меня одинаковый процессор, поэтому мы не получаем новую точку данных...):
C copy backwards : 8284.8 MB/s (0.3%)
C copy backwards (32 byte blocks) : 8273.9 MB/s (0.4%)
C copy backwards (64 byte blocks) : 8321.9 MB/s (0.8%)
C copy : 8863.1 MB/s (0.3%)
C copy prefetched (32 bytes step) : 8900.8 MB/s (0.3%)
C copy prefetched (64 bytes step) : 8817.5 MB/s (0.5%)
C 2-pass copy : 6492.3 MB/s (0.3%)
C 2-pass copy prefetched (32 bytes step) : 6516.0 MB/s (2.4%)
C 2-pass copy prefetched (64 bytes step) : 6520.5 MB/s (1.2%)
---
standard memcpy : 12169.8 MB/s (3.4%)
standard memset : 23479.9 MB/s (4.2%)
---
MOVSB copy : 10197.7 MB/s (1.6%)
MOVSD copy : 10177.6 MB/s (1.6%)
SSE2 copy : 8973.3 MB/s (2.5%)
SSE2 nontemporal copy : 12924.0 MB/s (1.7%)
SSE2 copy prefetched (32 bytes step) : 9014.2 MB/s (2.7%)
SSE2 copy prefetched (64 bytes step) : 8964.5 MB/s (2.3%)
SSE2 nontemporal copy prefetched (32 bytes step) : 11777.2 MB/s (5.6%)
SSE2 nontemporal copy prefetched (64 bytes step) : 11826.8 MB/s (3.2%)
SSE2 2-pass copy : 7529.5 MB/s (1.8%)
SSE2 2-pass copy prefetched (32 bytes step) : 7122.5 MB/s (1.0%)
SSE2 2-pass copy prefetched (64 bytes step) : 7214.9 MB/s (1.4%)
SSE2 2-pass nontemporal copy : 4987.0 MB/s
Некоторые ключевые выводы:
- Методы
rep movs
быстрее, чем все другие методы, которые не являются "rep movs
" 7 и значительно быстрее, чем подходы "C", которые копируют 8 байтов за раз. - "Невременные" методы более быстрые, примерно на 26%, чем
rep movs
, - но это намного меньшая дельта, чем та, о которой вы сообщили (26 ГБ/с против 15 ГБ/с = ~ 73%). - Если вы не используете невременные хранилища, использование 8-байтовых копий из C почти так же хорошо, как 128-битная загрузка/сохранение SSE. Это потому, что хороший цикл копирования может генерировать достаточное давление памяти для насыщения полосы пропускания (например, 2,6 ГГц * 1 магазин/цикл * 8 байт = 26 ГБ/с для магазинов).
- В tinymembench нет явных 256-битных алгоритмов (за исключением, вероятно, "стандартного"
memcpy
), но это, вероятно, не имеет значения из-за приведенного выше примечания. - Увеличенная пропускная способность невременных хранилищ приближается к временным примерно в 1,45x, что очень близко к 1,5x, которые можно ожидать, если NT исключит 1 из 3 передач (т.е. 1 чтение, 1 запись для NT против 2 читает, 1 пишу).
rep movs
кrep movs
лежат посередине. - Комбинация довольно низкой задержки памяти и скромной 2-канальной полосы пропускания означает, что этот конкретный чип способен насыщать пропускную способность памяти из одного потока, что резко меняет поведение.
-
rep movsd
похоже, использует ту же магию, что иrep movsb
на этом чипе. Это интересно, потому что ERMSB только явно нацелен наmovsb
а более ранние тесты на более ранних архивах с ERMSB показывают, чтоmovsb
работает намного быстрее чемmovsd
. В основном это академическийmovsb
, так как вmovsd
случаеmovsb
является более общим, чемmovsd
.
Haswell
Глядя на результаты Haswell, любезно предоставленные iwillnotexist в комментариях, мы видим те же общие тенденции (наиболее важные результаты извлечены):
C copy : 6777.8 MB/s (0.4%)
standard memcpy : 10487.3 MB/s (0.5%)
MOVSB copy : 9393.9 MB/s (0.2%)
MOVSD copy : 9155.0 MB/s (1.6%)
SSE2 copy : 6780.5 MB/s (0.4%)
SSE2 nontemporal copy : 10688.2 MB/s (0.3%)
rep movsb
все еще медленнее, чем rep movsb
memcpy
, но здесь только на 14% (по сравнению с ~ 26% в тесте Skylake). Преимущество техник NT над их временными собратьями теперь составляет ~ 57%, даже немного больше, чем теоретическое преимущество сокращения полосы пропускания.
Когда вы должны использовать rep movs
?
Наконец, удар по вашему актуальному вопросу: когда или почему вы должны его использовать? Это опирается на вышеизложенное и вводит несколько новых идей. К сожалению, нет простого ответа: вам придется поменять различные факторы, в том числе те, которые вы, вероятно, даже не знаете точно, например, будущие разработки.
Обратите внимание, что альтернативой rep movsb
может быть оптимизированный libc memcpy
(включая копии, встроенные компилятором), или это может быть свернутая вручную версия memcpy
. Некоторые из перечисленных ниже преимуществ применимы только по сравнению с одной или другой из этих альтернатив (например, "простота" помогает против версии, выпущенной вручную, но не против встроенной memcpy
), но некоторые применимы к обеим.
Ограничения на доступные инструкции
В некоторых средах есть ограничение на определенные инструкции или использование определенных регистров. Например, в ядре Linux использование регистров SSE/AVX или FP, как правило, запрещено. Поэтому большинство оптимизированных вариантов memcpy
нельзя использовать, так как они опираются на регистры SSE или AVX, а простая 64-битная копия mov
-based используется на x86. Для этих платформ использование rep movsb
обеспечивает большую часть производительности оптимизированного memcpy
без нарушения ограничений на код SIMD.
Более общим примером может быть код, предназначенный для многих поколений оборудования и не использующий аппаратную диспетчеризацию (например, с использованием cpuid
). Здесь вы можете быть вынуждены использовать только более старые наборы инструкций, что исключает любые AVX и т.д. rep movsb
может быть хорошим подходом, поскольку он позволяет "скрытый" доступ к более широким загрузкам и хранилищам без использования новых инструкций. Если вы ориентируетесь на аппаратное обеспечение, предшествующее ERMSB, вам придется посмотреть, приемлема ли там производительность rep movsb
, хотя...
Будущая проверка
rep movsb
аспектом rep movsb
является то, что rep movsb
он может использовать преимущества архитектурных улучшений будущих архитектур без изменений в исходном коде, чего не может сделать явное перемещение. Например, когда были введены 256-битные пути данных, rep movsb
смог воспользоваться ими (как утверждает Intel) без каких-либо изменений, необходимых для программного обеспечения. Программное обеспечение, использующее 128-битные перемещения (что было оптимальным до Haswell), должно быть модифицировано и перекомпилировано.
Таким образом, это одновременно и преимущество в обслуживании программного обеспечения (не нужно менять исходный код), и преимущество для существующих двоичных файлов (не нужно развертывать новые двоичные файлы, чтобы воспользоваться преимуществами улучшения).
Насколько это важно, зависит от вашей модели обслуживания (например, от того, как часто на практике внедряются новые двоичные файлы), и от того, насколько быстро эти инструкции появятся в будущем, очень сложно судить. По крайней мере, Intel является своего рода руководством для использования в этом направлении, взяв на себя обязательство по крайней мере разумной производительности в будущем (15.3.3.6):
REP MOVSB и REP STOSB продолжат работать на будущих процессорах достаточно хорошо.
Перекрытие с последующей работой
Это преимущество, конечно же, не будет отображаться в простом тесте memcpy
, который по определению не должен перекрывать последующую работу, поэтому размер выгоды должен быть тщательно измерен в реальном сценарии. Использование максимального преимущества может потребовать реорганизации кода, окружающего memcpy
.
Это преимущество указано Intel в их руководстве по оптимизации (раздел 11.16.3.4) и в их словах:
Когда известно, что подсчет составляет, по меньшей мере, тысячу байт или более, использование расширенного REP MOVSB /STOSB может предоставить другое преимущество для амортизации стоимости непотребляющего кода. Эвристику можно понять, используя значение Cnt = 4096 и memset() в качестве примера:
• Реализация memset() в 256-битной SIMD должна выпустить/выполнить исключить 128 экземпляров операции хранения байтов 32- с VMOVDQA, прежде чем непотребляющие последовательности команд смогут выйти на пенсию.
• Экземпляр расширенного REP STOSB с ECX = 4096 декодируется как длинный микрооперационный поток, предоставляемый аппаратными средствами, но удаляется как одна команда. Есть много операций store_data, которые должны завершиться, прежде чем результат memset() может быть использован. Поскольку завершение операции сохранения данных не связано с удалением программного порядка, значительная часть потока неиспользуемого кода может обрабатываться посредством выдачи/выполнения и удаления, по существу, без затрат, если непотребляющая последовательность не конкурирует для хранения ресурсов буфера.
Таким образом, Intel говорит, что после всех некоторых мопов код после rep movsb
как выдал rep movsb
, но, хотя многие магазины все еще находятся в полете, а rep movsb
в целом еще не удалился, мопы из следующих инструкций могут добиться большего прогресса через машин, чем они могли бы, если бы этот код появился после цикла копирования.
Все мопы из явного цикла загрузки и хранения должны фактически удаляться отдельно в программном порядке. Это должно произойти, чтобы освободить место в ROB для следующих мопов.
Похоже, не так много подробной информации о том, как работают очень длинные микрокодированные инструкции, такие как rep movsb
. Мы не знаем точно, как ветки микрокода запрашивают другой поток мопов из секвенсора микрокода, или как мопы удаляются. Если отдельным мопам не нужно выходить на пенсию отдельно, возможно, вся инструкция занимает только один слот в ROB?
Когда внешний интерфейс, который передает механизм OoO, видит rep movsb
в кэше rep movsb
, он активирует ПЗУ rep movsb
микрокодов (MS-ROM) для отправки микрокодов в очередь, которая передает этап выпуска/переименования. Возможно, что другие rep movsb
смешаться с этим и выпустить/выполнить 8, пока rep movsb
все еще выдает, но последующие инструкции могут быть извлечены/декодированы и rep movsb
сразу после того, как последний rep movsb
делает, в то время как некоторые из копий не имеют еще не выполнено. Это полезно только в том случае, если хотя бы часть вашего последующего кода не зависит от результата memcpy
(что не является необычным).
Теперь размер этого преимущества ограничен: самое большее, вы можете выполнить N инструкций (в действительности, rep movsb
) за пределами медленной команды rep movsb
, в которой вы остановитесь, где N - размер ROB. При текущих размерах ROB в ~ 200 (192 в Haswell, 224 в Skylake) это максимальное преимущество ~ 200 циклов бесплатной работы для последующего кода с IPC 1. В 200 циклов вы можете скопировать где-то около 800 байтов при 10 ГБ /s, поэтому для копий такого размера вы можете получить бесплатную работу, близкую к стоимости копии (таким образом, сделав копию бесплатной).
Однако, поскольку размеры копий становятся намного больше, относительная важность этого быстро уменьшается (например, если вместо этого вы копируете 80 КБ, бесплатная работа составляет всего 1% от стоимости копирования). Тем не менее, это довольно интересно для скромных размеров копий.
Циклы копирования также не полностью блокируют выполнение последующих инструкций. Intel не вдавается в подробности о размере выгоды или о том, какие копии или окружающий код приносят наибольшую пользу. (Горячий или холодный пункт назначения или источник, код с высокой задержкой ILP или низкой задержкой ILP после).
Размер кода
Размер исполняемого кода (несколько байтов) является микроскопическим по сравнению с типичной оптимизированной подпрограммой memcpy
. Если производительность вообще ограничена ошибками i-кеша (включая кеширование uop), уменьшенный размер кода может быть полезным.
Опять же, мы можем ограничить размер этого преимущества в зависимости от размера копии. На самом деле я не буду работать с этим численно, но интуиция заключается в том, что уменьшение размера динамического кода на B байтов может сэкономить не более C * B
кеш-пропусков при некоторой константе C. Каждый вызов memcpy
за собой memcpy
кеш-памяти (или выгода), но преимущество более высокой пропускной способности зависит от количества копируемых байтов. Таким образом, при больших передачах более высокая пропускная способность будет доминировать в эффектах кэша.
Опять же, это не то, что будет отображаться в простом тесте, где весь цикл, без сомнения, поместится в кэш UOP. Вам понадобится тест на месте, чтобы оценить этот эффект.
Оптимизация для конкретной архитектуры
Вы сообщили, что на вашем оборудовании rep movsb
был значительно медленнее, чем платформа memcpy
. Тем не менее, даже здесь есть сообщения об обратном результате на более раннем оборудовании (например, Ivy Bridge).
Это вполне правдоподобно, поскольку кажется, что операции перемещения строк получают любовь периодически - но не каждое поколение, поэтому вполне может быть быстрее или, по крайней мере, привязано (в этот момент оно может выиграть на основе других преимуществ) в архитектурах, где оно было доведен до даты, только чтобы отставать в последующем оборудовании.
Цитирование Энди Glew, который должен знать вещь или два об этом после реализации их на Р6:
большая слабость создания быстрых строк в микрокоде заключалась в том, что [...] микрокод терял связь с каждым поколением, становясь все медленнее и медленнее, пока кто-то не успел его исправить. Так же, как в библиотеке люди теряют слух. Я предполагаю, что возможно, что одной из упущенных возможностей было использование 128-битных загрузок и хранилищ, когда они стали доступны, и так далее.
В этом случае это можно рассматривать как очередную оптимизацию, специфичную для платформы, для применения в типичных подпрограммах memcpy
каждый трюк в книге", которые вы найдете в стандартных библиотеках и JIT-компиляторах: но только для использования на архитектурах, где она есть. лучше. Для JIT или AOT-скомпилированного материала это легко, но для статически скомпилированных двоичных файлов это требует специфической для платформы диспетчеризации, но это часто уже существует (иногда реализуется во время компоновки), или аргумент mtune
может использоваться для принятия статического решения.
Простота
Даже на Skylake, где кажется, что он отстает от самых быстрых невременных методов, он все же быстрее, чем большинство подходов, и очень прост. Это означает меньше времени на проверку, меньше загадочных ошибок, меньше времени на настройку и обновление реализации memcpy
монстра (или, наоборот, меньшую зависимость от прихотей разработчиков стандартной библиотеки, если вы полагаетесь на это).
Платформы с задержкой
Алгоритмы 9, ограничивающие пропускную способность памяти, могут фактически работать в двух основных режимах: ограничение полосы пропускания DRAM или ограничение параллелизма/задержки.
Первый режим - это тот, который вам, вероятно, знаком: подсистема DRAM имеет определенную теоретическую полосу пропускания, которую вы можете довольно легко рассчитать на основе количества каналов, скорости передачи данных/ширины и частоты. Например, моя система DDR4-2133 с 2 каналами имеет максимальную пропускную способность 2,133 * 8 * 2 = 34,1 ГБ/с, как и в ARK.
Вы не будете поддерживать более высокую скорость от DRAM (и, как правило, несколько меньше из-за различной неэффективности), добавляемого ко всем ядрам сокета (т.е. Это глобальный предел для систем с одним сокетом).
Другое ограничение связано с тем, сколько одновременных запросов ядро может выдать подсистеме памяти. Представьте, что ядро может одновременно выполнять только один запрос для 64-байтовой строки кэша - когда запрос завершен, вы можете выдать другой. Предположим также очень высокую задержку памяти 50 нс. Тогда, несмотря на большую пропускную способность DRAM (34,1 ГБ/с), вы получите 64 байта /50 нс = 1,28 ГБ/с или менее 4% от максимальной пропускной способности.
На практике ядра могут выдавать более одного запроса за раз, но не неограниченное количество. Обычно подразумевается, что между L1 и остальной частью иерархии памяти имеется только 10 буферов заполнения строки на ядро и, возможно, 16 или около того буферов заполнения между L2 и DRAM. Предварительная выборка конкурирует за те же ресурсы, но, по крайней мере, помогает уменьшить эффективную задержку. Для получения более подробной информации посмотрите на любые замечательные сообщения , написанные Dr. Bandwidth на эту тему, в основном на форумах Intel.
Тем не менее, большинство последних процессоров ограничены этим фактором, а не пропускной способностью ОЗУ. Обычно они достигают 12–20 ГБ/с на ядро, а пропускная способность ОЗУ может составлять 50+ ГБ/с (в 4-канальной системе). Лишь некоторые недавние 2-канальные "клиентские" ядра поколения, которые, кажется, имеют лучшие неядерные ядра, возможно, большее количество линейных буферов могут достичь предела DRAM на одном ядре, и наши чипы Skylake, похоже, являются одним из них.
Теперь, конечно, есть причина, по которой Intel разрабатывает системы с пропускной способностью DRAM 50 ГБ/с, в то же время поддерживая пропускную способность <20 ГБ/с на ядро из-за ограничений параллелизма: первое ограничение распространяется на сокеты, а второе - на ядро. Таким образом, каждое ядро в 8-ядерной системе может выдавать запросы на 20 ГБ/с, после чего они снова будут ограничены DRAM.
Почему я продолжаю об этом? Поскольку лучшая реализация memcpy
часто зависит от того, в каком режиме вы работаете. Как только вы ограничены DRAM BW (как, видимо, и есть наши чипы, но большинство не на одном ядре), использование невременных записей становится очень важным, так как экономит чтение для владения, которое обычно тратит 1/3 вашей пропускной способности. Вы видите это точно в результатах теста выше: реализации memcpy, которые не используют хранилища NT, теряют 1/3 своей пропускной способности.
Однако если у вас ограничен параллелизм, ситуация выравнивается, а иногда и наоборот. У вас есть запасная пропускная способность DRAM, поэтому хранилища NT не помогают, и они могут даже повредить, поскольку они могут увеличить задержку, поскольку время передачи обслуживания для буфера строки может быть больше, чем в сценарии, когда предварительная выборка приводит линию RFO в LLC (или даже L2), а затем магазин завершает свою работу в LLC для эффективной более низкой задержки. И, наконец, на основных ядрах серверов обычно гораздо медленнее хранилища NT, чем на клиентских (и с высокой пропускной способностью), что усиливает этот эффект.
Так что на других платформах вы можете обнаружить, что NT-хранилища менее полезны (по крайней мере, если вы заботитесь о однопоточной производительности) и, возможно, rep movsb
выигрывает где (если он получает лучшее из обоих миров).
Действительно, этот последний пункт является вызовом для большинства испытаний. Я знаю, что хранилища NT теряют свое явное преимущество для однопоточных тестов на большинстве архитектур (включая текущие серверные архивы), но я не знаю, как rep movsb
будет работать относительно...
Рекомендации
Другие хорошие источники информации, не интегрированные в выше.
Comp.Arch расследование rep movsb
против альтернатив. Множество хороших заметок о прогнозировании ветвлений и реализации подхода, который я часто предлагал для небольших блоков: использование перекрывающихся первых и/или последних операций чтения/записи вместо попыток записи только точно необходимого количества байтов (например, реализация все копии размером от 9 до 16 байтов в виде двух 8-байтовых копий, которые могут перекрываться до 7 байтов).
1 Предположительно, намерение состоит в том, чтобы ограничить его случаями, когда, например, размер кода очень важен.
2 См. Раздел 3.7.5: Префикс REP и перемещение данных.
3 Важно отметить, что это применимо только к различным хранилищам в пределах одной инструкции: после завершения блок хранилищ все еще выглядит упорядоченным по отношению к предыдущим и последующим хранилищам. Таким образом, код может видеть магазины из rep movs
не по порядку относительно друг друга, но не относительно предыдущих или последующих магазинов (и это последняя гарантия, которая вам обычно требуется). Это будет проблемой только в том случае, если вы используете конечный пункт назначения копирования в качестве флага синхронизации вместо отдельного хранилища.
4 Обратите внимание, что невременные дискретные хранилища также избегают большинства требований к упорядочению, хотя на практике rep movs
обладают еще большей свободой, поскольку все еще существуют некоторые ограничения по упорядочению для хранилищ WC/NT.
5 Это было обычным явлением в последней части битовой эры 32-, где многие микросхемы имели 64-битные тракты данных (например, для поддержки FPU, которые имели поддержку 64-битного double
типа). Сегодня "стерилизованные" микросхемы, такие как марки Pentium или Celeron, отключили AVX, но, по-видимому, rep movs
микрокоды могут по-прежнему использовать 256-битные загрузки/хранилища.
6 Например, из-за правил выравнивания языка, атрибутов или операторов выравнивания, правил совмещения имен или другой информации, определенной во время компиляции. В случае выравнивания, даже если точное выравнивание не может быть определено, они, по крайней мере, смогут поднять проверки выравнивания из циклов или иным образом исключить избыточные проверки.
7 Я делаю предположение, что "стандарт" memcpy
выбирает не-временный подход, который весьма вероятно, для этого размера буфера.
8 Это не обязательно очевидно, так как это может быть случай, когда поток rep movsb
генерируемый rep movsb
просто монополизирует диспетчеризацию, и тогда он будет очень похож на явный случай mov
. Однако, похоже, что это не так - мопы из последующих инструкций могут смешиваться с мопами из микрокодированного rep movsb
.
9 Т.е. те, которые могут выдавать большое количество независимых запросов памяти и, следовательно, насыщать доступную полосу пропускания DRAM-к-ядру, из которых memcpy
будет дочерним элементом плаката (и в сочетании с нагрузками с чисто латентной привязкой, такими как погоня за указателями).