774 Тем для обсуждения
6,825 Открытых обсуждений
- Association for Computing Machinery TechNews (ACM)
- Go Parallel! (Dr. Dobbs)
- HPCwire (Tabor Communications, Inc.)
- insideHPC (John West)
- Joe Duffy's Weblog (Microsoft)
- Microsoft Parallel Programming Development Center (Microsoft Germany)
- MultiCoreInfo.com
- scalability.org (Scalable Informatics)
- Software Dev Blog (Intel Germany)
- Soft Talk Blog (Intel United Kingdom)
- The Moth (Microsoft)
Строим приложение из Array Building Blocks
Alexey Kryukov (Intel) (3 пост(а)) 05.09.2011 17:28
В этом выпуске: присматриваемся к новому продукту Intel® Array Building Blocks (Intel® ArBB), пишем ArBB приложение для подсчета суммы чисел в массиве, а также пытаемся оценить преимущества данного подхода и вызванную им головную боль.
Есть много решений, упрощающих разработку параллельных приложений. Один из первых стандартов в области параллельных технологий – Cilk, разработка которого началась в 1994 году в лаборатории Информатики M.I.T. Кстати, c 2009 года группа разоработчиков Cilk стала частью Intel. В 1997 году появилась первая версия другого решения – OpenMP. Сейчас это одна из наиболее распространенных технологий, ключевыми элементами которой являются конструкции для управления потоками и данными, библиотечные функции и переменные окружения. В 2006 году Intel выпустила Intel® Threading Building Blocks (Intel® TBB), где параллелизм реализован на уровне задач, а в распоряжение разработчиков предоставлены планировщик и потокобезопасные контейнеры. Продукт получился весьма успешным и используется как основа для других решений, в частности, ArBB, на котором остановимся подробнее.
Основная идея – прячем параллельный код по контейнерам.
Intel® ArBB представляет собой расширение C++ новыми классами и функциями, а также собственный runtime. Продукт появился в результате слияния своего предшественника Intel’s Ct Technology c технологией Rapidmind. Cуть ArBB в том, что программист пишет код, похожий на обычный, непараллельный, но использует типы данных, контейнеры и функции из ArBB (например, i32 вместо int, dense вместо vector и т.д.) и указывает, выполение каких функций должен взять на себя ArBB runtime. По умолчанию runtime сам определяет, сколько потоков создать, что каждый из них будет делать, как синхронизовать данные и т.д. Посмотрим как это выглядит на деле. Для этого разберем небольшой пример кода, в котором используется ArBB.
Hello ArBB! Как это выглядит на практике?
Одна из простых типовых задач – подсчет суммы элементов в массиве целых чисел. Код с использованием ArBB выглядит следующим образом.
#include <iostream>
#include <arbb.hpp>
// включение основного include файла ArBB
template <typename T>
void f(arbb::dense<T> arr, T& res)
// arr – исходный контейнер, в res – записывается результат
{
res = arbb::add_reduce(arr);
// использование встроенной ArBB функции
// для подсчета суммы элементов
}
int main()
{
int arr_size = 500; // размер массива
int* arr = (int*)arbb::aligned_malloc(sizeof(int)*arr_size);
// выделение памяти под массив
// использование arbb::aligned_malloc
// гарантирует корректную работу ArBB с массивом
for(int i = 0; i<arr_size;i++) // инициализация массива
arr[i]=i;
arbb::dense<arbb::i32> arbb_arr;
// декларируем контейнер ArBB
arbb::bind(arbb_arr, arr, arr_size);
// связываем исходный массив и контейнер ArBB
arbb::i32 result;
// объявляем переменную для записи результата
arbb::call(f<arbb::i32>)(arbb_arr, result);
// вызов ArBB runtime для выполнения функции f
std::cout << arbb::value(result);
// Доступ к ArBB скалярам осуществляется через arbb::value
return 0;
}
В примере реализована функция f, принимающая два аргумента. Первый – исходный контейнер dense, что в терминах ArBB является аналогом vector. Cумму его элементов мы и будем считать. Второй аргумент нужен, чтобы вернуть результат.
Одна из «фишек» ArBB в том, что в продукте реализованы сотни полезных функций для всевозможных типовых операций над разными наборами данных. Это сильно облегчает жизнь: во-первых, не надо заботиться о реализации того или иного алгоритма, а во-вторых не надо городить много вложенных циклов с зависящими друг от друга параметрами, в результате код становится более читаемым. Одна из таких встроенных в ArBB функций – add_reduce, возвращает сумму элементов в контейнере.
В main() создается и инициализируется массив, объявляется ArBB контейнер, и происходит связывание массива и контейнера. Зачем все это? В использовании ArBB есть нюанс: ArBB может производить вычисления только в своем пространстве и только с ArBB типами (нельзя передать C++ массив напрямую в f). Теперь же массив и ArBB контейнер содержат одни и те же данные. Далее объявляем ArBB переменную для записи результата и вызываем функцию f. arbb::call(имя)(аргументы) – это и есть вызов ArBB runtime с указанием выполнить функцию, исполнение происходит в ArBB пространстве. Осталось вывести результат. Единственное, что надо не забыть – это прописать зависимость от библиотеки ArBB в свойствах проекта или напрямую в командной строке при компиляции, например
icl /D WIN32 /D NDEBUG /EHsc application.cpp /link arbb.lib
Что имеем в результате: минусы
- Написание и разбор ArBB кода может показаться утомительным, особенно с непривычки. Разработку осложняют требования использовать специальные типы данных. Необходимо организовать отдельное пространство для ArBB вычислений, а также связывать контейнеры из C++ и ArBB.
- Собственный runtime приносит дополнительные накладные расходы.
Что имеем в результате: плюсы
- Гарантированное отсутствие data races & deadlocks.
- Высокоуровневый API: сотни операций с контейнерами, в том числе и многомерными, делаются в одну строчку посредством встроенных функций, не загромождая код и мозг ненужными деталями.
- Собственный runtime обеспечивает отличную переносимость приложений. Не надо ничего изменять, чтобы приложение оптимально работало и на multi-, и на manycore системах, будь то 4-х ядерный десктоп, 16-core двухсокетный сервер или 32-ядерная MIC карта. Также нет необходимости оптимизировать приложение под конкретный набор SIMD инструкций. Это большое преимущество как с точки зрения цены поддержки приложения, так и в случае если вы не знаете заранее, на каком железе оно будет запускаться.
Лучшее место для начала знакомства с ArBB, пожалуй, здесь. Продукт сейчас в Beta стадии.
Категории: Intel Software Network, Параллельное программирование
Пожалуйста, обратитесь к странице Уведомление об оптимизации для более подробной информации относительно производительности и оптимизации в программных продуктах компании Intel.
Комментарии (10)
| 05.09.2011 07:48
Dmitry Kozlov (Intel)
|
несколько неожиданно выглядят две вещи: 1. сначала строчка кода, а потом комментарий что она делает 2. а нельзя без arbb::call'а позвать arbb::add_reduce? |
| 05.09.2011 08:19
Dmitry Oganezov (Intel)
|
Хм, а разве так выглядит лучше? // а сейчас мы попробуем вызвать фунцию my_function my_function(); Хотя ты прав, комментарии - зло. |
| 05.09.2011 08:21
Alexey Kryukov (Intel)
|
1. сначала строчка кода, а потом комментарий что она делает Просто хотел, чтобы вширь скролл-бар не надо было крутить. Может переборщил малость :) 2. а нельзя без arbb::call'а позвать arbb::add_reduce? Можно. С arbb::add_reduce это прокатит. Но разработчики продукта рекомендуют использовать call. Насколько мне известно, причины следующие: нет гарантии что arbb runtime будет корректно вызываться из произвольных операций с любыми ArBB типами. И есть еще момент: если 10 arbb операций поместить последовательно в main, то arbb runtmie будет подниматься 10 раз, и столько же раз будем возвращаться в C++ space. А если все делать из-под arbb::call, то будет только по одному переходу. Соответственно, ниже накладные расходы. |
| 06.09.2011 00:12
ilnarb
|
не очень понял для чего городить такую конструкцию, неужели нельзя массив сразу на нужное число памяти инициализировать, как вектор?
int arr_size = 500; // размер массива
int* arr = (int*)arbb::aligned_malloc(sizeof(int)*arr_size); 18. // выделение памяти под массив
// использование arbb::aligned_malloc 20. // гарантирует корректную работу ArBB с массивом
for(int i = 0; i<arr_size;i++) // инициализация массива 22. arr[i]=i;
arbb::dense<arbb::i32> arbb_arr; 24. // декларируем контейнер ArBB
arbb::bind(arbb_arr, arr, arr_size); 26. // связываем исходный массив и контейнер ArBB
|
| 06.09.2011 00:16
ilnarb
|
честно говоря, ограничения выглядят лишними: - инициализация массива длинным путем - а есть ли там lambda выражения? TBB выглядит намного проще, не говоря о OpenMP, там все массивные вычисления проще записать. Одно радует, оптимизацией базовых операций на SIMD, AVX, .. займутся разработчики ArBB |
| 06.09.2011 03:44
Alexey Kryukov (Intel)
|
неужели нельзя массив сразу на нужное число памяти инициализировать, как вектор? Можно :) Но работать это будет гораздо медленнее. Почему? Пока еще не сам разобрался. Постараюсь осветить это в следующем посте, который, наверняка будет об arbb runtime |
| 06.09.2011 05:59
ilnarb
|
Alexey Kryukov (Intel) он в каком виде распространяется? исходники есть? совсем ломает заполнять большущую форму для beta survey |
| 06.09.2011 06:15
Alexey Kryukov (Intel)
|
ilnarb Сейчас у arbb Бета программа. Я так понимаю, для участия в ней survey надо будет все таки заполнять. arbb не open source, для установки нужен серийник или лицензия. Кстати. Судя по информации на страничке Composer XE http://software.intel.com/en-us/articles/intel-composer-xe/ arbb через некоторое время arbb будет поставляться вместе с ним. |
| 22.09.2011 05:15
Alexey Kryukov (Intel)
|
Есть продолжение http://software.intel.com/ru-ru/blogs/2011/09/22/array-building-blocks-2/ |




Dmitry Oganezov (Intel)
25,608
Кстати меня несколько удивил тот факт, что Cilk-то, оказывается, старше OpenMP! Не поленился, схолил в википедию - и действительно, старше! Кто бы мог подумать...
С нетерпением ждем продолжения - особенности реализации ArBB runtime, в частности, рассказ о загадочной "двухфазной" компиляции ;) - имхо именно в этом главная фишка!