Аппаратные и программные циклы: кто быстрее?

15 Янв 2015

Аппаратные и программные циклы: кто быстрее?

Понятие цикла известно каждому программисту. А те, кто досконально разбирается в архитектуре персональных платформ, знают, что циклы бывают не только программными, но и аппаратными. В системе команд x86, созданной более 30 лет назад, с момента появления имеются так называемые строковые инструкции, реализующие операции запол­не­ния блока константой, копирования блока, сравнения двух блоков и поиск заданного элемента в блоке. Казалось бы, аппаратная реали­за­ция любой функции должна быть производительнее программной. Давайте разберемся, что происходит на самом деле.

 

История вопроса

В первом 16-разрядном процессоре Intel 8086 (советский аналог К1810ВМ86) аппаратная реализация обработки строк, как и положено, работала быстрее программной. Аппаратный цикл реализовался одной инструкцией: для копирования блока, например, используется инструкция MOVS (Move String) с префиксом повторения REP (Repeat).

Программная реализация потребует цикла, в теле которого будет три инструкции: чтение из блока-источника, запись в блок-получатель, и инструкция цикла, уменьшающая счетчик на единицу с последующим переходом на начало цикла, если счетчик не достиг нуля. При каждом проходе по циклу, чтение инструкций, составляющих тело цикла, выполнялось из оперативной памяти. Это были те тяжелые времена, когда о кэш-памяти еще никто и понятия не имел.

Кэш-память рождает интригу

С появлением процессора Intel 80486 ситуация существенно изменилась. Во-первых, процессор содержал 8 килобайт внутренней кэш-памяти, в связи с чем, чтение инструкций, составляющих тело цикла, осуществлялось внутри процессора без операций на шине (если, конечно, размер тела цикла в сочетании с используемыми данными не превышает 8 килобайт и инструкции размещены в кэш-памяти).

Во-вторых, разработчики процессора сделали акцент на оптимизации выполнения простых инструкций: пересылки регистр-память и память-регистр. Но они обошли вниманием сложные инструкции, в частности, — строковые.

От слов — к делу

В нашей тестовой лаборатории, с помощью программного обеспечения собственной разработки, проведено  экспериментальное сравнение производительности программного и аппаратного циклов при выполнении операции копирования блоков различных размеров. Исследуемая платформа — ноутбук ASUS N750JK c процессором Intel Core i7-4700HQ. Тактовая частота 2.4GHz, повышаемая в режиме Intel Turbo Boost до 3.4GHz. Установлено 16 гигабайт оперативной памяти DDR3-1600 (PC3-12800), работающей в двухканальном режиме. Операционная система — Microsoft Windows 8.1 64 бита.

Программный цикл построен на основе AVX-инструкций VMOVAPD, перемещающих 256-битные операнды из памяти в регистр и обратно.

Аппаратный цикл построен на основе строковой инструкции REP MOVSQ, выполняющей копирование блока-источника в блок-получатель. Размер операнда - 64 бита. Это максимальный размер операнда для строковых инструкций.

Результаты представлены в виде графиков зависимости скорости копирования блока от его размера. Процессор исследуемой платформы имеет три уровня кэш-памяти: L1=32 килобайта, L2=256 килобайт, L3=6 мегабайт. Очевидно, для блоков размером не более 32 килобайт, размещаемых в L1, скорость будет максимальной. Для блоков, размером более 6 мегабайт, вызывающих переполнение кэш-памяти всех уровней, скорость будет минимальной. Поскольку у нас операция копирования блока, в кэш-памяти требуется разместить блок-источник и блок-получатель. Поэтому, например, кэш-память L1, размер которой 32 килобайта, будет переполнена, если размер копируемого блока превысит 16 килобайт. Также отметим, что скорость обработки блоков, размером 1…6 килобайт может быть низкой, поскольку время выполнения инициализации цикла будет сравнимо с временем выполнения целевой операции.

Принятые обозначения

MBPS (Megabytes per Second), скорость копирования блока в мегабайтах в секунду.

CPI (Clocks per Instruction) — количество тактов на инструкцию (группу инструкций), выполняющих копирование одного элемента данных, либо на одну итерацию аппаратного цикла.

TSC (Time Stamp Counter) — счетчик процессорных тактов; тактовая частота регистра TSC может не соответствовать тактовой частоте процессора при работе в режиме Turbo Boost, что необходимо учитывать при интерпретации результатов.

В правой части графиков визуализируется шестнадцатеричный дамп инструкций, составляющих тело цикла целевой операции или первые 128 байт этого кода.

Результаты

Оценки скорости аппаратного цикла на малых блокахОценки скорости программного цикла на малых блоках

Рис 1. Сравнение производительности для малых блоков. Размер сопоставим с размером кэш-памяти L1. Средняя скорость для аппаратного цикла — 32936 мегабайт в секунду, для программного цикла — 34368 мегабайт в секунду. На малых блоках быстрее работает программный цикл.

Оценки скорости аппаратные цикла на средних блокахОценки скорости программные цикла на средних блоках

Рис 2. Сравнение производительности для средних блоков. Размер сопоставим с размером кэш-памяти L2. Средняя скорость для аппаратного цикла — 22984 мегабайт в секунду, для программного цикла — 18941 мегабайт в секунду. На средних блоках быстрее работает аппаратный цикл.

Оценки скорости аппаратные цикла на больших блокахОценки скорости программные цикла на больших блоках

Рис 3. Сравнение производительности для больших блоков. Размер сопоставим с размером кэш-памяти L3. Средняя скорость для аппаратного цикла — 13770 мегабайт в секунду, для программного цикла — 9907 мегабайт в секунду. На больших блоках быстрее работает аппаратный цикл.

Что осталось за кадром

Производительность строковых инструкций, и соответственно, результаты бенчмарок, могут существенно зависеть от инициализации процессора, выполняемой процедурами BIOS при старте платформы. В частности, имеет значение состояние бита Fast Strings Enable в регистре IA32_MISC_ENABLE MSR. Также отметим, что примененные в данном исследовании процедуры копирования блока используют только один программный поток, в случае мультипроцессорной обработки возможно будут получены иные результаты. Отдельно следует рассмотреть операции с блоками, размер которых существенно превышает размеры кэш-памяти всех уровней, выполняемые с применением некэшируемой записи (Non-Temporal Write). Поэтому, предстоит еще ряд экспериментов.

Резюме

Развитие x86 — самой популярной процессорной архитектуры — неразрывно связано с требованиями рынка, удовлетворение которых часто приводит к отходу от классических канонов проектирования. Поэтому в конкурентной борьбе инженерные шедевры зачастую проигрывают разработкам, «заточенным под несовершенный мир». И программный цикл копирования блока иногда оказывается быстрее аппаратного. Все в этом мире связано, c'est la vie.

Теги: