<?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; gabriel_fallen</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/author/gabriel_fallen/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/11/25/2005952/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/11/25/2005952/#comments</comments>
		<pubDate>Fri, 25 Nov 2011 11:11:07 +0000</pubDate>
		<dc:creator>gabriel_fallen</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[TBB]]></category>
		<category><![CDATA[Threading Building Blocks]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/11/25/2005952/</guid>
		<description><![CDATA[<p>На всякий случай напомню задачу конкурса Acceler8: необходимо найти прямоугольную подматрицу с максимальной суммой в матрице целых чисел (как положительных, так и отрицательных). При этом алгоритм заполнения матрицы зафиксирован условиями конкурса, и наложена ещё пара ограничений. Но мы сейчас не о решении, а только об одном маленьком цикле - поэтому не станем углубляться.</p>

<p>Читатель наверняка уже догадался, что в решении присутствует множество циклов, и важной задачей является их распараллеливание и оптимизация. Самые важные циклы содержат непростые вычисления и сложные зависимости - их распараллеливание бросает вызов творческим и математическим способностям программиста. Но даже в такой задаче есть место разминке мозгов перед решительной битвой за такты с превосходящими силами противника. :)</p>]]></description>
			<content:encoded><![CDATA[<p>На момент публикации этого поста конкурс уже наверняка закончится, так что в плане неспортивного поведения и помощи соперникам моя совесть чиста. В любом случае, излагаемые сведения едва ли станут откровением для кого-либо из участников. <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>На всякий случай напомню задачу конкурса <a href="http://software.intel.com/ru-ru/articles/contest-acceler8-2011-main/">Acceler8</a>: необходимо найти прямоугольную подматрицу с максимальной суммой в матрице целых чисел (как положительных, так и отрицательных). При этом алгоритм заполнения матрицы зафиксирован условиями конкурса, и наложена ещё пара ограничений. Но мы сейчас не о решении, а только об одном маленьком цикле - поэтому не станем углубляться.</p>
<p>Читатель наверняка уже догадался, что в решении присутствует множество циклов, и важной задачей является их распараллеливание и оптимизация. Самые важные циклы содержат непростые вычисления и сложные зависимости - их распараллеливание бросает вызов творческим и математическим способностям программиста. Но даже в такой задаче есть место разминке мозгов перед решительной битвой за такты с превосходящими силами противника. <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Этот цикл используется почти всеми участниками, так как составляет часть референсного кода заполнения матрицы, предоставленного организаторами. Он осуществляет нормализацию матрицы:</p>
<pre name="code" class="cpp">
for(int i = 0; i &lt; dstSize; ++i) {
    pDst[i] -= mean;
}
</pre>
<p>Здесь <code>pDst</code> является указателем на (одномерный) массив, представляющий матрицу, <code>dstSize</code> - его длина, а <code>mean</code> - среднее значение элементов, вычисленное ранее.</p>
<p>Этот цикл воодушевляет, поскольку его масштабируемость ни чем не ограничена - при наличии <code>dstSize</code> процессоров мы бы выполнили его за один такт! Это свойство, разумеется, обусловлено тем, что вычисление для любого элемента матрицы не зависит ни от чего другого - ни от предыдущих, ни от следующих элементов - всё необходимое мы знаем уже на момент начала цикла.</p>
<p>Что ж, библиотека Intel Threading Building Blocks позволяет нам явно записать это свойство и воспользоваться всеми доступными ядрами всех доступных процессоров:</p>
<pre name="code" class="cpp">
class Normalizer {
    int *m_pDst;
    int m_mean;
public:
    Normalizer(int *pDst, int mean) : m_pDst(pDst), m_mean(mean) {}

    void operator()(const blocked_range &amp;r) const;
};

void Normalizer::operator()(const blocked_range &amp;r) const {
    const int end = r.end();
    for(int i = r.begin(); i &lt; end; ++i) {
	m_pDst[i] -= m_mean;
    }
}
</pre>
<p>И теперь заменить исходный цикл одной строкой:</p>
<pre name="code" class="cpp">
parallel_for(blocked_range(0, dstSize), Normalizer(pDst, mean));
</pre>
<p>Но участники конкурса не могут остановиться на этом - их подгоняет мысль, что победу могут вырвать прямо из рук даже считанные наносекунды! К тому же, если разминаться - то до пота, а для этого нужно вспомнить о векторных инструкциях.</p>
<p>Как известно, компиляторы от Интел содержат мощный модуль автоматической векторизации циклов, и главное - не мешать им заниматься своим делом. Но зачастую нужно всё-таки немного помочь. Запустив компиляцию с опцией <code>-vec-report3</code> узнаём, что он не смог векторизовать наш цикл - в цикле присутствуют зависимости. В самом деле, ведь <code>m_pDst[i] -= m_mean;</code> означает <code>m_pDst[i] = m_pDst[i] - m_mean;</code>, т.е. новое значение элемента зависит от значения другого элемента. Но подождите, ведь "другой элемент" - это всего лишь предыдущее значение того же самого элемента! Мы возьмём прежнее значение элемента, положим его в регистр, вычтем среднее и вернём туда где взяли - и можем сделать так для всего массива одновременно. Компилятор просто перестраховывается, так что давайте скажем ему так не делать в данном конкретном случае:</p>
<pre name="code" class="cpp">
const int end = r.end();
#pragma ivdep
for(int i = r.begin(); i &lt; end; ++i) {
    m_pDst[i] -= m_mean;
}
</pre>
<p>Но он снова не смог векторизовать этот цикл! На сей раз жалуется на слишком сложную адресацию, хотя куда уж проще - просто берём элементы массива по номеру, один за другим. Дело в том, что <code>m_pDst</code> - переменная-член класса, а значит на самом деле адресуется через неявный указатель <code>this</code>. Просто создадим локальные синонимы:</p>
<pre name="code" class="cpp">
int *const pDst = m_pDst;
const int end = r.end();
const int mean = m_mean;
#pragma ivdep
for(int i = r.begin(); i &lt; end; ++i) {
    pDst[i] -= mean;
}
</pre>
<p>Вуаля! Цикл успешно векторизован компилятором.</p>
<p>Разминку закончили. <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/11/25/2005952/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

