<?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; kos66</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/author/kos66/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>Краткое руководство по оптимизации приложения с помощью компиляторов Intel двенадцатой версии</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/11/22/intel-8/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/11/22/intel-8/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 07:10:20 +0000</pubDate>
		<dc:creator>kos66</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[compiler]]></category>
		<category><![CDATA[intel compiler]]></category>
		<category><![CDATA[Intel Parallel Studio]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/11/22/intel-8/</guid>
		<description><![CDATA[Пошаговое руководство по повышению производительности приложения при помощи оптимизирующих флагов компилятора]]></description>
			<content:encoded><![CDATA[<h1>Пошаговое руководство по повышению производительности приложения</h1>
<p>Прежде чем начать оптимизировать производительность приложения, делать так называемый «тюнинг», необходимо проверить, вообще правильно ли работает приложение без флагов оптимизации. Для этого нужно его скомпилировать с флагом <strong>/Od (-O0)</strong>. В данной версии компилятора при любом флаге уровня оптимизации подразумевается возможность использования инструкций SSE2. Чтобы запустить приложение на более старых IA-32 процессорах, например Intel® Pentium® III, необходимо также добавить флаг <strong>/arch:IA32</strong> (Windows) или <strong>–mia32</strong> (Linux).</p>
<ol>
<li>Попробуйте скомпилировать приложение с различными флагами уровня оптимизации (Windows <strong>/O1</strong>, <strong>/O2</strong> или <strong>/O3</strong>; Linux и Mac OS X <strong>-O1</strong>, <strong>-O2</strong>, или <strong>-O3</strong>), и измерьте скорость выполнения в каждом случае. Как правило, можно начать с флага  <strong>/O2</strong> (<strong>–O2</strong>). Затем, попробуйте <strong>/O3</strong> (<strong>-O3</strong>), если ваше приложение содержит много циклов. Эти флаги должны повысить производительность в том числе и не для процессоров Intel®, однако, для процессоров Intel® оптимизация будет интенсивнее.</li>
<li>Попробуйте использовать флаги для конкретной линейки процессоров. Например, <strong>/QxSSE4.2</strong> (<strong>–xsse4.2</strong>) для процессоров семейства Intel® Core™, в частности, для Intel Core i7, и <strong>/arch:SSE3</strong> (<strong>-msse3</strong>) для совместимых не-Intel процессоров, поддерживающих, как минимум, набор инструкций SSE3. Другой вариант — использовать флаг <strong>/QxHOST</strong> (<strong>-xhost</strong>), который попытается применить оптимизации для конкретного процессора, на котором вы компилируете приложение. Опять же, на процессорах Intel данные оптимизации играют более существенную роль, чем на совместимых не-Intel процессорах.</li>
<li>Включите межпроцедурную оптимизацию (Interprocedural Optimization, IPO) и оптимизацию по профилю (Profile-guided optimization, PGO). Затем измерьте производительность приложения с каждым из флагов по отдельности, и с обоими флагами.</li>
<ul>
<li>С ростом объёма программ разработчики стали делать свой код всё более удобочитаемым и повторно используемым. Зачастую это приводит к тому, что процедуры становятся предельно общими, в то время как в конкретной программе можно обойтись и частным случаем. Задача межпроцедурной оптимизации — именно генерация таких частных случаев. Включается флагом <strong>/Qipo</strong> (<strong>-ipo</strong>).</li>
<li>PGO позволяет более эффективно использовать микроархитектуру процессора, проводить распределение инструкций, использовать кэш-память и делать более точное предсказание переходов. Она увеличивает производительность программы путем реорганизации кода для повышения эффективности работы кэша команд, снижения размера кода и уменьшения числа ситуаций с непредсказуемыми передачами управления. За данную оптимизацию отвечают флаги <strong>/Qprof-gen</strong> и <strong>/Qprof-use</strong> (<strong>-prof-gen</strong> и <strong>-prof-use</strong>).</li>
</ul>
<li>Оптимизируйте приложение для векторного и параллельного выполнения на многоядерных или многопроцессорных системах. Для этого можно воспользоваться советами новой функции Guided Auto-Parallelism (GAP), <strong>/Qguide</strong> (<strong>-guide</strong>); расширениями языка для C/C++ Intel® Cilk™ Plus; опциями <strong>/Qparallel</strong> (<strong>-parallel</strong>) или <strong>/Qopenmp</strong> (<strong>-openmp</strong>); или воспользоваться библиотеками Intel® Performance Libraries.</li>
<li>Воспользуйтесь Intel® VTune™ Amplifier XE, чтобы выявить узкие места в приложении, которые, возможно, получится устранить. Используйте Intel® Inspector XE, чтобы диагностировать ошибки памяти  в многопоточных приложениях и ускорить процесс разработки. Данные продукты не могут использоваться на не-Intel процессорах.</li>
</ol>
<p>По материалам <a href="http://software.intel.com/sites/products/collateral/hpc/compilers/compiler_qrg12.pdf">Quick-Reference Guide to Optimization with Intel® Compilers version 12</a></p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/11/22/intel-8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Введение в OpenMP: параллельное программирование на C++</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/11/21/openmp-c/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/11/21/openmp-c/#comments</comments>
		<pubDate>Mon, 21 Nov 2011 17:20:16 +0000</pubDate>
		<dc:creator>kos66</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[multicore]]></category>
		<category><![CDATA[openmp]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/11/21/openmp-c/</guid>
		<description><![CDATA[Доступно и просто, на примерах и с картинками, о директивах OpenMP и планировании (scheduling) в OpenMP.]]></description>
			<content:encoded><![CDATA[<h2>Введение</h2>
<p>Не секрет, что «гонка мегагерц», которая на протяжении многих лет оставалась главным способом повышения производительности процессоров, сменилась на тенденцию увеличения числа ядер. Грубо говоря, производители процессоров научились помещать на одном чипе несколько процессоров. Сейчас практически любой компьютер оборудован многоядерным процессором. Даже настольные системы начального уровня имеют два ядра — стоит ли говорить, что на подходе четырёх- и восьмиядерные системы. Если Закон Мура не утратит своей силы, в течение 5 лет средний компьютер будет иметь 16, или даже 32 ядра на чипе.</p>
<p>Проблема заключается в том, что программная индустрия пока не успевает за аппаратной, и только часть приложений способна эффективно использовать ресурсы многоядерных процессоров. Каждая программа имеет один главный поток выполнения — набор инструкций, которые выполняются последовательно одна за другой. Естественно, в этом случае задействовано одно ядро процессора. Программист должен позаботиться о загрузке остальных ядер работой, иными словами, он должен сделать так, чтобы некоторые инструкции выполнялись не последовательно, а одновременно — в параллельном режиме.</p>
<p>Стоит отметить, что производительность не возрастает линейно с увеличением количества ядер. То есть задействование четырёх ядер не гарантирует увеличение производительности в четыре раза. Тем не менее, прирост есть, и с каждым годом он будет сильнее — появится больше оптимизированных под многоядерные процессоры программ.</p>
<p>Каким образом программисты могут управлять потоками, чтобы задействовать всю мощь процессора? Как сделать так, чтобы приложение работало максимально быстро, масштабировалось с увеличением числа ядер, и чтобы написать такое приложение не было ночным кошмаром программиста? Один из вариантов — вручную создавать потоки в программном коде, отдавать им задачи на выполнение, затем удалять. Но в данном случае нужно позаботиться об одной очень важной вещи — синхронизации. Если одной задаче необходимы данные, которые обсчитываются другой задачей, ситуация усложняется. Трудно понять, что происходит, когда разные потоки в одно и то же время пытаются поменять значения общих переменных. Да и не хочется вручную создавать потоки и делегировать им задания. На помощь приходят различные библиотеки и стандарты для параллельного программирования. Рассмотрим подробнее наиболее распространённый стандарт для распараллеливания программ на языках C, C++, Fortran — OpenMP.</p>
<h2>OpenMP</h2>
<p><img class="alignright" src="http://openmp.org/wp/openmp_336x120.gif" alt="OpenMP" /></p>
<p><a href="http://openmp.org">OpenMP </a>— API, предназначенное для программирования многопоточных приложений на многопроцессорных системах с общей памятью. Разработку спецификации OpenMP ведут несколько крупных производителей вычислительной техники и программного обеспечения. OpenMP поддерживается <a href="http://openmp.org/wp/openmp-compilers/">основными компиляторами</a>.</p>
<p>В OpenMP вы «не увидите» потоки в коде. Вместо этого, вы сообщаете компилятору с помощью директив #pragma, что блок кода может быть распараллелен. Зная данную информацию, компилятор в состоянии сгенерировать приложение, состоящее из одного главного потока, который создаёт множество других потоков для параллельного блока кода. Эти потоки синхронизируются в конце параллельного блока кода, и мы снова возвращаемся к одному главному потоку.</p>
<p><img class="alignleft" src="http://upload.wikimedia.org/wikipedia/en/thumb/f/f1/Fork_join.svg/450px-Fork_join.svg.png" /></p>
<h2>Использование OpenMP</h2>
<p>Так как OpenMP контролируется #pragma, то код на C++ корректно скомпилируется любым компилятором C++, потому что неподдерживаемые #pragma должны игнорироваться. Однако OpenMP API содержит также несколько функций, и, чтобы ими воспользоваться, необходимо подключить заголовочный файл. Самый легкий способ определить, поддерживает ли компилятор OpenMP — попробовать подключить файл omp.h: <em>#include &lt;omp.h&gt;</em></p>
<p>Если OpenMP поддерживается, нужно его включить с помощью специальных флагов компилятора:</p>
<pre name="code" class="plain">gcc	-fopenmp
Intel	-openmp (Linux, MacOSX), -Qopenmp (Windows)
Microsoft	-openmp (Настройки проекта в Visual Studio)</pre>
<h2>Синтаксис</h2>
<p>Директивы OpenMP начинаются с <em>#pragma omp</em>.</p>
<h4>parallel</h4>
<p>Данная директива создаёт группу из N потоков. N определяется во время выполнения, обычно это число ядер процессора, но также можно задать N вручную. Каждый из потоков в группе выполняет следующую за директивой команду (или блок команд, определённый в {}-скобках). После выполнения, потоки «сливаются» в один.</p>
<pre name="code" class="cpp">
#pragma omp parallel  {
    // Код внутри блока выполняется параллельно.
    printf("Hello!\n");
  }</pre>
<p>Пример выведет текст “Hello!” с переносом строки столько раз, сколько потоков было создано в группе. Для двухъядерных систем, текст напечатается дважды. (Замечание: может быть выведено что-нибудь типа “HeHlellolo”, потому что вывод происходит параллельно.)</p>
<p><img class="alignright" src="http://community.topcoder.com/i/education/091106_01.gif" alt="Принцип fork/join в OpenMP" /></p>
<p>Если посмотреть, как это устроено, то можно увидеть, что GCC создаёт специальную функцию и перемещает код блока в эту функцию, таким образом, все переменные внутри блока становятся локальными переменными данной функции (соответственно, локальными переменными каждого потока). С другой стороны, ICC использует механизм, напоминающий fork(), и не создаёт специальной функции. Обе реализации, конечно, правильны и семантически идентичны.</p>
<p>Параллелизм может быть условным, если использовать выражение if:</p>
<pre name="code" class="cpp">extern int parallelism_enabled;
  #pragma omp parallel for if(parallelism_enabled)
  for(int c=0; c&lt;n; ++c)
    handle(c);</pre>
<p>В данном случае, если parallelism_enabled равно нулю, цикл будет обработан только одним (главным) потоком.</p>
<h4>for<br />
<h4>
<p>Директива for разделяет цикл for между текущей группой потоков, так что каждый поток в группе обрабатывает свою часть цикла.</p>
<pre name="code" class="cpp">#pragma omp for
 for(int n=0; n&lt;10; ++n)
 {
   printf(&quot; %d&quot;, n);
 }
 printf(&quot;.\n&quot;);</pre>
<p>Данный цикл выведет числа от 0 до 9 каждое ровно один раз. Однако, порядок их вывода неизвестен. Он может быть, например, таким:</p>
<p><code>0 5 6 7 1 8 2 3 4 9.</code></p>
<p>Данный код будет преобразован компилятором в нечто подобное:</p>
<pre name="code" class="cpp">int this_thread = omp_get_thread_num(), num_threads = omp_get_num_threads();
  int my_start = (this_thread  ) * 10 / num_threads;
  int my_end   = (this_thread+1) * 10 / num_threads;
  for(int n=my_start; n&lt;my_end; ++n)
    printf(&quot; %d&quot;, n);</pre>
<p>Таким образом, каждый поток обрабатывает свою часть цикла параллельно с остальными потоками.</p>
<p>Важно, что директива #pragma omp лишь делегирует порции цикла разным потокам в текущей группе потоков. В момент запуска программы группа из единственного (главного) потока. Чтобы создать новую группу потоков, необходимо использовать ключевое слово <em>parallel</em>:</p>
<pre name="code" class="cpp">#pragma omp parallel
 {
  #pragma omp for
  for(int n=0; n&lt;10; ++n) printf(&quot; %d&quot;, n);
 }
 printf(&quot;.\n&quot;);</pre>
<p>Можно написать короче:</p>
<pre name="code" class="cpp">#pragma omp parallel for
 for(int n=0; n&lt;10; ++n) printf(&quot; %d&quot;, n);
 printf(&quot;.\n&quot;);</pre>
<p>Обе записи эквиваленты.
<p>Чтобы задать количество потоков в группе, можно воспользоваться параметром <em>num_threads</em>:</p>
<p>
<pre name="code" class="cpp">#pragma omp parallel num_threads(3)
 {
   // Данный код будет выполнен тремя потоками.
   // Части цикла будут поделены между
   // тремя потоками текущей группы потоков.
   #pragma omp for
   for(int n=0; n&lt;10; ++n) printf(&quot; %d&quot;, n);
 }</pre>
</p>
<p>В OpenMP 2.5, итерационная переменная цикла должна быть типа signed. В OpenMP 3.0. она также может иметь тип unsigned integer, может быть указателем или constant-time random access  итератором. В последнем случае, для определения количества итераций цикла будет использоваться std::distance().</p>
<h4>Краткое резюме: parallel, for и группа потоков<br />
<h4>
<p>Ещё раз отметим разницу между parallel, parallel for и for:</p>
<ul>
<li>Группа потоков – те потоки, которые выполняются в данный момент.</li>
<li>При запуске программы, группа содержит один поток.</li>
<li>Директива parallel разделяет текущий поток в новую группу потоков, пока не будет достигнут конец следующего выражения/блока выражений, затем группа потоков сливается в один поток.</li>
<li>for разделяет цикл for на части, и отдаёт каждую часть потоку из текущей группы. Данная директива не создаёт новых потоков, она всего лишь делит работ между потоками текущей группы.</li>
<li>parallel for — краткая запись двух команд: parallel и for. Parallel создаёт новую группу потоков, затем for раздает каждому потоку из этой группы часть цикла.</li>
</ul>
<p>Если программа не содержит директивы parallel, то она выполняется единственным потоком.</p>
<h4>scheduling (планирование)</h4>
<p>Программист может контролировать то, каким образом потоки будут загружаться работой при обработке цикла. Существует несколько вариантов.</p>
<pre name="code" class="cpp">#pragma omp for schedule(static)
 for(int n=0; n&lt;10; ++n) printf(&quot; %d&quot;, n);
 printf(&quot;.\n&quot;);</pre>
<p><em>static </em>является вариантом по умолчанию. Ещё до входа в цикл каждый поток «знает», какие части цикла он будет обрабатывать.</p>
<p>Второй вариант — ключевое слово <em>dynamic</em>:</p>
<pre name="code" class="cpp">#pragma omp for schedule(dynamic)
 for(int n=0; n&lt;10; ++n) printf(&quot; %d&quot;, n);
 printf(&quot;.\n&quot;);</pre>
<p>В данном случае невозможно предсказать порядок, в котором итерации цикла будут назначены потокам. Каждый поток выполняет указанное число итераций. Если это число не задано, по умолчанию оно равно 1. После того как поток завершит выполнение заданных итераций, он переходит к следующему набору итераций. Так продолжается, пока не будут пройдены все итерации. Последний набор итераций может быть меньше, чем изначально заданный. Такой вариант очень полезен, когда разные итерации цикла обсчитываются разное время.  Можно также указать количество итераций, после выполнения которых поток «попросит» у OpeMP следующие:</p>
<pre name="code" class="cpp">#pragma omp for schedule(dynamic, 3)
 for(int n=0; n&lt;10; ++n) printf(&quot; %d&quot;, n);
 printf(&quot;.\n&quot;);</pre>
<p>В данном примере, каждый поток выполняет три итерации, затем «берёт» следующие три и так далее. Последние, конечно, могут иметь размер меньше трёх.</p>
<p>Существует также вариант <em>guided</em>. Он похож на <em>dynamic</em>, но размер порции уменьшается экспоненциально. Если указать директиву #pragma omp for schedule(dynamic, 15), цикл for из 100 итераций может быть выполнен четырьмя потоками следующим образом:</p>
<pre name="code" class="cpp">Поток 0 получает право на выполнение итераций 1-15
Поток 1 получает право на выполнение итераций 16-30
Поток 2 получает право на выполнение итераций 31-45
Поток 3 получает право на выполнение итераций 46-60
Поток 2 завершает выполнение итераций
Поток 2 получает право на выполнение итераций 61-75
Поток 3 завершает выполнение итераций
Поток 3 получает право на выполнение итераций 76-90
Поток 0 завершает выполнение итераций
Поток 0 получает право на выполнение итераций 91-100</pre>
<p>А вот каким может оказаться результат выполнения того же цикла четырьмя потоками, если будет указана директива #pragma omp for schedule(guided, 15):</p>
<pre name="code" class="cpp">Поток 0 получает право на выполнение итераций 1-25
Поток 1 получает право на выполнение итераций 26-44
Поток 2 получает право на выполнение итераций 45-59
Поток 3 получает право на выполнение итераций 60-64
Поток 2 завершает выполнение итераций
Поток 2 получает право на выполнение итераций 65-79
Поток 3 завершает выполнение итераций
Поток 3 получает право на выполнение итераций 80-94
Поток 2 завершает выполнение итераций
Поток 2 получает право на выполнение итераций 95-100</pre>
<p>Динамическое и управляемое планирование хорошо подходят, если при каждой итерации выполняются разные объемы работы или если одни процессоры более производительны, чем другие. При статическом планировании нет никакого способа, позволяющего сбалансировать нагрузку на разные потоки. При динамическом и управляемом планировании нагрузка распределяется автоматически — такова сама суть этих подходов. Как правило, при управляемом планировании код выполняется быстрее, чем при динамическом, вследствие меньших издержек на планирование.</p>
<h4>ordering (упорядочивание)</h4>
<p>Порядок, в котором будут обрабатываться итерации цикла, вообще говоря, непредсказуем. Тем не менее, возможно «заставить» OpenMP выполнять выражения в цикле по порядку. Для этого существует ключевое слово <em>ordered</em>:</p>
<pre name="code" class="cpp">#pragma omp for ordered schedule(dynamic)
 for(int n=0; n&lt;100; ++n)
 {
   files[n].compress();

   #pragma omp ordered
   send(files[n]);
 }</pre>
<p>Цикл «сжимает» 100 файлов в параллельном режиме, но «посылает» их строго в последовательном порядке. Если, например, поток «сжал» седьмой файл, но шестой файл к этому моменту ещё не был «отправлен», поток будет ожидать «отправки» шестого файла. Каждый файл «сжимается» и «посылается» один раз, но «сжатие» может происходить в параллельном режиме. Разрешено использовать только один ordered блок на цикл.</p>
<h4>Дальнейшее чтение</h4>
<ul>
<li><a href="http://en.wikipedia.org/wiki/OpenMP">Wikipedia article for OpenMP</a></li>
<li><a href="http://bisqwit.iki.fi/story/howto/openmp/">Guide into OpenMP: Easy multithreading programming for C++</a></li>
<li><a href="http://community.topcoder.com/tc?module=Static&amp;d1=features&amp;d2=091106">Introduction to OpenMP</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/11/21/openmp-c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

