Аппаратное ускорение корпоративных вычислений

«Ускоренные вычисления» (Accelerated Computing) — модель вычислений, при которой в тандеме с традиционными CPU применяются узкоспециализированные сопроцессоры («ускорители»). Основной задачей сопроцессоров является высокопараллельное выполнение интенсивной вычислительной нагрузки и высвобождение ресурсов CPU для других нужд приложения («offloading»).

Хорошими примерами таких «ускорителей» могут служить GPU от NVIDIA или сопроцессоры Xeon Phi, без которых не обходится практически ни один проект в сфере научных или инженерных вычислений. Однако в корпоративном секторе подобные технологии практически не применялись (если не считать использование GPU в фермах виртуализации рабочих мест).

Именно поэтому выход серверов на чипе Oracle SPARC M7, содержащего помимо ядер общего назначения специализированные сопроцессоры Data Analytics Accelerators (DAX), можно считать отправной точкой в проникновении «ускоренных вычислений» на корпоративный рынок.

Основной задачей DAX является ускорение in-memory вычислений за счёт разгрузки основных ядер путём выполнения операций поиска по содержимому оперативной памяти на сопроцессорах.

В случае необходимости переноса операции поиска на DAX ядро общего назначения формирует запрос и передаёт его на выполнение «ускорителям», после чего продолжает выполнение основного кода. При этом происходит автоматическое распараллеливание задачи по всем акселераторам чипа, а затем сбор результатов (похоже на MapReduce) в кэше чипа и уведомление ядра о завершении операции. Сопроцессоры подключены к L3-кэшу чипа, что позволяет обеспечить быстрое взаимодействие с ядрами общего назначения и передачу результатов поиска:

41480e465e4e4d309f7971c528ddd207.png

Стоит отметить, что для обеспечения возможности поиска по данным с помощью DAX они должны располагаться в памяти в специальном формате (In-Memory Column Store). Характерным свойством этого формата является возможность хранения данных в сжатом виде (алгоритм сжатия — проприетарный Oracle Zip), что позволяет разместить в оперативной памяти больший объём информации и положительно влияет на скорость обработки данных акселераторами за счёт экономии пропускной способности шины, связывающей чип и оперативную память. При поиске декомпрессия выполняется аппаратно, средствами DAX, и не влияет на производительность. Другой особенностью является наличие индексов, содержащих минимальные и максимальные значения для каждого из множества сегментов памяти (In-Memory Compression Units — IMCUs), составляющих In-Memory Column Store. Получается, что «ускорение» выборки имеет свою цену — долгое первичное размещение данных в памяти, во время которого происходит их сжатие и предварительный анализ (своего рода индексирование).

Основным потребителем данной технологии на данный момент является СУБД Oracle Database 12c, использующая DAX для ускорения операций поиска по таблицам, расположенным в In-Memory Column Store. СУБД автоматически переносит часть операций на DAX, что приводит к значительному ускорению некоторых запросов.

Однако нам в «Инфосистемы Джет» было интересно изучить технологию DAX без промежуточного «чёрного ящика» в виде СУБД Oracle Database, скрывающего интересные подробности и создающего дополнительные накладные расходы, не позволяющие точно оценить преимущества, создаваемые использованием сопроцессоров.

Использование сопроцессоров DAX из сторонних приложений


В начале марта 2016 года Oracle открыла API доступа к DAX для независимых разработчиков (Open DAX API). Теперь DAX можно использовать не только в СУБД Oracle Database, но и в любых других приложениях.

Oracle пригласила всех желающих в свое облако протестировать DAX не только из СУБД, но и с использованием SDK для различных языков программирования (C, Python и Java). Поскольку низкоуровневый API, предназначенный для взаимодействия непосредственно с аппаратной частью сопроцессора, достаточно сложен, для ознакомления с новой технологией помимо самого SDK было предложено использовать дополнительную библиотеку, предоставляющую высокоуровневые средства для работы с данными (libvector), расположенными в оперативной памяти. Именно на её основе и был сделан ряд тестов для проверки работы DAX.

Компоненты SDK

536babe5fe0e4971b58e2309a950c1ae.png

Сценарий тестирования


В качестве тест-кейса рассматривалась простая аналитическая задача — поиск значений в расположенном в памяти целочисленном массиве, удовлетворяющих заданному условию. В виде SQL-запроса эту задачу можно было бы записать так:
SELECT value FROM values WHERE value BETWEEN value_low AND value_high;

Задачу планировалось решать двумя способами — классическим перебором всех элементов и с помощью сопроцессоров DAX.

Реализация


На языке C решение этой задачи выглядело приблизительно следующим образом:
#define RANDOM_SEED 42
int *values, *results;
int low = VALUE_LOW, high = VALUE_HIGH;

values = generate_random_values_array(NUM_VALUES, RANDOM_SEED);
results = malloc(NUM_VALUES * sizeof(int));

for (i=0; i= low && values[i] <= high) {
		results[n] = values[i];
		n++;
	}
}

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

Для DAX поиск и получение результатов разделены на две операции:

#include  /* DAX */

#define RANDOM_SEED 42

int low = VALUE_LOW, high = VALUE_HIGH;
vector valuesVec, bitVec, resultsVec;

valuesVec = generate_random_values_vector(NUM_VALUES, RANDOM_SEED);

/* Поиск */
bitVec = vector_in_range(valuesVec, &low, &high);

/* Подсчёт количества значений, удовлетворяюших условию */
n = bit_vector_count(bitVec);

/* Извлечение значений, удовлетворяюших условию */
resultsVec = vector_extract(valuesVec, bitVec);

В случае с DAX операция поиска значений (функция vector_in_range), удовлетворяющих условию, возвращает битовый вектор (bit vector), на основе которого еще одним запросом (vector_extract) формируется новый вектор с результатами. Искомые записи будут извлечены из своих IMCU и записаны в новые IMCU, с которыми снова можно работать через DAX.

Такой подход позволяет эффективно работать с наборами данных типа ключ/значение, когда требуется найти ключи, значения которых удовлетворяют условию. В этом случае в памяти формируются два массива данных — вектор ключей и вектор значений:

vector keysVec, valuesVec;
int low = VALUE_LOW, high = VALUE_HIGH;

populateKeyValueVectors(&keysVec, &valueVec);

Выполняется поиск по вектору значений с помощью DAX, результатом которого является битовая карта:
bitVec = vector_in_range(valuesVec, &low, &high);

Для извлечения искомых элементов полученная битовая карта применяется с помощью DAX к вектору ключей:
resultsVec = vector_extract(keysVec, bitVec);

К тому же над множеством битовых векторов можно проводить операции типа AND и OR, то есть перекладывать на DAX объединение результатов нескольких сравнений, как, например, в запросе:
SELECT part FROM parts WHERE mass > 100 AND volume < 30;

Наши эксперименты с объединением через AND двух битовых векторов показали преимущество вызова, выполненного на DAX:
bit_vector_and2(bitVec1, bitVec2);

Перед поэлементным (с элементами типа long) объединением битовых карт на процессоре вида:
for (i=0; i

в 3–6 раз по скорости выполнения в зависимости от количества элементов.

Но вернемся к программе. Элементами нашего массива будут случайные целые числа, а поиск будет выполняться по диапазону от –109 до 109 (то есть примерно половина чисел будет удовлетворять условию).

Мы запустили оба варианта реализации нашего теста несколько раз на количествах чисел в массиве от 1 миллиона до 500 миллионов и измерили время выполнения поиска и время копирования результатов в новый массив, с которым можно снова работать. Для классического перебора не имеет смысла разделять эти две операции, т.к. копировать в новый массив придется либо адрес элемента (8 байт), либо сам элемент (4 байта).

Результаты


Итак, ниже представлен график зависимости времени поиска и получения данных от количества элементов массива:
2825025400f74f1fa016de016293e850.png

Использование DAX показало 2-кратное превосходство над простым перебором. Если сравнивать только поиск (без сохранения найденных значений, т.е. при выполнении операции вида «SELECT COUNT (*)» или в целях получения битовой карты), то скорость поиска через DAX более чем в 5 раз выше.

Следить за использованием сопроцессоров в системе можно с помощью утилиты busstat, собирающей метрики производительности с различных компонентов процессора (busstat -w dax 30 1). Во время выполнения наших тестов мы наблюдали распараллеливание запросов на 8 из 32 сопроцессоров DAX (в каждом процессоре M7 их по восемь). При использовании нескольких пользовательских процессов параллельно загрузка будет видна на всех 32 сопроцессорах.

Безусловно, можно реализовать все алгоритмы DAX программно (что и было реализовано в Oracle Database In-Memory Option до появления DAX), сделать дополнительные оптимизации и получить ещё более впечатляющие результаты, чем с DAX (особенно если вручную распараллелить задачу на все процессорные нити SPARC M7). Но назначение DAX в том, чтобы переложить работу ядер процессора на специализированные сопроцессоры. Т.е. в целом важен не сам прирост производительности, а именно возможность разгрузки основного CPU.

Прочие интересные моменты


В числе примеров кода для DAX инженеры Oracle реализовали его поддержку в приложении для Apache Spark. По заверениям производителя, при использовании DAX производительность выросла в 6 раз. Суть оптимизации заключалась во множестве операций с битовыми картами через DAX, что получилось гораздо быстрее, чем на процессоре.

Выводы


Перенос исполнения программной логики с процессоров на специализированные устройства в очередной раз доказал свою целесообразность. Особенно в такой «горячей» в настоящий момент области как In-Memory Computing.

Возможность использовать DAX через открытый API может привлечь в мир SPARC новые программные продукты.

Однако подобные функции могут быть реализованы в будущем и на платформе Intel на уже существующих аппаратных решениях — с использованием сопроцессора Xeon Phi. Как минимум исследования в этой области уже ведутся:

  1. Rethinking SIMD Vectorization for In-Memory Databases.
  2. Design of an In-Memory Database Engine Using Intel Xeon Phi Coprocessors.

Post Scriptum


Тестовые программы собирались с помощью компилятора Solaris Studio 12.4. Использовался максимальный уровень оптимизации (-xO5), с помощью которого удавалось значительно ускорить «классические» вычисления. Исходные коды доступны на github.

SPARC M7 и DAX — официальный релиз Oracle.


Статья подготовлена Дмитрием Глушенком, системным архитектором Центра проектирования вычислительных комплексов компании «Инфосистемы Джет». Мы будем рады вашим конструктивным комментариям.

Комментарии (0)

© Habrahabr.ru