<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Блоги &#187; Alexey Kryukov (Intel)</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/author/alexey-kryukov/feed/" rel="self" type="application/rss+xml" />
	<link>http://software.intel.com/ru-ru/blogs</link>
	<description></description>
	<lastBuildDate>Thu, 24 May 2012 12:16:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>Несколько слов о диспетчеризации</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/12/29/2006663/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/12/29/2006663/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 10:42:59 +0000</pubDate>
		<dc:creator>Alexey Kryukov (Intel)</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/12/29/2006663/</guid>
		<description><![CDATA[В этом посте я хотел бы поговорить об оптимизации кода сразу под несколько семейств процессоров. Данная задача актуальна, если мы заранее не знаем, на каком "железе" код будет исполняться. А это довольно типовой случай. В предыдущем посте я немного рассказал об одном из возможных решений - динамической генерации кода. Это реализовано в Intel Array Building [...]]]></description>
			<content:encoded><![CDATA[<p>В этом посте я хотел бы поговорить об оптимизации кода сразу под несколько семейств процессоров. Данная задача актуальна, если мы заранее не знаем, на каком "железе" код будет исполняться. А это довольно типовой случай.</p>
<p>В предыдущем посте я немного <a href="http://software.intel.com/ru-ru/blogs/2011/09/22/array-building-blocks-2/">рассказал</a> об одном из возможных решений - динамической генерации кода. Это реализовано в <a href="http://software.intel.com/en-us/articles/intel-array-building-blocks/">Intel Array Building Blocks</a>. У этого подхода свои плюсы и минусы. К плюсам следует отнести универсальность и гибкость, к минусам внутреннюю сложность и значительные накладные расходы (ведь сначала код надо сгенерировать).</p>
<p>Вопрос: какие еще существуют решения для оптимизации кода под различные платформы? Попытаемся разобраться в способе, который называется диспетчеризацией (Dispatching).</p>
<p>Для начала уточним задачу: код должен быть оптимизирован не под все железо, а только под определенный набор. Это нормально, ведь продукты поддерживают ограниченное число процессоров. Преемлемым решением для остальных процессоров выглядит запуск неоптимизированного кода на них. Кроме того, не всегда имеет смысл оптимизировать код под каждый процессор. Многие могут быть объединены в классы эквивалентности. Например, много процессоров поддерживают одинаковый набор SIMD инструкций.</p>
<p>Исходя из того, что простое лучше, чем сложное, можно сделать несколько вариантов кода, каждый из которых функционально идентичен остальным, но оптимизирован под свой набор процессоров. Очевиден и основной недостаток: на каждом CPU необходимо выбрать, какой процесс запустить или какую библиотеку вызвать. Этот минус можно обойти, если использовать дополнительный модуль. Он вызывается в run-time, автоматически определяет тип процессора и предлагает выбрать ветку кода, которая подойдет под текущее "железо" наилучшим образом. Этот модуль называется диспетчером (Dispatcher).</p>
<p>Процесс диспетчеризации представляет собой определение типа и возможностей CPU во время исполнения приложения. Вызов диспетчера обычно происходит в первую очередь еще до начала использования функциональности. На основании данных от диспетчера происходит выбор ветки кода, соответствующего текущему процессору.</p>
<p>Существуют различные реализации диспетчеров. По <a href="http://software.intel.com/en-us/articles/intel-integrated-performance-primitives-intel-ipp-understanding-cpu-optimized-code-used-in-intel-ipp/">одной</a> из них (в библиотеке <a href="http://www.intel.com/software/products/ipp/">Intel IPP</a>) диспетчер идентифицирует CPU один раз - во время инициализации библиотеки, после чего задает набор внутренних переменных. В соответствии с этим набором библиотечные вызовы из приложения перенаправляются в соответствующие внутренние функции библиотеки. Например, при запуске приложения на Core 2 Duo в 32-битной операционной системе вызов функции <em>ippsCopy_8u()</em> перенаправляется на <em>p8_ippsCopy_8u()</em> - одну из множества реализаций функциональности <em>ippsCopy_8u()</em>, имеющихся в библиотеке.</p>
<p>К недостаткам такой системы следует отнести громоздкость. Размер библиотеки увеличивается вместе с ростом поддерживаемого железа. Рост размеров библиотеки означает и рост объемов приложений, которые ее используют. Справедливости ради стоит сказать, что есть способы борьбы и против этого эффекта.</p>
<p>Преимущества данного подхода следующие. Простота и модульность: если необходимо что-то исправить в одной ветке, нет необходимости менять остальные. Низкие накладные расходы (по сравнению с подходом, где осуществляется динамическая генерация кода), которые определяются временем работы диспетчера. </p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/12/29/2006663/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Array Building Blocks: два блока компиляции</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/09/22/array-building-blocks-2/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/09/22/array-building-blocks-2/#comments</comments>
		<pubDate>Thu, 22 Sep 2011 12:14:37 +0000</pubDate>
		<dc:creator>Alexey Kryukov (Intel)</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[ArBB]]></category>
		<category><![CDATA[compiler]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/09/22/array-building-blocks-2/</guid>
		<description><![CDATA[Здесь я постараюсь дать краткий обзор ArBB runtime и пояснить почему говорят о двухфазной компиляции в ArBB]]></description>
			<content:encoded><![CDATA[<p>Во втором посте по Array Building Blocks (первый мой пост <a href="http://software.intel.com/ru-ru/blogs/2011/09/05/array-building-blocks">здесь</a>) я планировал рассказать о блюде под названием ArBB runtime и разобрать пару примеров кода: более простой, но медленный и более сложный, но быстрый. Но думается, что этот пирог лучше есть по частям. Остановимся пока на описании runtime и выясним, что означает двухфазная компиляция в ArBB.</p>
<p>Сразу надо признать, что ArBB runtime в значительной степени - это черный ящик, о его внутренней структуре мы знаем немного. Оно и понятно: повара не любят раскрывать рецепты своих блюд. Начнем с общеизвестной информации, не связанной непосредственно с ArBB. Когда приложение скомпилировано, динамически слинковано, находится на исполнении и встречается вызов библиотечной функции, начинает исполняться код самой библиотеки. Таким образом, если в приложении встречается вызов arbb::call(), ArBB берет на себя контроль над вычислениями, а по завершении контроль возвращается к самому приложению.</p>
<p>Дальше идет специфика. &laquo;Фишка&raquo; ArBB наличии собственной среды исполнения, которая осуществляет динамическую компиляцию с оптимизацией под текущую платформу во время исполнения. Таким образом, первая фаза компиляции - это собственно компиляция С++ кода в исполняемый файл, а вторая - во время работы приложения. Для этого в составе ArBB runtime есть JIT  (Just In Time) компилятор, генерирующий бинарный код для исполнения конкретной операции с учетом количества ядер, поддерживаемых процессором SIMD инструкций (SSE, AVX) и т.д. Для чего все это? Проблема в том, что если весь бинарный код будет готов еще до исполнения, то он не может быть оптимален сразу для разного железа. Будет неправдой сказать, что это не пытались обойти без динамической генерации кода. Например, можно <a href="http://software.intel.com/en-us/articles/intel-integrated-performance-primitives-intel-ipp-understanding-cpu-optimized-code-used-in-intel-ipp/">положить</a> в библиотеку несколько заранее подготовленных вариантов бинарного кода для одной и той же операции. Когда идет обращение к библиотеке, специальный модуль (Dispatcher) принимает решение, какой вариант лучше всего подойдет для текущей машины. У каждого из подходов есть свои недостатки, обсуждение которых я оставлю за рамками этого поста. Но нужно сказать, что подход с JIT-ом представляется более тяжеловесным с точки зрения накладных расходов, но более легким с точки зрения объема библиотеки и более гибким с точки переносимости и количества поддерживаемого железа.</p>
<p>Теперь нюанс. Дело в том, что JIT генерирует код не всегда. Согласно <a href="http://software.intel.com/en-us/articles/two-phase-compilation/">опубликованной информации</a>, в зависимости от уровня оптимизации ArBB предлагает два варианта. В неоптимизированном случае просто исполняется код, &laquo;зашитый&raquo; в библиотеку изначально. Случай с оптимизацией более интересен. Здесь JIT компилятор генерирует код требуемой операции на лету, и этот код будет оптимизирован под платфому, на которой идет исполнение. После этого сгенерированный код исполняется. Кроме того, ArBB runtime записывает этот код, чтобы не генерировать его снова, если этот же вызов будет выполнен в приложении еще раз.</p>
<p>Здесь становятся видны сходство и разница между JIT из ArBB и JIT из .NET. И тот и другой получают на вход некую конструкцию (ArBB вызов или IMSL), генерируют бинарный код, и записывают его на случай, если в приложении потребуется выполнить его снова. ArBB JIT генерирует оптимизированный код, чтобы приложение быстро работало там, где оно реально выполняется. Сами приложения, использующие ArBB (насколько я понимаю) - это обычный бинарный код с библиотечными вызовами. .NET же предлагает дополнительный уровень абстракции на уровне приложения (IMSL), в часности, предлагая независимость от языка программирования.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/09/22/array-building-blocks-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Строим приложение из Array Building Blocks</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/09/05/array-building-blocks/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/09/05/array-building-blocks/#comments</comments>
		<pubDate>Mon, 05 Sep 2011 14:28:52 +0000</pubDate>
		<dc:creator>Alexey Kryukov (Intel)</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/09/05/array-building-blocks/</guid>
		<description><![CDATA[В этом выпуске: присматриваемся к новому продукту Intel® Array Building Blocks (Intel® ArBB), пишем ArBB приложение для подсчета суммы чисел в массиве, а также пытаемся оценить преимущества данного подхода и вызванную им головную боль.]]></description>
			<content:encoded><![CDATA[<p>В этом выпуске: присматриваемся к новому продукту Intel® Array Building Blocks (Intel® ArBB), пишем ArBB приложение для подсчета суммы чисел в массиве, а также пытаемся оценить преимущества данного подхода и вызванную им головную боль.</p>
<p>Есть много решений, упрощающих разработку параллельных приложений. Один из первых стандартов в области параллельных технологий – Cilk, разработка которого началась в 1994 году в лаборатории Информатики <a href="http://web.mit.edu/">M.I.T</a>. Кстати, c 2009 года группа разоработчиков Cilk <a href="http://software.intel.com/ru-ru/blogs/2009/12/11/2002726/">стала частью</a> Intel. В 1997 году появилась первая версия другого решения – <a href="http://openmp.org/wp/openmp-specifications/">OpenMP</a>. Сейчас это одна из наиболее распространенных технологий, ключевыми элементами которой являются конструкции для управления потоками и данными, библиотечные функции и переменные окружения. В 2006 году Intel выпустила Intel® Threading Building Blocks (Intel® TBB), где параллелизм реализован на уровне задач, а в распоряжение разработчиков предоставлены планировщик и потокобезопасные контейнеры. Продукт получился весьма успешным и используется как основа для других решений, в частности, ArBB, на котором остановимся подробнее.  </p>
<p><strong>Основная идея – прячем параллельный код по контейнерам.</strong></p>
<p><a href="http://www.intel.com/go/arbb">Intel® ArBB</a> представляет собой расширение C++ новыми классами и функциями, а также собственный runtime. Продукт появился в результате слияния своего предшественника Intel’s Ct Technology c технологией <a href="http://software.intel.com/en-us/articles/rapidmind/">Rapidmind</a>. Cуть ArBB в том, что программист пишет код, похожий на обычный, непараллельный, но использует типы данных, контейнеры и функции из ArBB (например, i32 вместо int, dense вместо vector и т.д.) и указывает, выполение каких функций должен взять на себя ArBB runtime. По умолчанию runtime сам определяет, сколько потоков создать, что каждый из них будет делать, как синхронизовать данные и т.д. Посмотрим как это выглядит на деле. Для этого разберем небольшой пример кода, в котором используется ArBB.</p>
<p><strong>Hello ArBB! Как это выглядит на практике?</strong></p>
<p>Одна из простых типовых задач – подсчет суммы элементов в массиве целых чисел. Код с использованием ArBB выглядит следующим образом.</p>
<pre name="code" class="cpp">
#include &lt;iostream&gt;
#include &lt;arbb.hpp&gt;
// включение основного include файла ArBB

template &lt;typename T&gt;
void f(arbb::dense&lt;T&gt; arr, T&amp; 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&lt;arr_size;i++)  // инициализация массива
        arr[i]=i;
    arbb::dense&lt;arbb::i32&gt; arbb_arr;
    // декларируем контейнер ArBB
    arbb::bind(arbb_arr, arr, arr_size);
    // связываем исходный массив и контейнер ArBB
    arbb::i32 result;
    // объявляем переменную для записи результата
    arbb::call(f&lt;arbb::i32&gt;)(arbb_arr, result);
    // вызов ArBB runtime для выполнения функции f

    std::cout &lt;&lt; arbb::value(result);
    // Доступ к ArBB скалярам осуществляется через arbb::value

    return 0;
}
</pre>
<p>В примере реализована функция f, принимающая два аргумента. Первый – исходный контейнер dense, что в терминах ArBB является аналогом vector. Cумму его элементов мы и будем считать. Второй аргумент нужен, чтобы вернуть результат.</p>
<p>Одна из &laquo;фишек&raquo; ArBB в том, что в продукте реализованы сотни полезных функций для всевозможных типовых операций над разными наборами данных. Это сильно облегчает жизнь: во-первых, не надо заботиться о реализации того или иного алгоритма, а во-вторых не надо городить много вложенных циклов с зависящими друг от друга параметрами, в результате код становится более читаемым.  Одна из таких встроенных в ArBB функций – add_reduce, возвращает сумму элементов в контейнере.</p>
<p>В main() создается и инициализируется массив, объявляется ArBB контейнер, и происходит связывание массива и контейнера. Зачем все это? В использовании ArBB есть нюанс: ArBB может производить вычисления только в своем пространстве и только с ArBB типами (нельзя передать C++ массив напрямую в f). Теперь же массив и ArBB контейнер содержат одни и те же данные. Далее объявляем ArBB переменную для записи результата и вызываем функцию f. arbb::call(имя)(аргументы) – это и есть вызов ArBB runtime с указанием выполнить функцию, исполнение происходит в ArBB пространстве. Осталось вывести результат. Единственное, что надо не забыть – это прописать зависимость от библиотеки ArBB в свойствах проекта или напрямую в командной строке при компиляции, например</p>
<p><code>icl /D WIN32 /D NDEBUG /EHsc application.cpp /link arbb.lib</code></p>
<p><strong>Что имеем в результате: минусы</strong></p>
<ul>
<li>Написание и разбор ArBB кода может показаться утомительным, особенно с непривычки. Разработку осложняют требования использовать специальные типы данных. Необходимо организовать отдельное пространство для ArBB вычислений, а также связывать контейнеры из C++ и ArBB.</li>
<li>Собственный runtime приносит дополнительные накладные расходы.</li>
</ul>
<p><strong>Что имеем в результате: плюсы</strong></p>
<ul>
<li>Гарантированное отсутствие data races &amp; deadlocks.</li>
<li>Высокоуровневый API: сотни операций с контейнерами, в том числе и многомерными, делаются в одну строчку посредством встроенных функций, не загромождая код и мозг ненужными деталями.</li>
<li>Собственный runtime обеспечивает отличную переносимость приложений. Не надо ничего изменять, чтобы приложение оптимально работало и на multi-, и на manycore системах, будь то 4-х ядерный десктоп, 16-core двухсокетный сервер или 32-ядерная MIC карта. Также нет необходимости оптимизировать приложение под конкретный набор SIMD инструкций. Это большое преимущество как с точки зрения цены поддержки приложения, так и в случае если вы не знаете заранее, на каком железе оно будет запускаться.</li>
</ul>
<p>Лучшее место для начала знакомства с ArBB, пожалуй, <a href="http://www.intel.com/go/arbb">здесь</a>. Продукт сейчас в Beta стадии.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/09/05/array-building-blocks/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

