Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Практика БЖД / Пример ПЗ.docx
Скачиваний:
42
Добавлен:
11.05.2015
Размер:
82.44 Кб
Скачать
      1. Оптимизация медленных команд

Иногда горячая точка оказывается просто в медленном фрагменте кода, или даже в единственной команде. Однако часто причиной потери производительности является не сам факт большой латентности той или иной команды, а нерациональное применение команд. Некоторые команды слишком универсальны, и для конкретной цели возможно применить более оптимальную по быстродействию команду или последовательность команд. Рассмотрим пример, когда нам необходимо сравнить длину строки с заданной константой. Один из вариантов реализации такой задачи:

if (strlen(string) == constant)

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

if (string[constant] == 0).

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

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

C = 123;

Num = c & ~15;

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

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

      1. Оптимизация работы с памятью

Оперативная память является основным хранилищем данных для большинства программ. От того, насколько быстро из памяти будут считаны для данные для дальнейшей обработки, а также записаны результаты вычислений зависит быстродействие программы в целом. Быстродействие современной динамической памяти существенно возросло, однако взаимодействие с ней все же проигрывает по скорости работе со статической памятью или регистрами процессора. Так, например, в 2008 году время доступа к динамической памяти составляло примерно 50-70 наносекунд при цене 20-75 долларов за гигабайт объема. Для статической памяти же скорость доступа на порядок выше и составляет 0,5-2,5 наносекунды при цене 2000-5000 долларов за гигабайт [7].

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

Кэш представляет собой дополнительный уровень иерархии памяти между процессором и оперативной памятью [7]. Он применяется для сокращения латентности оперативной памяти. В современных процессорах существует 3 уровня кэша. Кэш первого уровня меньше всех по объему, но самый быстрый. Кэш второго уровня больше первого, но и медленнее. И, наконец, самый медленный и одновременно самый большой – это кэш третьего уровня.

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

Кэш работает по принципу пространственной и временной локальности [5]. Пространственная локальностьпроявляется тогда, когда совместно используются области памяти, находящиеся друг с другом. Обычно программа обращается к смежным областям памяти: например, если произошло обращение к байту с номеромx, то скорее всего далее программа обратится к байту с номеромx+1и так далее. Поэтому при промахе кэша, в него заносится не один байт, требуемый из основной памяти в текущем обращении, а линейка байтов, равная размеру строки кэша [5]. Такой способ работы с кэшем является оптимальным, так как однократная передача данных для линейки кэша происходит быстрее, чем побайтовая передача.

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

Обычно предварительная выборка данных в кэш производится аппаратно, однако на уровне команд процессора есть возможность запросить выборку некоторой области программно. Данная команда называется prefetchh[8]. У нее существует четыре подвида, позволяющих выбирать область данных в кэш различного уровня или же непосредственного в буфер прямого доступа к памяти.

В случае записи данных существует возможность так же миновать шаг записи в кэш. Это обеспечивается командами процессора movntdq,movntdqaи другими [8]. При этом запись происходит в специальные буферыWC-памяти, или памятью собъединением записи(Writecombining,WC) [5]. Этот тип памяти используется в случаях, когда очередность и срочность записи не важны, а важна производительность. Однако, при использовании команд некэшируемой записи, следует убедиться, что записываемые данные не будут вскоре использоваться, так как они удаляются из кэша.

Механизм кэширования накладывает и еще одну особенность на расположение данных в памяти – адреса доступа к данным должны быть выровнены [5]. Если некая части переменной находятся на двух разных строках кэша, то необходимо будет считать обе и объединить части переменной воедино.

Кроме того, выравнивания адресов памяти требуют и SIMD-команды, в противном случае возникнет необработанное исключение.

Соседние файлы в папке Практика БЖД