Повышение производительности памяти с помощью потоковой загрузки Intel® Streaming SIMD Extensions 4 (Intel® SSE4)

Создать новую статью

20.07.2009 13:00


Введение
Команда потоковой загрузки
Модели программирования потоковых загрузок
    Реализация модели «Массовая загрузка и использование»
Производительность потоковой загрузки
    Производительность потоковой загрузки при одновременном использовании буферов загрузки
Заключение
A. Код для измерения производительности потоковой загрузки

Аннтотация

Intel® SSE4 – это новый набор команд SIMD (Single Instruction Multiple Data), реализованный в семействе процессоров Intel® Core™2 на базе 45-нанометровой технологии следующего поколения (Penryn), который позволяет улучшить производительность и функции энергосбережения для ряда приложений.

Intel SSE4 включает команду MOVNTDQA для управления «потоковой загрузкой» с устройств, отображаемых в памяти USWC (Uncacheable Write Combining) процессора. Потоковые загрузки улучшают функции считывания с устройств ввода/вывода, отображенных на USWC, обеспечивая увеличение скорости в 7,5 раза по сравнению с обычными загрузками (как, например, MOVDQA).

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

Введение

Отображаемые в памяти устройства ввода/вывода, включая графические и видеоустройства, обычно отображаются в качестве памяти USWC (Uncacheable Write Combining). Память UC, т.е. некэшируемая память (uncacheable memory – данные не сохраняются в модули кэш-памяти процессора), обычно используется для отображаемых в памяти устройств ввода/вывода. Память USWC – это расширение памяти UC, которое содержит некэшируемые данные, обычно сохраняющиеся с помощью последовательных операций записи, например, в буферной памяти кадров. Функция Write combining позволяет объединять все записи в строке кэш-памяти до их окончательного переноса в память [1].

Streaming SIMD Extensions 2 (SSE2) содержат команду MOVNTDQ для «потоковых записей», которая увеличивает производительность потоковых операций некэшируемых записей в устройства, отображаемые в память USWC. Команда потоковой записи используется в качестве явного способа сообщить процессору, что данные должны быть записаны непосредственно во внешнюю память, а не в кэш-память процессора любого уровня. Потоковые записи позволяют объединять сохраняемую информацию в одной строке кэш-памяти (обычно 64-разрядной) в отличие от метода записи данных в 16-разрядные блоки. Это решение позволяет намного повысить производительность этих устройств при записи.

Хотя память USWC в первую очередь используется для операций записи (как, например, буферная память кадров), ее также можно использовать для операций загрузки. Современные сложные графические устройства могут эффективно выполнять рендеринг, пока процессор занят другими операциями. После завершения рендеринга процессор загрузит данные для последующей обработки и передачи для окончательного отображения. Однако команды загрузки SSE функционируют на базе максимум 16-разрядных блоков и при доступе к памяти USWC обладают ограниченной производительностью: необходимо применение двух отдельных транзакций системной шины с общей производительностью четырех синхронизаторов шины.

В наборе команд Intel® Streaming SIMD Extensions 4 (Intel® SSE4) для «потоковых загрузок» используется команда MOVNTDQA. Эта команда повышает производительность операций чтения, поддерживая потоковые некэшируемые операции чтения из памяти USWC. По отношению к потоковым записям команда потоковых загрузок позволяет перемещать данные полными строками кэша, значительно повышая эффективность транзакций шины, а также скорость передачи данных. По сравнению со стандартными 16-разрядными загрузками (как MOVDQA) команда потоковых загрузок MOVNTDQA расширяет возможности чтения с устройств ввода/вывода, отображаемых в памяти USWC, что повышает скорость операций более чем в 7,5 раз.

Команда потоковой загрузки

Команда потоковой загрузки (MOVNTDQA) – это единственная команда, которая позволяет считывать 16-разрядный блок данных в объединенную область строк кэша (потоковая строка кэша) памяти USWC. Данная команда также позволяет считывать не только 16-разрядный блок данных, но и полную 64-разрядную строку кэша. Эта 64-разрядная строка хранится не в одной из модулей кэш-памяти процессора, а в небольшом наборе временных «буферов потоковой загрузки». Извлекая данные из полной 64-разрядной строки кэша и временно сохраняя содержимое этой строки в буфере потоковой загрузки, повторные загрузки по тому же адресу строки можно производить непосредственно из буфера потоковой загрузки. Таким образом, скорость и производительность повторных загрузок будут значительно повышены, как показано в листинге 1.

Листинг 1. Загрузка полных строк кэша с помощью команды MOVNTDQA

; This load retrieves a full cache line that is stored in a temporary streaming load
; buffer
; USWC_Memory is a pointer to the system allocated memory of type USWC

      MOVNTDQA xmm0, USWC_Memory+0

; Subsequent 16-byte loads from the same cache line are supplied from the streaming
; load buffer and occur much faster 

      MOVNTDQA xmm1, USWC_Memory+16
      MOVNTDQA xmm2, USWC_Memory+32
      MOVNTDQA xmm3, USWC_Memory+48

При передаче потока по нескольким адресам строки необходимо сгруппировать загрузку всех четырех блоков, что позволит повысить производительность операций. Загрузка каждого блока (через дискретные команды MOVNTDQA) не обязательно должна происходить в порядке, показанном на примере выше. Тем не менее, потоковая загрузка определенного блока приведет к новому распределению буферов потоковой загрузки, если такое распределение не было сделано до этого. Ограничение количества буферов потоковой загрузки для реализации на уровне микроархитектуры и их объединение позволяет повысить общую эффективность работы.

Далее представлены дополнительные рекомендации по использованию команды потоковой загрузки:

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

Команда потоковой загрузки предназначена для повышения скорости передачи данных из памяти типа USWC. Для других видов памяти, включая кэшируемую (WB) и некэшируемую (UC) память, данная команда выступает в качестве обычной 16-разрядной команды загрузки MOVDQA. Однако в следующих версиях процессоров команда потоковой загрузки может использоваться и для других типов памяти (как, например, WB) в качестве напоминания, что указанная строка кэша должна передаваться по потоку непосредственно в ядро, уменьшая тем самым занимаемые объемы кэш-памяти.

Модели программирования потоковых загрузок

Существует две стандартные модели программирования для использования потоковых загрузок в приложении. Основное отличие этих моделей – в том, какие операции производятся над данными при их извлечении через потоковые загрузки. Ниже представлено описание обеих моделей с примерами и рекомендациями по реализации:

  1. Массовая загрузка и использование. В этой модели приложение загружает данные через потоковые загрузки, а потом копирует информацию во временный, кэшируемый буфер WB (т.е., происходит загрузка массива данных). После завершения загрузки и копирования данных процессор использует временный буфер и в итоге отсылает данные обратно в память.
  2. Постепенная загрузка и использование. В данной модели приложение загружает одну строку кэша с помощью потоковой загрузки, производит операции над данными и записывает их обратно в память. В этом случае работа с данными происходит по мере их загрузки в отличие от первой модели, которая предусматривает исходную загрузку большого объема данных (модель «Массовая загрузка и использование»).
    Данная модель программирования может использоваться, если между процессором и устройством ввода/вывода, отображаемом в память, поддерживаются отношения производитель-потребитель. К примеру, для графических устройств, отображаемых в память USWC, процессор может использовать потоковые операции записи, чтобы передавать данные на рендеринг в графический процессор, использовать операции потоковой загрузки, чтобы получить данные из графического процессора после рендеринга, и, в заключение, использовать потоковые операции записи, чтобы передать обработанную информацию обратно в графический процессор для отображения.

Более предпочтительной является модель программирования «Массовая загрузка и использование», поскольку она обеспечивает более значительное улучшение производительности. В модели «Постепенная загрузка и использование» операции над данными, которые выполняются между потоками загрузки, могут повлиять на производительность потоковой загрузки (в связи с конкуренцией между буферами потоковой загрузки и другими ресурсами процессора). В модели «Массовая загрузка и использование» эта возможность снижается, поскольку загрузка данных и их использование производятся в разных пакетах.

Реализация модели «Массовая загрузка и использование»

В листинге 2 приведен пример исполнения модели программирования «Массовая загрузка и использование». В данном разделе описан сам процесс исполнения и представлены дополнительные рекомендации по использованию этой модели в приложениях.

Листинг 2. Пример исполнения модели программирования «Массовая загрузка и использование»

   ; Initialize pointers to start of the USWC memory
   mov esi, Pointer_to_USWC_memory
   mov edx, Pointer_to_USWC_memory
                 
   ; Initialize pointer to end of the USWC memory
   add edx, Memory_Length
                 
   ; Initialize pointer to start of the cacheable WB buffer
   mov edi, Pointer_to_WB_memory
                 
     ; Start of Bulk Load loop
     inner_start:
        ; Load data from USWC Memory using Streaming Load
        MOVNTDQA xmm0, xmmword ptr [esi]
        MOVNTDQA xmm1, xmmword ptr [esi+16]
        MOVNTDQA xmm2, xmmword ptr [esi+32]
        MOVNTDQA xmm3, xmmword ptr [esi+48]
                        
        ; Copy data to buffer
        MOVDQA xmmword ptr [edi], xmm0
        MOVDQA xmmword ptr [edi+16], xmm1
        MOVDQA xmmword ptr [edi+32], xmm2
        MOVDQA xmmword ptr [edi+48], xmm3
                        
        ; Increment pointers by cache line size and test for end of loop
        add esi, 040h
        add edi, 040h
        cmp si, edx
        jne inner_start
                       
     ; End of Bulk Load loop
                 
  ; Bulk load completed. Now operate on data in buffer . . . 

Инициализация до массовой загрузки

В строках 1-9 инициализируются указатели, которые будут использоваться для массовой загрузки. ESI и EDX обозначают начало и конец загружаемого участка памяти USWC. Команда Memory_Length означает объем копируемой памяти, которая должна состоять из блоков по 64 байта, чтобы обеспечить максимальную производительность.

EDI обозначает кэшируемый буфер WB, куда будут копироваться данные из памяти USWC. Важно, чтобы копирование этого буфера производилось в кэш-память процессора первого уровня. При использовании кэш-памяти более высокого уровня возможно возникновение конкуренции между буферами потоковой загрузки и другими ресурсами процессора, что может отрицательно сказаться на производительности потоковой загрузки.

Чтобы сохранить буфер WB в кэш-памяти первого уровня, необходимо выполнить следующие действия:

  1. Выделить выровненный буфер WB с помощью команды aligned malloc. Размер буфера не должен превышать 4КБ и должен состоять максимум из 64 байтов:
    void * _aligned_malloc(size_t size, size_t alignment)
  2. С помощью команды memset очистите память; в результате буфер окажется в кэш-памяти первого уровня:
    void * memset (void* ptr, int value, size_t num)

Массовая загрузка и копирование

В строках 10-29 происходит загрузка данных через потоковую загрузку и копирование информации в кэшируемый буфер WB. В строках 14-17 представлены команды MOVNTDQA, которые отвечают за выполнение потоковых загрузок. Первая команда MOVNTDQA выделяет буфер потоковой загрузки и загружает полную строку 64-разрядного кэша в буфер. Следующие 3 команды MOVNTDQA забирают данные непосредственно из буфера потоковой загрузки. Поскольку каждая команда MOVNTDQA загружает 16 байтов, для загрузки одной строки кэша необходимо сделать 4 вызова.

В этом примере происходит загрузка только одной строки кэша при каждом повторении цикла. Однако при наличии дополнительных реестров (например, xmm4 – xmm7, xmm8 – xmm15 технология Intel® Extended Memory 64) во время повторения цикла могут быть загружены несколько строк кэша.

В этом примере потоковые загрузки производятся последовательно с помощью смещения памяти (т.е., +0, +16, +32, +48), но данный порядок загрузок не обязателен. Вместе с тем, все потоковые загрузки из одной строки кэша должны быть сгруппированы вместе и необходимо избегать повторных загрузок одного и того же блока информации. Это позволит обеспечить оптимальное использование буферов потоковых загрузок.

В строках 20-23 данные копируются в кэшируемый буфер WB с помощью команды MOVDQА. Происходит копирование одной строки кэша за одно повторение цикла и копирование считается законченным после того, как будет загружена полная строка кэша. Загрузка и копирование данных должны производиться отдельно и не чередоваться. Не рекомендуется чередовать (другое название – пошаговая операция) загрузку и копирование (т.е., загрузка и копирование блока 1, загрузка и копирование блока 2), поскольку возможная конкуренция между буферами потоковой загрузки и другими ресурсами процессора может отрицательно сказаться на производительности операций потоковой загрузки.

В строках 26-29 управление повторением цикла происходит с помощью увеличения соответствующих указателей и проверки полной загрузки всего сегмента памяти USWC.

Производительность потоковой загрузки

Чтобы измерить производительность потоковых загрузок из локальной системной памяти, было использовано четыре исполнения модели программирования «Массовая загрузка и использование», которые загрузили 4КБ данных из памяти USWC и скопировали информацию в кэшируемый буфер WB. Одно исполнение не использует потоковые загрузки (т.е. загрузки в память выполняются с помощью команды MOVDQA). Другое исполнение использует потоковые загрузки (загрузки в память выполняются с помощью команды MOVNTDQA). В качестве двух других исполнений используются двухпотоковые модули, которые разделяют сегмент USWC на две части и выполняют операции загрузки и копирования в два отдельных потока (каждый поток относится к отдельному ядру). Список исходных кодов представлен в Приложении А.

В каждом исполнении программы цикл загрузки и копирования данных объемом 4КБ повторялся бесчисленное количество раз (примерно 10 тысяч) и, количество времени, которое потребовалось на эти повторения, позволило измерить среднюю производительность памяти [2].

На Рис. 1 показана производительность памяти по отношению к каждому исполнению. Использование потоковых загрузок в однопотоковых модулях программы повысило производительность памяти более чем в 5 раз. В двухпотоковом исполнении потоковые загрузки позволили увеличить производительность памяти более чем в 7,5 раз.

Рис. 1. Производительность памяти при потоковой загрузке из системной памяти [3]
ПРИМЕЧАНИЕ. Str Ld = исполнение использует потоковую загрузку

Кроме того, на Рис. 1 показана теоретическая пиковая производительность, которой система может достичь (1067FSB * 8 МБайт/сек = 8,53 ГБ/сек). Двухпотоковое исполнение позволяет достичь производительности, всего лишь немного не дотягивающей до теоретического пикового значения, а однопотоковое исполнение поддерживает около половины указанной производительности. Причина такой ситуации – ограниченное количество потоковых буферов загрузки, которые доступны для каждого ядра. При использовании сразу двух ядер двухпотоковое исполнение команды получает доступ к большему количеству буферов загрузки, что увеличивает производительность почти вдвое.

Производительность потоковой загрузки при одновременном использовании буферов загрузки

В разделе Реализация модели «Массовая загрузка и использование» содержатся несколько советов по предотвращению состязания за буферы потоковой загрузки и другие ресурсы, что может отрицательно сказаться на производительность потоковой загрузки (например, хранение кэшируемого буфера WB в кэш-памяти первого уровня, отказ от пошаговых загрузок и копирования). Чтобы показать изменения в производительности потоковой загрузки при состязании за обладание буферами потоковой загрузки и другими ресурсами процессора, однопотоковое исполнение, использованное в предыдущем разделе, было изменено таким образом, что кэшируемый буфер WB сохраняется в кэш-памяти второго уровня [4]

На Рис. 2 сравнивается исходное однопотоковое исполнение модуля программы с измененной версией. При введение состязания за обладание буферами потоковой загрузки и другими ресурсами процессора производительность памяти упала более чем на 30%.

Рис. 2. Производительность при передаче данных через системную память с помощью потоковой загрузки при состязании за буферы потоковой загрузки и другие ресурсы процессора.

Заключение

Intel SSE4 содержат команду MOVNTDQA, которая регулирует «потоковые загрузки» из устройств, отображенных в памяти USWC процессора. Совместное использование потоковых загрузок и потоковых записей значительно повышает производительность данных через устройства ввода/вывода, отображенные в память.

Устройства ввода/вывода, отображенные в память, могут использовать потоковые загрузки только при отображении в память USWC. Применение модели программирования «Массовая загрузка и использование» и выполнение потоковых загрузок по нескольким каналам позволит обеспечить пиковую производительность процессов. В примере в данном документе показано, что потоковые загрузки позволяют повысить скорость операций чтения с локальной системной памяти в 7,5 раз, что практически соответствует теоретической максимальной скорости шины!

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

A. Код для измерения производительности потоковой загрузки

__declspec(noinline) void BulkLoadAndOperate (void) {

   _asm {

   ; Get pointer to start of the USWC memory
   mov esi, Pointer_to_USWC_memory
   mov edx, Pointer_to_USWC_memory

   ; Get pointer to end of the USWC memory
   add edx, Memory_Length

   ; Get pointer to start of the cacheable WB buffer
   mov edi, Pointer_to_WB_memory

   ; Get the Loop Iteration Count
   mov ebx, Loop_Iteration_Count

   ; Take Start Timestamp
   rdtsc
   mov DWORD PTR [StartClockticks], eax
   mov DWORD PTR [StartClockticks+4], edx

  ; Start of outer Iteration Loop
  align 16
  iter_loop:

  mov esi, Pointer_to_USWC_memory
  mov edi, Pointer_to_WB_memory
; Start of Copy loop
align 16
copy_loop:
; Stream a cache line from USWC Memory
MOVNTDQA xmm0, xmmword ptr [esi]
MOVNTDQA xmm1, xmmword ptr [esi+16]
MOVNTDQA xmm2, xmmword ptr [esi+32]
MOVNTDQA xmm3, xmmword ptr [esi+48]

; Copy/Store the streamed cache-line to a Cacheable ; WB buffer
MOVDQA xmmword ptr [edi], xmm0
MOVDQA xmmword ptr [edi+16], xmm1
MOVDQA xmmword ptr [edi+32], xmm2
MOVDQA xmmword ptr [edi+48], xmm3

; Increment by cache line size
; Test for end of copy loop
add esi, 040h
add edi, 040h
cmp esi, edx
jne copy_loop
 ; Test for end for loop iteration
 add ecx, 1
 cmp ecx, ebx
 jne iter_loop
 ; Take End Timestamp
 rdtsc
 mov DWORD PTR [EndClockticks], eax
 mov DWORD PTR [EndClockticks+4], edx
   }
   return;

}

Авторы

Ашиш Джха (Ashish Jha) является старшим инженером по производительности архитектур в подразделении Software and Solutions Group. В течение 10 лет он работал в данной отрасли промышленности, 6 из которых Джха посвятил работе с оптимизацией производительности Java Virtual Machine со встроенными новейшими функциями, разработанными корпорацией Intel. В настоящее время объект его работы – анализ архитектуры процессоров и оптимизация производительности используемых и разрабатываемых поколений процессоров. Ашиш обладает степенью бакалавра в области электроники и техники связи Технологического Университета Бирла в г. Месра, Индия.

Даррен Йи (Darren Yee) является старшим инженером по техническому маркетингу в подразделении Software and Solutions Group. В течение своей 9-летней работы в Intel Даррен занимал различные должности в сфере конструкторского и технического маркетинга, работал с рядом различных технологий, включая Java, системы электронной коммерции, сетевых решений, а также технологию Intel® Viiv™. Даррен владеет патентом в области решений электронной коммерции и имеет степенью бакалавра в области вычислительной науки и техники Корнелльского Университета.



[1] Поскольку память USWC не кэшируется, программное обеспечение не может предоставить непротиворечивые данные по другим процессорам или абонентам шины, полагаясь на механизмы когерентности кэша. In order to ensure consistency of USWC memory accesses between producers and consumers, software should employ proper serialization mechanisms such as the memory fence instruction (i.e. MFENCE). {} Чтобы обеспечить согласованность доступов к памяти USWC между производителями и потребителями, в программном обеспечении должны использоваться соответствующие механизмы присвоения серийного номера, как, например, команда ограждения памяти (MFENCE).

[2] В частности, производительность памяти = (тактовая частота процессора * количество повторений * объем скопированных данных за одно повторение) / общее время исполнения (в тактовых циклах). Для двухпотоковых исполнений производительность подсчитывалась по каждому потоку, а затем полученные значения складывались.

[3] Тестовая система состояла из двухъядерного процессора Intel® для настольных ПК на базе 45-нанометровой технологии (Wolfdale), системной платы Intel D975XBX2KR, памяти объемом 2 ГБ DDR2 RAM PC2-5300 (667 МГц), Windows* XP Professional с пакетом сервисных программ Service Pack 2.

[4] Это производилось с помощью чтения данных объемом 64 КБ в начале каждого повторения цикла потоковой загрузки, в результате чего буфер WB отправлялся в кэш-память второго уровня.