<?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>Ars Longa, Vita Brevis &#187; OpenMP</title>
	<atom:link href="http://blog.sjinks.pro/openmp/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.sjinks.pro</link>
	<description>Quod scripsi, scripsi</description>
	<lastBuildDate>Sat, 19 May 2012 17:55:07 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Параллельная версия генерации и проверки подписи по алгоритму DSA</title>
		<link>http://blog.sjinks.pro/security/552-parallel-dsa-signature-generation-verification-with-openmp/</link>
		<comments>http://blog.sjinks.pro/security/552-parallel-dsa-signature-generation-verification-with-openmp/#comments</comments>
		<pubDate>Sun, 03 May 2009 05:27:53 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[OpenMP]]></category>
		<category><![CDATA[Безопасность]]></category>
		<category><![CDATA[DSA]]></category>
		<category><![CDATA[GMP]]></category>
		<category><![CDATA[алгоритмы]]></category>
		<category><![CDATA[закрытый ключ]]></category>
		<category><![CDATA[криптография]]></category>
		<category><![CDATA[открытый ключ]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=552</guid>
		<description><![CDATA[Пример совместного использования GMP и OpenMP для повышения производительности DSA — алгоритм для создания и проверки электронной подписи с использованием открытого ключа, основанный на вычислительной сложности взятия логарифмов в конечных полях. Алгоритмы, использующие «большие числа» — всегда хорошие кандидаты на распараллеливание. Дело в том, что даже при современной мощности процессоров многие задачи являются довольно сложными с вычислительной точки зрения. [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/security/552-parallel-dsa-signature-generation-verification-with-openmp/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Пример совместного использования <a href="http://blog.sjinks.pro/tag/gmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  GMP">GMP</a> и <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a> для повышения производительности</em></h2>
<p><dfn><abbr title="Digital Signature Algorithm"><a href="http://blog.sjinks.pro/tag/dsa/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  DSA">DSA</a></abbr></dfn> — алгоритм для создания и проверки электронной подписи с использованием открытого ключа, основанный на вычислительной сложности взятия логарифмов в конечных полях.</p>
<p><a href="http://blog.sjinks.pro/tag/algorithms/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  алгоритмы">Алгоритмы</a>, использующие «большие числа» — всегда хорошие кандидаты на распараллеливание. Дело в том, что даже при современной мощности процессоров многие задачи являются довольно сложными с вычислительной точки зрения. Хотя криптографические <a href="http://blog.sjinks.pro/tag/algorithms/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  алгоритмы">алгоритмы</a>, как правило, очень тяжело поддаются распараллеливанию (например, когда значение, вычисленное на предыдущем шаге алгоритма, используется на текущем шаге), чисто математические задачи все же дают определённый простор для распараллеливания.</p>
<p>В данной статье рассмотрим возможность распараллеливания алгоритма DSA.<span id="more-552"></span></p>
<p>Кратко рассмотрим алгоритмы, затем перейдём к их анализу с целью нахождения блоков, которые можно распараллелить.</p>
<p>Алгоритмы работают не с исходным сообщением, а с его цифровым дайджестом, сгенерированным при помощи хэш-функции <code>H(<var>x</var>)</code>.<br />
Алгоритмы используют следующие параметры:</p>
<ul>
<li>простое число <var>q</var>, размерность которого в битах совпадает с размерностью значений, генерируемых хэш-функцией;</li>
<li>простое число <var>p</var>, такое, что (<var>p</var>-1) mod <var>q</var> = 0. Размерность данного числа задаёт криптостойкость системы и должна быть не менее 1024 бит;</li>
<li>число <var>g</var>, такое, что его мультипликативный порядок по модулю <var>p</var> равен <var>q</var> (<var>g</var> = <var>h</var><sup>(<var>p</var>-1)/<var>q</var></sup> mod <var>p</var>, 1&lt;<var>h</var>&lt;<var>p</var>-1, <var>g</var> ≠ 1);</li>
<li><a href="http://blog.sjinks.pro/tag/private-key/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  закрытый ключ">закрытый ключ</a> <var>x</var> — число в интервале (0; <var>q</var>);</li>
<li><a href="http://blog.sjinks.pro/tag/public-key/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  открытый ключ">открытый ключ</a> <var>y</var>, <var>y</var> = <var>g</var><sup><var>x</var></sup> mod <var>p</var>.</li>
</ul>
<p>Подпись (<var>r</var>, <var>s</var>) сообщения <var>H</var> генерируется следующим образом:</p>
<ol>
<li>Выбор случайного числа <var>k</var> из диапазона (0; <var>q</var>).</li>
<li>Вычисление <var>r</var> = (<var>g</var><sup><var>k</var></sup> mod <var>p</var>) mod <var>q</var>.</li>
<li>Вычисление <var>s</var> = (<var>k</var>⁻¹(H + <var>x</var><var>r</var>)) mod <var>q</var>.</li>
<li>В маловероятном случае, когда <var>r</var> или <var>s</var> равны нулю, переход к шагу 1.</li>
</ol>
<p>Проверка подписи (<var>r</var>, <var>s</var>) сообщения <var>H</var> осуществляется следующим образом:</p>
<ol>
<li>Вычисление <var>w</var> = <var>s</var>⁻¹ mod <var>q</var>.</li>
<li>Вычисление <var>u₁</var> = <var>H</var><var>w</var> mod <var>q</var>.</li>
<li>Вычисление <var>u₂</var> = <var>r</var><var>w</var> mod <var>q</var>.</li>
<li>Вычисление <var>v</var> = ((<var>g</var><sup><var>u₁</var></sup><var>y</var><sup><var>u₂</var></sup>) mod <var>p</var>) mod <var>q</var></li>
<li>Если <var>v</var> = <var>r</var>, то подпись верна.</li>
</ol>
<p><cite>Я</cite> ранее <a href="http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/">писал</a>, что <q cite="http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/">технология OpenMP наиболее эффективна, если код включает длительные циклы без зависимостей</q>. Но в реализации алгоритмов создания и проверки подписи циклов нет. К счастью, OpenMP не ограничивает нас только циклами.</p>
<p>Среди конструкций для разделения совместной работы есть директива <a href="https://computing.llnl.gov/tutorials/openMP/#SECTIONS">SECTIONS</a>, указывающая, что заключённые в неё разделы (секции) будут разделены между всеми потоками. Каждая секция выполняется только одним потоком, при этом различные секции могут быть выполнены различными потоками.</p>
<p>В этом случае для распараллеливания необходимо найти выражения, которые могут быть вычислены независимо друг от друга. Например, для алгоритма генерации подписи это будут вычисление <var>r</var> и <var>k</var>⁻¹ mod <var>q</var>, для алгоритма проверки подписи можно распараллелить два блока (которые будут выполняться последовательно):</p>
<ol>
<li>Вычисление <var>u₁</var> и <var>u₂</var>.</li>
<li>Вычисление <var>g</var><sup><var>u₁</var></sup> mod <var>p</var> и <var>y</var><sup><var>u₂</var></sup> mod <var>p</var>.</li>
</ol>
<p>А в алгоритме генерации ключей распараллелить можно только генерацию простых чисел (два простых числа будут вычислены со скоростью одного).</p>
<p>Теперь переходим к программной реализации (для простоты я использовал генератор псевдослучайных чисел GMP, который <strong>не является</strong> криптографически безопасным):</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5522">
        <div class="code c" id="p552code2">
<span class="kw4">struct</span> dsa_pubkey <span class="br0">&#123;</span><br />
&nbsp; &nbsp; mpz_t p<span class="sy0">;</span>&nbsp; &nbsp; <span class="coMULTI">/* prime */</span><br />
&nbsp; &nbsp; mpz_t q<span class="sy0">;</span>&nbsp; &nbsp; <span class="coMULTI">/* group order */</span><br />
&nbsp; &nbsp; mpz_t g<span class="sy0">;</span>&nbsp; &nbsp; <span class="coMULTI">/* group generator */</span><br />
&nbsp; &nbsp; mpz_t y<span class="sy0">;</span>&nbsp; &nbsp; <span class="coMULTI">/* g^x mod p */</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span><br />
<br />
<span class="kw4">struct</span> dsa_privkey <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">struct</span> dsa_pubkey public<span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_t x<span class="sy0">;</span>&nbsp; &nbsp; <span class="coMULTI">/* secret exponent */</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span><br />
<br />
<span class="kw4">void</span> dsa_sign<span class="br0">&#40;</span>mpz_t r<span class="sy0">,</span> mpz_t s<span class="sy0">,</span> mpz_t hash<span class="sy0">,</span> <span class="kw4">struct</span> dsa_privkey<span class="sy0">*</span> key<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; mpz_t k<span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_t kinv<span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_t tmp<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; gmp_randstate_t state<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw4">unsigned</span> <span class="kw4">int</span> nlimbs<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>k<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>kinv<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>tmp<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; gmp_randinit_default<span class="br0">&#40;</span>state<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; nlimbs <span class="sy0">=</span> mpz_size<span class="br0">&#40;</span>key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>1<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">do</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_urandomb<span class="br0">&#40;</span>k<span class="sy0">,</span> state<span class="sy0">,</span> nlimbs <span class="sy0">*</span> <span class="kw4">sizeof</span><span class="br0">&#40;</span>mp_limb_t<span class="br0">&#41;</span> <span class="sy0">*</span> 8<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span> <span class="kw1">while</span> <span class="br0">&#40;</span>mpz_cmp<span class="br0">&#40;</span>k<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span> <span class="sy0">&gt;=</span> 0<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp parallel sections</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* r = g^k mod p mod q */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_powm<span class="br0">&#40;</span>r<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">g</span><span class="sy0">,</span> k<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">p</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_mod<span class="br0">&#40;</span>r<span class="sy0">,</span> r<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* Kinv = k^-1 mod q */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_invert<span class="br0">&#40;</span>kinv<span class="sy0">,</span> k<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>0 <span class="sy0">==</span> mpz_cmp_ui<span class="br0">&#40;</span>r<span class="sy0">,</span> 0<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">continue</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* s = (Kinv * (x*r + hash)) mod q */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; mpz_mul<span class="br0">&#40;</span>tmp<span class="sy0">,</span> key<span class="sy0">-&gt;</span>x<span class="sy0">,</span> r<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; mpz_add<span class="br0">&#40;</span>tmp<span class="sy0">,</span> tmp<span class="sy0">,</span> hash<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; mpz_mul<span class="br0">&#40;</span>s<span class="sy0">,</span> kinv<span class="sy0">,</span> tmp<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; mpz_mod<span class="br0">&#40;</span>s<span class="sy0">,</span> s<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>0 <span class="sy0">==</span> mpz_cmp_ui<span class="br0">&#40;</span>s<span class="sy0">,</span> 0<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">continue</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">break</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>k<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>kinv<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>tmp<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">int</span> dsa_verify<span class="br0">&#40;</span>mpz_t r<span class="sy0">,</span> mpz_t s<span class="sy0">,</span> mpz_t hash<span class="sy0">,</span> <span class="kw4">struct</span> dsa_privkey<span class="sy0">*</span> key<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; mpz_t v<span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_t w<span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_t u1<span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_t u2<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">int</span> res<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>mpz_cmp<span class="br0">&#40;</span>r<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span> <span class="sy0">&gt;=</span> 0 <span class="sy0">||</span> mpz_cmp<span class="br0">&#40;</span>s<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span> <span class="sy0">&gt;=</span> 0<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>w<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_invert<span class="br0">&#40;</span>w<span class="sy0">,</span> s<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="coMULTI">/* w = (s^(-1)) mod q */</span><br />
<br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel sections</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* u1 = H*w mod q */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>u1<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_mul<span class="br0">&#40;</span>u1<span class="sy0">,</span> hash<span class="sy0">,</span> w<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_mod<span class="br0">&#40;</span>u1<span class="sy0">,</span> u1<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* u2 = r*w mod q */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>u2<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_mul<span class="br0">&#40;</span>u2<span class="sy0">,</span> r<span class="sy0">,</span> w<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_mod<span class="br0">&#40;</span>u2<span class="sy0">,</span> u2<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel sections</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* v = g^u1 mod p */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_init<span class="br0">&#40;</span>v<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_powm<span class="br0">&#40;</span>v<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">g</span><span class="sy0">,</span> u1<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">p</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>u1<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">/* w = y^u2 mod p */</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_powm<span class="br0">&#40;</span>w<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">y</span><span class="sy0">,</span> u2<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">p</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>u2<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; mpz_mul<span class="br0">&#40;</span>v<span class="sy0">,</span> v<span class="sy0">,</span> w<span class="br0">&#41;</span><span class="sy0">;</span> <span class="coMULTI">/* v = (g^u1 mod p)*(y^u2 mod p) */</span><br />
&nbsp; &nbsp; mpz_mod<span class="br0">&#40;</span>v<span class="sy0">,</span> v<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">p</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_mod<span class="br0">&#40;</span>v<span class="sy0">,</span> v<span class="sy0">,</span> key<span class="sy0">-&gt;</span>public.<span class="me1">q</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; res <span class="sy0">=</span> <span class="br0">&#40;</span>0 <span class="sy0">==</span> mpz_cmp<span class="br0">&#40;</span>v<span class="sy0">,</span> r<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="sy0">?</span> 1 <span class="sy0">:</span> <span class="nu0">0</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>w<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; mpz_clear<span class="br0">&#40;</span>v<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> res<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/security/552-parallel-dsa-signature-generation-verification-with-openmp/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/security/552-parallel-dsa-signature-generation-verification-with-openmp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Распараллеливать или не распараллеливать — вот в чём вопрос</title>
		<link>http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/</link>
		<comments>http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/#comments</comments>
		<pubDate>Fri, 10 Apr 2009 03:02:33 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[OpenMP]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[gcc]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=527</guid>
		<description><![CDATA[Всегда ли оправданно распараллеливание? OpenMP — мощная технология, позволяющая значительно повысить быстродействие приложения без переработки его архитектуры. Как и в случае с любой другой мощной технологией, знать, когда нужно её использовать не менее важно, чем уметь с этой технологией работать. В данной статье мы попытаемся показать, что распараллеливание — не панацея от всех бед, и неправильное его использование не [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Всегда ли оправданно распараллеливание?</em></h2>
<p><a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a> — мощная технология, позволяющая значительно повысить быстродействие приложения без переработки его архитектуры. Как и в случае с любой другой мощной технологией, знать, когда нужно её использовать не менее важно, чем уметь с этой технологией работать. В данной статье мы попытаемся показать, что распараллеливание — не панацея от всех бед, и неправильное его использование не только не улучшает <a href="http://blog.sjinks.pro/tag/performance/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  производительность">производительность</a> приложения, но и может привести к проблемам. Мы постараемся рассмотреть реализацию <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a> на низком уровне, чтобы оценить потери производительности, связанные с издержками на управление потоками и внутреннюю синхронизацию. В конце статьи будут даны некоторые практические рекомендации по использованию <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a>.<span id="more-527"></span></p>
<p>Если целевая платформа является многопроцессорной (или многоядерной, что в данном контексте одно и то же), а код программы допускает возможность организации параллельных вычислений, то <strong>правильное</strong> использование OpenMP практически всегда приведёт к росту производительности приложения.</p>
<p>Слово "правильное" выделено не случайно: распараллеливание программы имеет свою цену. Для того, чтобы использование OpenMP действительно привело к росту производительности, выигрыш в скорости, который обеспечивается параллельным выполнением кода, обязательно должен покрывать издержки на создание группы потоков и их поддержку.</p>
<p>Технология OpenMP наиболее эффективна, если код включает <em>длительные</em> циклы без зависимостей (текущая итерация цикла не зависит от результатов, полученных на предшествующих итерациях) и работает с разделяемыми массивами данных <a href="http://www.microsoft.com/Rus/Msdn/Magazine/2005/10/OpenMP.mspx">[1]</a>.</p>
<h3>Организация OpenMP в <a href="http://blog.sjinks.pro/tag/gcc/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  gcc">GCC</a>/<a href="http://blog.sjinks.pro/tag/linux/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Linux">Linux</a></h3>
<p>Активация поддержки OpenMP в компиляторе GCC включается путём использования ключа <code>-fopenmp</code> (заметим, что этот ключ должен указываться как при компиляции программы, так и при связывании). Цена использования OpenMP — зависимость от двух динамических библиотек: <code>libpthread</code> и <code>libgomp</code>. Собственно реализация OpenMP находится в библиотеке <code>libgomp</code>; для параллельной обработки используются POSIX-потоки.                           </p>
<p>При запуске программы инициализационный код libgomp разбирает <a href="http://gcc.gnu.org/onlinedocs/libgomp/Environment-Variables.html#Environment-Variables">переменные среды</a> и инициализирует внутренние структуры данных. Так как инициализация происходит единовременно при запуске приложения, влияния на дальнейшую производительность приложения она не оказывает, поэтому мы не будем её анализировать.                                                                      </p>
<p>Потоки, выполняющие параллельные участки кода, создаются при самом первом попадании в параллельный регион. Система повторно использует созданные потоки, поэтому если в приложении используется постоянное количество потоков, система будет использовать уже существующие потоки, что позволит не использовать дорогой (с точки зрения производительности) системный вызов <code>sys_clone</code>.                                                                                       </p>
<p>При желании можно создать потоки заранее (например, при <a href="http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/">задании маски соответствия процессоров</a>). В этом случае лучше сразу создавать требуемое количество потоков, ибо создание каждого дополнительного потока (например, при использовании вложенных регионов) приводит к необходимости вызова <span class="codebox"><code class="c">realloc<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> — не самой быстрой функции на свете.<br />
Создание одного потока сводится к следующим операциям:</p>
<ul>
<li>создание мьютекса, барьера и семафора, а также вызов <span class="codebox"><code class="c">malloc<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> для выделения памяти под внутренние структуры данных (функция <code>new_team()</code>). В зависимости от поддержки TLS может понадобиться вызов <span class="codebox"><code class="c">pthread_getspecific<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>;</li>
<li>создаётся барьер (синхронизационный примитив) для пула потоков (при вхождении в самый первый параллельный регион);</li>
<li>создаётся требуемое количество потоков; потоки (если система поддерживает данную операцию) могут быть привязаны к конкретному процессору;</li>
<li>путём вызова <span class="codebox"><code class="c">pthread_create<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> создаётся необходимое количество потоков;</li>
<li>каждый из созданных потоков создаёт семафор;</li>
<li>производится внутренняя синхронизация потоков, после чего, собственно, и начинаются вычисления.</li>
</ul>
<p>Отметим несколько особенностей, характерных для <code>libgomp</code>:</p>
<ul>
<li>если количество создаваемых потоков оставляется на усмотрение библиотеки времени выполнения, то, например, при наличии четырёх процессоров нет гарантии, что система создаст три потока (один — главный — уже есть, это основная программа): количество создаваемых потоков определяется как разность между доступными процессорами и средней загрузкой за последние 15 минут;</li>
<li>для того, чтобы библиотека времени выполнения осуществила привязку созданных потоков к конкретным процессорам, наличия в системе функции <span class="codebox"><code class="c">pthread_setaffinity_np<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> не является достаточным условием — по умолчанию созданные потоки будут привязаны ко всем доступным процессорам. Это очень легко проверяется:
          
<div class="codebox">
    <div class="the_code" style="" id="p5276">
        <div class="code c" id="p527code6">
<span class="co2">#define _GNU_SOURCE 1</span><br />
<span class="co2">#include &lt;unistd.h&gt;</span><br />
<span class="co2">#include &lt;stdio.h&gt;</span><br />
<span class="co2">#include &lt;sched.h&gt;</span><br />
<span class="co2">#include &lt;omp.h&gt;</span><br />
<br />
<span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">int</span> argc<span class="sy0">,</span> <span class="kw4">char</span><span class="sy0">**</span> argv<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; cpu_set_t mask<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> i<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; sched_getaffinity<span class="br0">&#40;</span>0<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">&#40;</span>mask<span class="br0">&#41;</span><span class="sy0">,</span> <span class="sy0">&amp;</span>mask<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span>CPU_SETSIZE<span class="sy0">;</span> <span class="sy0">++</span>i<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>CPU_ISSET<span class="br0">&#40;</span>i<span class="sy0">,</span> <span class="sy0">&amp;</span>mask<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">printf</span><span class="br0">&#40;</span><span class="st0">&quot;Thread %d: CPU %d<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> omp_get_thread_num<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> i<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Этот код выдаст результат, схожий с нижеприведённым:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5277">
        <div class="code text" id="p527code7">
Thread 0: CPU 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
Thread 0: CPU 1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
Thread 0: CPU 2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
Thread 0: CPU 3 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
Thread 1: CPU 0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
Thread 1: CPU 1<br />
Thread 1: CPU 2<br />
Thread 1: CPU 3<br />
Thread 3: CPU 0<br />
Thread 3: CPU 1<br />
Thread 3: CPU 2<br />
Thread 3: CPU 3<br />
Thread 2: CPU 0<br />
Thread 2: CPU 1<br />
Thread 2: CPU 2<br />
Thread 2: CPU 3
        </div>
    </div>
</div>

Для того, чтобы привязать потоки к конкретному процессору, нужно правильно задать переменную среды <a href="http://gcc.gnu.org/onlinedocs/libgomp/GOMP_005fCPU_005fAFFINITY.html">GOMP_CPU_AFFINITY</a>.</li>
</ul>
<p>В Linux вся синхронизация потоков в OpenMP основана на фьютексах (это отдельная тема для разговора) и атомарных инструкциях синхронизации процессора, что позволяет снизить накладные расходы. Тем не менее, создание и запуск потока являются относительно дорогими операциями.</p>
<p>С точки зрения ассемблера, создание параллельного региона выглядит так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p5278">
        <div class="code asm" id="p527code8">
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">xor</span> &nbsp; &nbsp; <span class="kw3">edx</span><span class="sy0">,</span> <span class="kw3">edx</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> &nbsp; &nbsp; rsi<span class="sy0">,</span> rsp<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> &nbsp; &nbsp; <span class="kw5">DWORD</span> <span class="kw4">PTR</span> <span class="br0">&#91;</span>rsp<span class="br0">&#93;</span><span class="sy0">,</span> <span class="kw3">edi</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> &nbsp; &nbsp; <span class="kw3">edi</span><span class="sy0">,</span> <span class="kw4">OFFSET</span> <span class="kw5">FLAT</span><span class="sy0">:</span>parallel<span class="sy0">.</span>omp_fn<span class="sy0">.</span>0<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">call</span> &nbsp; &nbsp;GOMP_parallel_start<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">mov</span> &nbsp; &nbsp; rdi<span class="sy0">,</span> rsp<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">call</span> &nbsp; &nbsp;parallel<span class="sy0">.</span>omp_fn<span class="sy0">.</span>0<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">call</span> &nbsp; &nbsp;GOMP_parallel_end
        </div>
    </div>
</div>

<p>Иными словами, компилятору приходится создавать дополнительную функцию (в нашем примере это <code>parallel.omp_fn.0</code>). Как следствие, тратится процессорное время на организацию вызовов процедур, сохранение/восстановление регистров и т.п. Сама "параллельная" функция (в случае статического планирования) вызывает <span class="codebox"><code class="c">omp_get_num_threads<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> и <span class="codebox"><code class="c">omp_get_thread_num<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> для определения количества итераций, которые она должна выполнить.</p>
<p>Подведём краткий итог: если распараллеливаемый цикл является "тяжёлым" с точки зрения вычислительной мощности, его стоит распараллеливать. Если итерация цикла не ресурсоёмка, но цикл выполняется большое количество раз, его, возможно, стоит распараллелить. Но если цикл является очень простым (например, сложение целочисленных массивов по модулю два), то, возможно, что распараллеливание будет не очень удачной идеей.</p>
<h3>Методика эксперимента</h3>
<p>Попытаемся определить, какое количество времени уходит на создание, поддержку и внутреннюю синхронизацию потоков. Для этого сравним по времени последовательную и параллельную реализации кода, выполняющего побитовую инверсию блока памяти (операция вида "чтение-модификация-запись").<br />
Рассмотрим, как количество и частота процессоров влияют на производительность и выясним, при каком количестве итераций имеет смысл распараллеливать цикл. Для простоты эксперимента будем считать, что все процессоры имеют одинаковую производительность; будет рассмотрено только статическое планирование.</p>
<p>Процесс поиска начинается со ста итераций и идёт с переменным шагом: <var>&Delta;</var>=100 при 100&le;<var>i</var>&lt;1000; <var>&Delta;</var>=1,000 при 1,000&le;<var>i</var>&lt;10,000; <var>&Delta;</var>=10,000 при 10,000&le;<var>i</var>&lt;100,000; <var>&Delta;</var>=100,000 при 100,000&le;<var>i</var>&lt;10,000,000.</p>
<p>Для получения более точных временных характеристик каждая итерация выполняется 20 раз. Из полученных данных отбрасываются два худших результата и вычисляется среднее арифметическое значение.</p>
<h3>Результаты эксперимента</h3>
<p>Эксперимент проводился на процессоре AMD Phenom(tm) II X4 940 Processor, позволяющем программно изменять частоту каждого из ядер, на частотах 800 MHz, 1.8 GHz, 2.3 GHz и 3 GHz. Размер блока, обрабатываемого за одну итерацию, составляет 8 байт.</p>
<p><a href='http://static.sjinks.info/wp-content/uploads/2009/04/data.csv'>Данные эксперимента (text/csv, 23,688 байт)</a><br />
<a href='http://static.sjinks.info/wp-content/uploads/2009/04/data.ods'>Данные эксперимента (application/vnd.oasis.opendocument.text, 2.5 МиБ)</a></p>
<p>Используемые обозначения:</p>
<dl>
<dt>S/f</dt>
<dd>последовательное выполнение алгоритма, частота процессора <var>f</var> ГГц;</dd>
<dt>P/n/f</dt>
<dd>параллельное выполнение алгоритма с использованием <var>n</var> потоков, частота ядер процессора <var>f</var> ГГц.</dd>
</dl>
<table cellspacing="2" cellpadding="10">
<tbody style="vertical-align: top">
<tr>
<td><a href="http://static.sjinks.info/wp-content/uploads/2009/04/800mhz.png" rel="lightbox[omp]" title="Сравнение алгоритмов при частоте процессора 800 МГц"><img src="http://static.sjinks.info/wp-content/uploads/2009/04/800mhz-300x136.png" alt="Сравнение алгоритмов при частоте процессора 800 МГц" width="300" height="136" class="alignnone size-medium wp-image-532" /></a></td>
<td><a href="http://static.sjinks.info/wp-content/uploads/2009/04/1800mhz.png" rel="lightbox[omp]" title="Сравнение алгоритмов при частоте процессора 1800 МГц"><img src="http://static.sjinks.info/wp-content/uploads/2009/04/1800mhz-300x158.png" alt="Сравнение алгоритмов при частоте процессора 1800 МГц" width="300" height="158" class="alignnone size-medium wp-image-533" /></a></td>
</tr>
<tr>
<td>Рисунок 1 — Результаты эксперимента<br/>при частоте 800 МГц</td>
<td>Рисунок 2 — Результаты эксперимента<br/>при частоте 1.8 ГГц</td>
</tr>
<tr>
<td><a href="http://static.sjinks.info/wp-content/uploads/2009/04/2300mhz.png" rel="lightbox[omp]" title="Сравнение алгоритмов при частоте процессора 2300 МГц"><img src="http://static.sjinks.info/wp-content/uploads/2009/04/2300mhz-300x136.png" alt="Сравнение алгоритмов при частоте процессора 2300 МГц" width="300" height="136" class="alignnone size-medium wp-image-534" /></a></td>
<td><a href="http://static.sjinks.info/wp-content/uploads/2009/04/3000mhz.png" rel="lightbox[omp]" title="Сравнение алгоритмов при частоте процессора 3000 МГц"><img src="http://static.sjinks.info/wp-content/uploads/2009/04/3000mhz-300x107.png" alt="Сравнение алгоритмов при частоте процессора 3000 МГц" width="300" height="107" class="alignnone size-medium wp-image-535" /></a></td>
</tr>
<tr>
<td>Рисунок 3 — Результаты эксперимента<br/>при частоте 2.3 ГГц</td>
<td>Рисунок 4 — Результаты эксперимента<br/>при частоте 3 ГГц</td>
</tr>
</tbody>
</table>
<h3>Анализ результатов</h3>
<p>Сразу отметим, что данные, полученные в ходе эксперимента, являются весьма приблизительными: очень трудно учесть влияние других процессов, обработку прерываний и прочие вещи, характерные для многозадачных систем; кроме того, использование функции <span class="codebox"><code class="c">gettimeofday<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> для измерения промежутка времени <a href="http://answers.google.com/answers/threadview/id/203397.html">ограничивает точность измерений до 1 мс</a>.</p>
<p>В ходе проведения эксперимента были получены весьма интересные (и даже парадоксальные) результаты. В частности, при понижении тактовой частоты процессора с 3 ГГц до 2.3 или 0.8 ГГц получалось, что четыре потока работают быстрее, чем три (что, в принципе, логично), но медленнее, чем два. Парадоксальный, но вполне воспроизводимый факт.</p>
<p>Отметим некоторые факты:</p>
<ul>
<li>создание четырёх потоков произошло довольно быстро — в пределах точности <span class="codebox"><code class="c">gettimeofday<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> (Википедия <a href="http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library">приводит пример</a>, что на создание 100,000 потоков уходит порядка 2 секунд на IA-32);</li>
<li>затраты на поддержание потоков нелинейны: по предварительным результатам получается, что если стоимость поддержания одного потока составляет <var>N</var> единиц, то для двух потоков стоимость будет равна примерно 2<var>N</var>, а для трёх — примерно 2.5<var>N</var> (<var>N</var> <strong>не зависит</strong> от количества итераций). Очень интересно проверить результаты на восьмипроцессорной системе;</li>
<li>при произвольно большом количестве процессоров производительность системы с памятью, разделяемой всеми процессорами, ограничена сверху пропускной способностью памяти. Особенно хорошо это видно из данных, полученных для частоты 800 МГц: максимальная производительность алгоритма достигается при использовании всего двух потоков; добавление новых потоков не улучшает ситуацию;</li>
<li>производительность приложения OpenMP очень сильно зависит от загрузки системы: в случае большого количества активных процессов, затраты на переключение между ними сводят на нет преимущества OpenMP. Отсутствие общесистемной библиотеки, координирующей все потоки OpenMP, может привести к тому, что запуск нескольких приложений OpenMP может привести к большому потреблению системных ресурсов;</li>
<li>при проектировании высокопроизводительного приложения OpenMP очень важно понимать, как работает кэш процессора. Неправильный подход к работе с данными может привести к неэффективному использованию процессорных кэшей и, как следствие, вынудить процессор тратить полезное время не на вычисления, а на относительно медленные обращения к памяти. Перед проектированием подобных приложений очень неплохо ознакомиться, например, с такими публикациями, как "<a href="http://people.redhat.com/drepper/parallelprog.pdf">Programming for tomorrow's high speed processors, today</a>" и "<a href="http://people.redhat.com/drepper/cpumemory.pdf">Understanding CPU Caches</a>" Ульриха Дреппера.</li>
</ul>
<h3>Рекомендации</h3>
<p>На основании результатов, полученных в ходе эксперимента, можно дать следующие рекомендации:</p>
<ul>
<li>количество потоков по возможности не должно превышать количество доступных процессоров, ибо один процессор (одно ядро) не в состоянии выполнить две задачи параллельно;</li>
<li>максимальное количество потоков (если не ограничено сверху количеством доступных процессоров) можно приблизительно рассчитать отношением средней скорости копирования блоков данных <strong>требуемого размера</strong> в памяти к средней скорости работы распараллеливаемого алгоритма. Например, если скорость копирования восьмибайтных блоков составляет 9.6 ГиБ/сек, то например, при скорости алгоритма в 5 ГиБ/сек не имеет смысла создавать более двух (возможно, трёх) потоков. Тем не менее, стоит помнить, что даже если скорость памяти позволяет увеличить скорость передачи данных в два раза, добавление еще одного вычислителя может не привести к реальному ускорению работы в два раза;</li>
<li>так как производительность приложения OpenMP зависит от загрузки системы, не стоит без необходимости явно указывать количество потоков: в идеале, планировщик операционной системы знает лучше;</li>
<li>примерное количество итераций, с которого распараллеливание имеет смысл, эмпирически можно оценить при помощи следующего выражения:
<p><var>L</var>/(<var>v</var>×<var>n</var>)+<var>c</var> &lt; <var>L</var>/<var>v<var>,</p>
где <var>L</var> — объём обрабатываемых данных, <var>v</var> — скорость их обработки, <var>n</var> — поправочный коэффициент (равный произведению количества вычислителей на процентный объём распараллеливаемых вычислений), <var>c</var> — время, необходимое на внутреннюю организацию потоков.<br />
В общем случае коэффициент <var>n</var> является системно-зависимым.</li>
</ul>
<p></p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/openmp/527-to-parallelize-or-not-to-parallelize/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>OpenMP на многоядерном процессоре и криптография</title>
		<link>http://blog.sjinks.pro/openmp/522-openmp-on-multicore-cpu-and-cryptography/</link>
		<comments>http://blog.sjinks.pro/openmp/522-openmp-on-multicore-cpu-and-cryptography/#comments</comments>
		<pubDate>Sat, 04 Apr 2009 01:36:56 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[OpenMP]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[ECB]]></category>
		<category><![CDATA[криптография]]></category>
		<category><![CDATA[производительность]]></category>
		<category><![CDATA[шифрование]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=522</guid>
		<description><![CDATA[Практический пример использования OpenMP — это набор директив компилятора, библиотечных процедур и переменных окружения, которые предназначены для программирования многопоточных приложений на многопроцессорных системах с разделяемой памятью. OpenMP реализует параллельные вычисления с помощью многопоточности, в которой главный поток создает набор подчиненных потоков и задача распределяется между ними. Предполагается, что потоки выполняются параллельно на машине с несколькими процессорами. Использование [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/openmp/522-openmp-on-multicore-cpu-and-cryptography/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Практический пример использования</em></h2>
<p><a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a> — это набор директив компилятора, библиотечных процедур и переменных окружения, которые предназначены для программирования многопоточных приложений на многопроцессорных системах с разделяемой памятью. <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a> реализует параллельные вычисления с помощью многопоточности, в которой главный поток создает набор подчиненных потоков и задача распределяется между ними. Предполагается, что потоки выполняются параллельно на машине с несколькими процессорами.</p>
<p>Использование OpenMP должно приводить к увеличению производительности за счет того, что программа (по крайней мере, её параллельные участки) выполняется не на одном процессоре, а на всех доступных. Процесс распределения потоков по процессорам можно <a href="http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/">контролировать</a>.</p>
<p>В соответствии с законом Амдаля–Уэра (увеличение количества вычислителей приводит к ограничению роста производительности), имея четыре процессора, мы не получим четырёхкратное увеличение производительности. К тому же затраты на синхронизацию и управление потоками сказываются на производительности не лучшим образом. Да и увеличение вычислительной мощности в N раз не приводит к аналогичному росту скорости обращения к памяти.</p>
<p>Я решил проверить, каким будет прирост производительности параллельного шифрования в <a href="http://blog.sjinks.pro/security/18-data-encryption-modes-when-strong-cipher-doesnt-help/">режиме ECB</a> у алгоритма шифрования ГОСТ 28147—89 на четырёхядерном процессоре.<span id="more-522"></span></p>
<p>Операция зашифрования по ГОСТ 28147—89 сама по себе не может быть распараллелена (да и есть ли смысл?). Но так как в режиме <a href="http://blog.sjinks.pro/tag/ecb/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  ECB">ECB</a> операция зашифрования одного блока не зависит от остальных блоков, то процесс можно распараллелить.</p>
<p>Цикл зашифрования выглядит следующим образом (для краткости я не привожу вспомогательные таблицы):</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52216">
        <div class="code c" id="p522code16">
uint_fast32_t gost_data_1<span class="br0">&#91;</span>256<span class="br0">&#93;</span><span class="sy0">;</span><br />
uint_fast32_t gost_data_2<span class="br0">&#91;</span>256<span class="br0">&#93;</span><span class="sy0">;</span><br />
uint_fast32_t gost_data_3<span class="br0">&#91;</span>256<span class="br0">&#93;</span><span class="sy0">;</span><br />
uint_fast32_t gost_data_4<span class="br0">&#91;</span>256<span class="br0">&#93;</span><span class="sy0">;</span><br />
<br />
<span class="kw4">union</span> gostrec_t <span class="br0">&#123;</span><br />
&nbsp; &nbsp; uint8_t x<span class="br0">&#91;</span>8<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; uint32_t y<span class="br0">&#91;</span>2<span class="br0">&#93;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span><br />
<br />
<span class="kw4">void</span> do_encode<span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span> buf<span class="sy0">,</span> <span class="kw4">const</span> uint32_t<span class="sy0">*</span> <span class="kw4">const</span> key<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> uint32_t<span class="sy0">*</span> k <span class="sy0">=</span> key<span class="sy0">;</span><br />
&nbsp; &nbsp; uint_fast32_t a <span class="sy0">=</span> buf<span class="sy0">-&gt;</span>y<span class="br0">&#91;</span>0<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; uint_fast32_t b <span class="sy0">=</span> buf<span class="sy0">-&gt;</span>y<span class="br0">&#91;</span>1<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; uint_fast32_t t<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>uint_fast32_t i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;=</span><span class="nu0">11</span><span class="sy0">;</span> <span class="sy0">++</span>i<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>0 <span class="sy0">==</span> <span class="br0">&#40;</span>i <span class="sy0">&amp;</span> 3<span class="br0">&#41;</span><span class="br0">&#41;</span> k <span class="sy0">=</span> key<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; t &nbsp;<span class="sy0">=</span> a <span class="sy0">+</span> k<span class="br0">&#91;</span>0<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; b <span class="sy0">^=</span> gost_data_1<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span>t<span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_2<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 8<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_3<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 16<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_4<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 24<span class="br0">&#41;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; t <span class="sy0">=</span> b <span class="sy0">+</span> k<span class="br0">&#91;</span>1<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; a <span class="sy0">^=</span> gost_data_1<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span>t<span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_2<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 8<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_3<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 16<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_4<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 24<span class="br0">&#41;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; k <span class="sy0">+=</span> <span class="nu0">2</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; k <span class="sy0">=</span> key <span class="sy0">+</span> <span class="nu0">8</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>uint_fast32_t i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;=</span><span class="nu0">3</span><span class="sy0">;</span> <span class="sy0">++</span>i<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; k <span class="sy0">-=</span> <span class="nu0">2</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; t &nbsp;<span class="sy0">=</span> a <span class="sy0">+</span> k<span class="br0">&#91;</span>1<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; b <span class="sy0">^=</span> gost_data_1<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span>t<span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_2<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 8<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_3<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 16<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_4<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 24<span class="br0">&#41;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; t <span class="sy0">=</span> b <span class="sy0">+</span> k<span class="br0">&#91;</span>0<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; a <span class="sy0">^=</span> gost_data_1<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span>t<span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_2<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 8<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_3<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 16<span class="br0">&#41;</span><span class="br0">&#93;</span> <span class="sy0">^</span> gost_data_4<span class="br0">&#91;</span><span class="br0">&#40;</span>uint8_t<span class="br0">&#41;</span><span class="br0">&#40;</span>t <span class="sy0">&gt;&gt;</span> 24<span class="br0">&#41;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; buf<span class="sy0">-&gt;</span>y<span class="br0">&#91;</span>0<span class="br0">&#93;</span> <span class="sy0">=</span> <span class="br0">&#40;</span>uint32_t<span class="br0">&#41;</span>b<span class="sy0">;</span><br />
&nbsp; &nbsp; buf<span class="sy0">-&gt;</span>y<span class="br0">&#91;</span>1<span class="br0">&#93;</span> <span class="sy0">=</span> <span class="br0">&#40;</span>uint32_t<span class="br0">&#41;</span>a<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Зашифрование в режиме ECB выглядит так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52217">
        <div class="code c" id="p522code17">
<span class="kw4">void</span> encode<span class="br0">&#40;</span>uint8_t<span class="sy0">*</span> buf<span class="sy0">,</span> uint32_t len<span class="sy0">,</span> <span class="kw4">const</span> uint32_t<span class="sy0">*</span> key<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>int32_t i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span><span class="br0">&#40;</span>int32_t<span class="br0">&#41;</span>len<span class="sy0">;</span> i<span class="sy0">+=</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; do_encode<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span><span class="br0">&#41;</span><span class="br0">&#40;</span>buf<span class="sy0">+</span>i<span class="br0">&#41;</span><span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Длина явно приводится к знаковому типу для облегчения переноса на OpenMP (в спецификации есть требование, чтобы счетчики циклов были знаковыми).</p>
<p>Параллельная версия процедуры зашифрования будет выглядеть так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52218">
        <div class="code c" id="p522code18">
<span class="kw4">void</span> encode_omp<span class="br0">&#40;</span>uint8_t<span class="sy0">*</span> buf<span class="sy0">,</span> uint32_t len<span class="sy0">,</span> <span class="kw4">const</span> uint32_t<span class="sy0">*</span> key<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp for schedule(static) nowait</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>int32_t i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span><span class="br0">&#40;</span>int32_t<span class="br0">&#41;</span>len<span class="sy0">;</span> i<span class="sy0">+=</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do_encode<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span><span class="br0">&#41;</span><span class="br0">&#40;</span>buf<span class="sy0">+</span>i<span class="br0">&#41;</span><span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Makefile:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52219">
        <div class="code make" id="p522code19">
CC<span class="sy0">=</span>gcc<br />
CFLAGS<span class="sy0">=-</span>O3 <span class="sy0">-</span>fopenmp <span class="sy0">-</span>march<span class="sy0">=</span>native <span class="sy0">-</span>fstrict<span class="sy0">-</span>aliasing <span class="sy0">-</span>std<span class="sy0">=</span>gnu99 <span class="sy0">-</span>Wall <span class="sy0">-</span>Wextra <span class="sy0">-</span>Wno<span class="sy0">-</span>unused<span class="sy0">-</span>parameter <span class="sy0">-</span>Wstrict<span class="sy0">-</span>aliasing<span class="sy0">=</span>1 <span class="sy0">-</span>Wdisabled<span class="sy0">-</span>optimization<br />
CFLAGS_PGEN<span class="sy0">=-</span>g3 <span class="sy0">-</span>pg <span class="sy0">-</span>fprofile<span class="sy0">-</span>arcs <span class="sy0">-</span>ftest<span class="sy0">-</span>coverage <span class="sy0">-</span>fprofile<span class="sy0">-</span>generate<br />
CFLAGS_PUSE<span class="sy0">=-</span>fomit<span class="sy0">-</span>frame<span class="sy0">-</span>pointer <span class="sy0">-</span>fprofile<span class="sy0">-</span>use<br />
LDFLAGS<span class="sy0">=-</span>fopenmp<br />
LDFLAGS_PGEN<span class="sy0">=-</span>fprofile<span class="sy0">-</span>generate<br />
<br />
all<span class="sy0">:</span> gost<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">./</span>gost<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CC</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CFLAGS</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">LDFLAGS</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CFLAGS_PUSE</span><span class="br0">&#41;</span> main<span class="sy0">.</span>c gost<span class="sy0">.</span>c <span class="sy0">-</span>o gost<br />
<br />
gost<span class="sy0">:</span> main<span class="sy0">.</span>o gost<span class="sy0">.</span>o<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CC</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">LDFLAGS</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">LDFLAGS_PGEN</span><span class="br0">&#41;</span> <span class="re0">$^</span> <span class="sy0">-</span>o <span class="re0">$@</span><br />
<br />
main<span class="sy0">.</span>o<span class="sy0">:</span> main<span class="sy0">.</span>c gost<span class="sy0">.</span>h<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CC</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CFLAGS</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CFLAGS_PGEN</span><span class="br0">&#41;</span> <span class="sy0">-</span>c <span class="re0">$*</span><span class="sy0">.</span>c<br />
<br />
gost<span class="sy0">.</span>o<span class="sy0">:</span> gost<span class="sy0">.</span>c gost<span class="sy0">.</span>h<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CC</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CFLAGS</span><span class="br0">&#41;</span> <span class="sy0">$</span><span class="br0">&#40;</span><span class="re2">CFLAGS_PGEN</span><span class="br0">&#41;</span> <span class="sy0">-</span>c <span class="re0">$*</span><span class="sy0">.</span>c<br />
<br />
clean<span class="sy0">:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">-</span>rm <span class="sy0">-</span>f <span class="sy0">*.</span>o gost <span class="sy0">*.</span>gcda <span class="sy0">*.</span>gcno<br />
<br />
<span class="kw2">.PHONY</span><span class="sy0">:</span> all clean
        </div>
    </div>
</div>

<p>Производительность измерялась путем многократного прогона операции зашифрования над стомегабайтным (точнее, стомебибайтным) буфером.</p>
<p><del datetime="2009-04-03T22:53:17+00:00">Получились весьма интересные результаты:</p>
<table cellpadding="2" cellspacing="1" class="bordered">
<thead>
<tr>
<th>&nbsp;</th>
<th>Min</th>
<th>Max</th>
<th>Avg</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" align="right">Parallel</th>
<td>1.43</td>
<td>1.46</td>
<td>1.44</td>
</tr>
<tr>
<th scope="row" align="right">Serial</th>
<td>1.39</td>
<td>1.42</td>
<td>1.41</td>
</tr>
</tbody>
</table>
<p>Параллельная версия алгоритма не только не даёт прироста в производительности, но еще и проигрывает последовательной версии!<br />
</del></p>
<p>Так бы и считал, если бы во время тестирования альтернативной версии не заметил, что параллельная версия выполняется быстрее. Мораль: читайте маны.</p>
<p>Дело в том, что время измерялось следующим образом:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52220">
        <div class="code c" id="p522code20">
&nbsp; &nbsp; start <span class="sy0">=</span> clock<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; func<span class="br0">&#40;</span>buf<span class="sy0">,</span> n<span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; stop <span class="sy0">=</span> clock<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; x <span class="sy0">=</span> <span class="br0">&#40;</span><span class="kw4">double</span><span class="br0">&#41;</span><span class="br0">&#40;</span>stop <span class="sy0">-</span> start<span class="br0">&#41;</span> <span class="sy0">/</span> CLOCKS_PER_SEC<span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Но <code>CLOCKS_PER_SEC</code> не учитывает, что у нас четыре ядра, а не одно. Или как раз-таки учитывает. И вообще <abbr title="зависит от реализации">хз</abbr>. Я так понял, что на выходе я получаю время, которое понадобилось бы процессору на выполнение задачи, если бы он был один. Тёмный лес.</p>
<p>UPD: <q cite="http://linux.die.net/man/3/clock">POSIX requires that CLOCKS_PER_SEC equals 1000000 <strong>independent of the actual resolution</strong></q>.</p>
<p>Как правильно:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52221">
        <div class="code c" id="p522code21">
&nbsp; &nbsp; gettimeofday<span class="br0">&#40;</span><span class="sy0">&amp;</span>start<span class="sy0">,</span> NULL<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; func<span class="br0">&#40;</span>buf<span class="sy0">,</span> n<span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; gettimeofday<span class="br0">&#40;</span><span class="sy0">&amp;</span>stop<span class="sy0">,</span> NULL<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; x <span class="sy0">=</span> <span class="br0">&#40;</span>1000000.0f<span class="sy0">*</span><span class="br0">&#40;</span>stop.<span class="me1">tv_sec</span> <span class="sy0">-</span> start.<span class="me1">tv_sec</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="br0">&#40;</span>stop.<span class="me1">tv_usec</span> <span class="sy0">-</span> start.<span class="me1">tv_usec</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">/</span><span class="nu0">1000000</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Итак, что получилось на четырёх ядрах:</p>
<table cellpadding="2" cellspacing="1" class="bordered">
<thead>
<tr>
<th>&nbsp;</th>
<th>Min</th>
<th>Max</th>
<th>Avg</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" align="right">Parallel</th>
<td>0.365861</td>
<td>0.408662</td>
<td>0.371971</td>
</tr>
<tr>
<th scope="row" align="right">Serial</th>
<td>1.406327</td>
<td>1.418306</td>
<td>1.412239</td>
</tr>
</tbody>
</table>
<p>Что характерно, в случае с одним процессором оба способа измерения времени работают примерно одинаково, так что будем считать, что в статье <strong>"<a href="http://blog.sjinks.pro/c-cpp/519-practical-use-of-fast-types/">Практическая польза fast-типов</a>"</strong> я не сильно наврал со скоростью.</p>
<p>Когда я неудачно измерял время выполнения кода, я думал, что причина неудачи в том, что кэш процессора не резиновый: как-никак буфер для зашифрования 100 мебибайт, потоки берут код из совершенно разных участков памяти, процедура зашифрования активно использует 4 кибибайта таблиц…</p>
<p>Тогда я написал такую процедуру зашифрования, рассчитанную на четыре ядра:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p52222">
        <div class="code c" id="p522code22">
<span class="kw4">void</span> encode_omp<span class="br0">&#40;</span>uint8_t<span class="sy0">*</span> buf<span class="sy0">,</span> uint32_t len<span class="sy0">,</span> <span class="kw4">const</span> uint32_t<span class="sy0">*</span> key<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel sections shared(buf, key, len)</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>int32_t i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span><span class="br0">&#40;</span>int32_t<span class="br0">&#41;</span>len<span class="sy0">;</span> i<span class="sy0">+=</span>4<span class="sy0">*</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do_encode<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span><span class="br0">&#41;</span><span class="sy0">&amp;</span>buf<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>int32_t i<span class="sy0">=</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="sy0">;</span> i<span class="sy0">&lt;</span><span class="br0">&#40;</span>int32_t<span class="br0">&#41;</span>len<span class="sy0">;</span> i<span class="sy0">+=</span>4<span class="sy0">*</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do_encode<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span><span class="br0">&#41;</span><span class="sy0">&amp;</span>buf<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>int32_t i<span class="sy0">=</span>2<span class="sy0">*</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="sy0">;</span> i<span class="sy0">&lt;</span><span class="br0">&#40;</span>int32_t<span class="br0">&#41;</span>len<span class="sy0">;</span> i<span class="sy0">+=</span>4<span class="sy0">*</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do_encode<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span><span class="br0">&#41;</span><span class="sy0">&amp;</span>buf<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp section</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>int32_t i<span class="sy0">=</span>3<span class="sy0">*</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="sy0">;</span> i<span class="sy0">&lt;</span><span class="br0">&#40;</span>int32_t<span class="br0">&#41;</span>len<span class="sy0">;</span> i<span class="sy0">+=</span>4<span class="sy0">*</span><span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; do_encode<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">union</span> gostrec_t<span class="sy0">*</span> <span class="kw4">const</span><span class="br0">&#41;</span><span class="sy0">&amp;</span>buf<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="sy0">,</span> key<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Идея в том, что если потоки выполняются с примерно одинаковой скоростью, возможно улучшить локальность данных в кэше процессора (код, скорее, представлял proof of concept, ибо не учитывает целый ряд факторов).</p>
<table cellpadding="2" cellspacing="1" class="bordered">
<thead>
<tr>
<th>&nbsp;</th>
<th>Min</th>
<th>Max</th>
<th>Avg</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" align="right">Parallel Sections</th>
<td>0.375868</td>
<td>0.404222</td>
<td>0.384918</td>
</tr>
</tbody>
</table>
<p>Результат с использованием секций получился чуть-чуть хуже.</p>
<p>Выигрыш в производительности на четырёх ядрах составил 380% — не так уж и плохо (особенно если считать, что основной целью статьи было показать, что параллельность — это не всегда хорошо).</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/openmp/522-openmp-on-multicore-cpu-and-cryptography/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/openmp/522-openmp-on-multicore-cpu-and-cryptography/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>OpenMP: установка маски соответствия процессоров в Linux</title>
		<link>http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/</link>
		<comments>http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/#comments</comments>
		<pubDate>Sun, 15 Mar 2009 22:19:30 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[OpenMP]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=517</guid>
		<description><![CDATA[Минимизация переключения контекста между процессорами Специалисты Intel рекомендуют задавать маску соответствия процессоров (известную как CPU affinity mask) для потоков OpenMP, чтобы привязать поток к определённому процессору (или ядру процессора, что в данном случае одно и то же). Как утверждается, это позволяет минимизировать миграцию потоков и снизить стоимость переключения контекста между процессорами. Повышение производительности будет наблюдаться [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Минимизация переключения контекста между процессорами</em></h2>
<p>Специалисты Intel <a href="http://www.intel.com/technology/itj/2007/v11i4/8-video/4-methodology.htm">рекомендуют</a> задавать маску соответствия процессоров (известную как <dfn>CPU affinity mask</dfn>) для потоков <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a>, чтобы привязать поток к определённому процессору (или ядру процессора, что в данном случае одно и то же). Как утверждается, это позволяет минимизировать миграцию потоков и снизить стоимость переключения контекста между процессорами.<br />
<span id="more-517"></span><br />
Повышение производительности будет наблюдаться не во всех случаях, но речь сейчас не об этом.</p>
<p><a href="http://en.wikipedia.org/wiki/Affinity_mask">Wikipedia</a> приводит пример кода для Windows, для <a href="http://blog.sjinks.pro/tag/linux/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Linux">Linux</a> я ничего подобного не нашел.</p>
<p>В надежде, что код кому-нибудь пригодится, выкладываю версию для Linux:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p51725">
        <div class="code c" id="p517code25">
<span class="co2">#define _GNU_SOURCE</span><br />
<span class="co2">#include &lt;sched.h&gt;</span><br />
<span class="co2">#include &lt;omp.h&gt;</span><br />
<br />
<span class="kw4">void</span> set_thread_affinity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; cpu_set_t mask<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CPU_ZERO<span class="br0">&#40;</span><span class="sy0">&amp;</span>mask<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; CPU_SET<span class="br0">&#40;</span>omp_get_thread_num<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="sy0">&amp;</span>mask<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> res <span class="sy0">=</span> sched_setaffinity<span class="br0">&#40;</span>0<span class="sy0">,</span> <span class="kw4">sizeof</span><span class="br0">&#40;</span>mask<span class="br0">&#41;</span><span class="sy0">,</span> <span class="sy0">&amp;</span>mask<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">-</span><span class="nu0">1</span> <span class="sy0">==</span> res<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// failure</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Это соответствует такому коду для Windows:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p51726">
        <div class="code c" id="p517code26">
<span class="co2">#include &lt;windows.h&gt;</span><br />
<span class="co2">#include &lt;omp.h&gt;</span><br />
<br />
<span class="kw4">void</span> set_thread_affinity<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel default(shared)</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; DWORD_PTR mask <span class="sy0">=</span> <span class="br0">&#40;</span>1 <span class="sy0">&lt;&lt;</span> omp_get_thread_num<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; DWORD_PTR res <span class="sy0">=</span> SetThreadAffinityMask<span class="br0">&#40;</span>GetCurrentThread<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> mask<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="nu0">0</span> <span class="sy0">==</span> res<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// failure</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/517-openmp-setting-cpu-affinity-mask-in-linux/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

