<?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; Параллельное программирование</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/category/parallel/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>Практическое введение в строковые операции SSE4.2 (STTNI)</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/23/sse42-sttni/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/23/sse42-sttni/#comments</comments>
		<pubDate>Wed, 23 May 2012 12:19:17 +0000</pubDate>
		<dc:creator>chasovshikova</dc:creator>
				<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>
		<category><![CDATA[Intel SSE4.2]]></category>
		<category><![CDATA[STTNI]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/23/sse42-sttni/</guid>
		<description><![CDATA[В рамках конкурса Accelerate 2012 нам всем пришлось хорошенько поработать со строками. Сначала от участников, а затем и от организаторов прозвучала идея, что использование набора инструкций SSE4.2 может придать значительное ускорение. Скажу честно, мы с некоторым недоверием посмотрели на эту идею: — Как мы применим SSE в нашем, еще не существующем, решении? Но внесли её [...]]]></description>
			<content:encoded><![CDATA[<p>В рамках <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-main/">конкурса Accelerate 2012</a> нам всем пришлось хорошенько поработать со строками.<br />
Сначала от участников, а затем и от организаторов прозвучала идея, что использование набора инструкций SSE4.2 может придать значительное ускорение.</p>
<p>Скажу честно, мы с некоторым недоверием посмотрели на эту идею:<br />
<em>— Как мы применим SSE в нашем, еще не существующем, решении?</em><br />
Но внесли её в список todo и, когда код уже начал обретать законченный вид, мы нашли места "приложения рычага".<br />
Потратив всего несколько часов (на чтение документации по STTNI, эксперименты и переписывание) нам удалось ускорить "тяжелые" фрагменты <strong>в два-три раза</strong>!<br />
<span id="more-2007776"></span></p>
<p align="justify">Небольшое предупреждение: не ожидайте что SSE инструкции превратят неэффективный алгоритм в конфетку. Уделяйте внимание прежде всего асимптотике и простоте. Затем беритесь за специальные инструменты для участков кода на которые указал профайлер.</p>
<p>Я постараюсь описать картину крупными мазками. За подробностями загляните в <a href="http://softwarecommunity.intel.com/isn/Downloads/Intel%20SSE4%20Programming%20Reference.pdf">руководство по SSE4 для разработчиков</a>. Хорошая документация по командам и параметрам лежит в <a href="http://msdn.microsoft.com/en-us/library/bb514048.aspx">MSDN</a>. Оба ресурса на английском языке.</p>
<p><strong>Что включает в себя SSE4.2</strong></p>
<ul>
<li>операции с парами строк — именно о них пойдет речь дальше</li>
<li>вычисление CRC32</li>
<li>подсчет количества установленных бит</li>
<li>операция "больше чем" упакованных 64-битных знаковых чисел (Compare Packed Signed 64-bit data For Greater Than)</li>
</ul>
<p><strong>Выбираем форму</strong></p>
<p>Существует 4 инструкции процессора и целых 14 команд поддерживаемых компилятором.<br />
Следующий рисунок поможет разобраться с ними:</p>
<p><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/5-22-2012-21-02-13_cr1.png" alt="" width="224" height="237" class="aligncenter size-full wp-image-2007792" /></p>
<ul>
<li><strong>e</strong> или <strong>i</strong> в середине команды говорит о том, как определена длина входных строк: <strong>e</strong>xplicit&nbsp;—&nbsp;длина указывается явно или <strong>i</strong>mplicit&nbsp;—&nbsp;процессор сам определит длину нуль-терминированной строки.</li>
<li align="justify">суффикс команды определяет тип результата: <strong>i</strong>ndex — значение индекса или <strong>m</strong>ask — битовая маска. Кроме этого, можно получить значение арифметических регистров (выделены синим), которые изменяются в зависимости от содержимого операндов и полученного результата. Это пригодится, если вам, к примеру, интересно не точное местоположение символа в строке, а сам факт его наличия.</li>
</ul>
<p><strong>... и содержание</strong></p>
<p align="justify">Последний аргумент каждой из команд — управляющий байт (imm8 control byte) объясняет как нужно интерпретировать данные и что с ними нужно сделать.</p>
<p>Байт формируется комбинацией флагов которые определяют:</p>
<ul>
<li>формат данных: знаковые или беззнаковые числа? Байты или пары байт (unicode)?</li>
<li>тип сравнения:
<ul>
<li>равенство любых символов — для поиска символов из набора (<code>_SIDD_CMP_EQUAL_ANY</code>)</li>
<li>равенство символов из диапазона — поиск символов из диапазона (<code>_SIDD_CMP_RANGES</code>)</li>
<li>посимвольное сравнение — сравнение строк (<code>_SIDD_CMP_EQUAL_EACH</code>)</li>
<li>упорядоченное сравнение — поиск подстрок (<code>_SIDD_CMP_EQUAL_ORDERED</code>)</li>
</ul>
</li>
<li align="justify">промежуточное преобразование результата (polarity). Подробности в документации. Ниже будет пример с использованием этого флага</li>
<li align="justify">для результата-индекса — нужно ли вернуть наименьший (<code>_SIDD_LEAST_SIGNIFICANT</code>) или наибольший найденный индекс (<code>_SIDD_MOST_SIGNIFICANT</code>)</li>
<li align="justify">для результата-маски — вернуть битовое (<code>_SIDD_BIT_MASK</code>) или целочисленное (<code>_SIDD_UNIT_MASK</code>) её представление.</li>
</ul>
<p><strong>Примеры</strong></p>
<p>Пара примеров прямо из нашего кода. Мы оперируем беззнаковыми байтами в силу специфики решения.</p>
<p>Поиск первого вхождения любого символа из строки:</p>
<pre name="code" class="cpp">
int get_first_index_of_symbol(unsigned char * needle, int needle_length,
	unsigned char * haystack, int haystack_length)
{
	return _mm_cmpestri(
		_mm_loadu_si128((const __m128i*)needle), needle_length,
		_mm_loadu_si128((const __m128i*)haystack), haystack_length,
		_SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
}
</pre>
<p>Длина строк известна и нам нужна позиция символа. Выбор падает на _mm_cmpestri.</p>
<p>Определяемся с флагами:</p>
<ul>
<li>мы оперируем с беззнаковыми байтами — <code>_SIDD_UBYTE_OPS</code></li>
<li>нужно найти вхождения символа в любом месте — <code>_SIDD_CMP_EQUAL_ANY</code></li>
<li>нас интересует первое вхождение — <code>_SIDD_LEAST_SIGNIFICANT</code></li>
</ul>
<p>Все эти флаги можно опустить, поскольку они установлены по умолчанию.</p>
<p>Поиск первого несовпадающего символа для строк равной длины (используется для нахождения длины наибольшего общего префикса):</p>
<pre name="code" class="cpp">
int get_first_unmatch(unsigned char * a, unsigned char * b, int length)
{
	return _mm_cmpestri(
		_mm_loadu_si128((const __m128i*)a), length,
		_mm_loadu_si128((const __m128i*)b), length,
		_SIDD_CMP_EQUAL_EACH | _SIDD_NEGATIVE_POLARITY);
}
</pre>
<p>Обратите внимание, что поменялись только управляющие флаги:</p>
<ul>
<li>нужно посимвольное сравнение — <code>_SIDD_CMP_EQUAL_EACH</code></li>
<li>нас интересует несовпадение — <code>_SIDD_NEGATIVE_POLARITY</code></li>
</ul>
<p>Что получится, если символ не был найден или строки совпадают? Мы получим в ответ 16 (или 8, если бы операции были над строками из двухбайтовых символов).</p>
<p>Чтобы не быть голословной, утверждая что STTNI дает преимущество, я замерила время выполнения участка кода для файла размером 200 Мб:</p>
<ul>
<li>без SSE - 360 мс
<pre name="code" class="cpp">
//i - текущее положение в строке buffer размера size
while (i &lt; size &amp;&amp; buffer[i] != '\n' &amp;&amp; buffer[i] != '&gt;') i++;
//i выходит за пределы строки или указывает на искомый символ
</pre>
</li>
<li>с SSE - 171 мс
<pre name="code" class="cpp">
//i - текущее положение в строке buffer размера size
char * needle = "\n&gt;";
while (i + 15 &lt; size)
{
	int r = get_first_index_of_symbol(
		(unsigned char *)needle, 2,
		(unsigned char *)(buffer + i),
		16);
	i += r;
	if (r != 16) break;
}
while (i &lt; size &amp;&amp; buffer[i] != '\n' &amp;&amp; buffer[i] != '&gt;') i++;
//i выходит за пределы строки или указывает на искомый символ
</pre>
</li>
</ul>
<p>Результат налицо (:</p>
<p>Интересно, а кто еще использовал SSE4.2 в решении? Насколько быстрее оно сделало ваш код?</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/23/sse42-sttni/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Решение задачи конкурса Accelerate 2012 при помощи классического суффиксного дерева</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/22/accelerate-2012-3/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/22/accelerate-2012-3/#comments</comments>
		<pubDate>Tue, 22 May 2012 06:10:09 +0000</pubDate>
		<dc:creator>leventov</dc:creator>
				<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>
		<category><![CDATA[суффиксное дерево]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/22/accelerate-2012-3/</guid>
		<description><![CDATA[Мы выбрали суффиксное дерево в качестве базовой структуры данных для решения задачи конкурса Accelerate 2012, потому что во многих источниках, начиная с английской википедии и заканчивая рефератами, непосредственно касающихся поиска совпадающих частей в геномах, рекомендуется именно суффиксное дерево. Не буду описывать базовый алгоритм Укконена построения дерева, - это уже сделано за меня в огромном количестве [...]]]></description>
			<content:encoded><![CDATA[<p>Мы выбрали суффиксное дерево в качестве базовой структуры данных для решения <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-problem/">задачи конкурса Accelerate 2012</a>, потому что во многих источниках, начиная с <a href="http://en.wikipedia.org/wiki/Longest_common_substring_problem">английской википедии</a> и заканчивая рефератами, непосредственно касающихся поиска совпадающих частей в геномах, рекомендуется именно суффиксное дерево.<br />
<span id="more-2007711"></span><br />
Не буду описывать базовый алгоритм Укконена построения дерева, - это уже сделано за меня в огромном количестве мест, можно найти не одну готовую реализацию на C++. Для наилучшего понимания могу посоветовать эту лекцию на русском языке: <a href="http://yury.name/internet/01ianote.pdf">http://yury.name/internet/01ianote.pdf</a></p>
<p><strong>Построение дерева "оффлайн"</strong><br />
Преимуществом алгоритма Укконена считается возможность построить дерево "онлайн", то есть по строчке заранее неизвестного размера. Мне трудно представить, в какой реальной задаче это нужно, однако большинство открытых реализаций "наращивают" дерево по букве. Оффлайновый вариант проще для понимания и быстрее на 30%.</p>
<p><strong>Дерево только из короткой строки</strong><br />
В конкурсной задаче максимальные общие подстроки (LCS) ищутся <em>только из 2</em> источников. При таком ограничении смысла в так называемом <a href="http://en.wikipedia.org/wiki/Generalised_suffix_tree">обобщенном дереве</a> нет (вот было бы исходных строк 3 - пришлось бы строить обобщенное дерево по 2 из них). Вместо этого можно "пройтись второй строкой" по дереву, построенному по первой, запоминая "места" (пара: позиция в строке - позиция в дереве) максимального совпадения, и после несложной обработки получить из них ответы в <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-problem/">нужном формате</a>.</p>
<p>Строить дерево лучше по более короткой строке, потому что оно занимает очень много памяти.</p>
<pre name="code" class="cpp">typedef unsigned int uint;
// в 64-битных системах размер uint - 4 байта, любой ссылки - 8 байт.
// структура вершины дерева - 48 байт
struct vertex {
        uint left, right; // срез в строке, соотв. подвешивающему эту вершину ребру
        uint length; // right - left, используется очень часто
        // 5 8-байтных ссылок было бы уже слишком накладно
        // vertex *child = tree + v-&gt;children['A']; // арифметика указателей
        uint children[5]; // ACTG$
        // При особом желании можно было бы сэкономить еще 8 байт (8 + 8 - 4 - 4)
        // так же, как в children. Но неудобно, эти ссылки часто используются
        vertex *parent, *link;
};
// Дерево: C-style массив
vertex *tree;
</pre>
<p>Теоретически, для составленной из 4 букв строки длины L суффиксное дерево может содержать от 1,34L до 2L вершин, практический диапазон для генома - 1,6-1,7L. Таким образом, в среднем наше дерево жрет около 48*1,66 = 80 байт на каждую букву строки. Это критичный недостаток с точки зрения формальных конкурсных ограничений: для строки длиной в 2^32 буквы дерево занимает 320 Гб! Но вполне терпимо, если рассматриваются только реальные геномы: скажем, в самой длинной хромосоме человека 250 млн нуклеотидов, ее дерево - "всего" 20 Гб.</p>
<p><strong>Масштабирование</strong><br />
Справедливо отмечается, что построение суффиксного дерева (по крайней мере, алгоритмом Укконена) совсем или почти не поддается распараллеливанию. Мы решили занять ядра процессора проще: разбить самые длинные из входных строк на части, искать максимальные совпадения среди этих частей, а в конце, при необходимости, "склеить" совпадения, касающиеся одной исходной строки. Есть только одна проблема - возможная потеря решений:<br />
<img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/accelerate-split-problem.png" alt="" width="730" class="aligncenter" /><br />
Эту проблему можно решить модификацией (тривиальной при использовании суффиксных деревьев) алгоритма нахождения LCS: пусть он возвращает не только совпадения, большие M (min match length), но и любой длины, заканчивающиеся в конце одной или обоих строк (частей исходных строк, в реальности). Если какое-то большое фактическое совпадение из-за разбиения входных строк придется склеить из K совпадений между частями, то все они, кроме, возможно, последнего, как раз будут заканчиваться в точке разбиения. Например на рисунке выше совпадение 1 заканчивается в точке разбиения 1-й строки. Т. о. после "склейки" еще нужно попробовать продлить полученное совпадение вправо  - максимум на M - 1 символов. В примере выше получится продлить совпадение 1 на длину совпадения 2.</p>
<p>Однако, если на этом ограничиться, количество ответов будет слишком большим (допустим, если одна из строк заканчивается на 'A', придется вывести все позиции буквы A во второй строке, и наоборот). Поэтому мы разбиваем строки с фиксированным перекрытием (OL, overlap): очередная часть начинается на OL символов раньше, чем закончилась предыдущая. Тогда, очевидно, можно не выводить совпадения, заканчивающиеся в конце одной из строк и меньшие OL. Нетрудно заметить, что если M существенно меньше длин входных строк (а в реальности так оно и есть), можно разбивать их с перекрытием M и вообще отказаться от модификации алгоритма нахождения LCS.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/22/accelerate-2012-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Интересные подробности решения команды X!</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/21/x/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/21/x/#comments</comments>
		<pubDate>Mon, 21 May 2012 09:40:47 +0000</pubDate>
		<dc:creator>chasovshikova</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/21/x/</guid>
		<description><![CDATA[Привет всем! Я решила рассказать о том, что не вошло в наше отправленное решение на конкурс Accelereate 2012 из-за ограничения на размер. Для начала рекомендую прочитать краткую версию, чтобы быть в контексте. Итак: Получение бинарного кода символа Поскольку символов во входной последовательности всего 4, то их можно закодировать всего двумя битами. Решение "в лоб": switch [...]]]></description>
			<content:encoded><![CDATA[<p>Привет всем!</p>
<p>Я решила рассказать о том, что не вошло в наше отправленное решение на <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-main/">конкурс Accelereate 2012</a> из-за ограничения на размер.<br />
Для начала рекомендую прочитать <a href='http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/readme.pdf'>краткую версию</a>, чтобы быть в контексте.</p>
<p>Итак:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/1283278043_1283276920_bazzinga-208.jpg"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/1283278043_1283276920_bazzinga-208-300x248.jpg" alt="" width="300" height="248" class="alignnone size-medium wp-image-2007762" /></a></p>
<p><strong>Получение бинарного кода символа</strong></p>
<p>Поскольку символов во входной последовательности всего 4, то их можно закодировать всего двумя битами.<br />
Решение "в лоб":</p>
<pre name="code" class="cpp">
switch (c)
{
case 'A':
	return 0;
case 'C':
	return 1;
case 'G':
	return 2;
case 'T':
	return 3;
}
</pre>
<p>Очень просто, но наличие ветвления в таком "популярном" месте не радует. Попробуем посмотреть внимательно на символы:</p>
<pre name="code" class="cpp">A = 0x41 = 1000(00)1
C = 0x43 = 1000(01)1
G = 0x47 = 1000(11)1
T = 0x54 = 1010(10)0</pre>
<p>Видно, что выделенные биты у каждого символа разные, поэтому switch можно заменить на</p>
<pre name="code" class="cpp">return c &gt;&gt; 1 &amp; 3;</pre>
<p><strong>Хранение тестовых последовательностей</strong></p>
<p align="justify">В промежуточном решении для хранения координаты суффикса из тестовой строки мы использовали пару <em>&lt;#тестовой&nbsp;строки, индекс&gt;</em>. Это неэффективно с точки зрения использования памяти (нужно 8 байт на каждый суффикс).</p>
<p align="justify">Немного поразмыслив, мы переписали работу с тестовыми строками: все они хранятся в сцепленном (concatenated) виде. Для разделения существует массив с информацией о длинах. Это позволило вдвое уменьшить размер структуры хранения суффиксов и на четверть — массива результатов (в одной записи результата мы теперь храним всего три 4-х байтовых числа - индексы рефересной и тестовой строк и длину).</p>
<p><strong>Параллельное чтение файлов</strong></p>
<p align="justify">Мы пытались реализовать чтение с помощью TBB Pipeline. Но, к сожалению, при использовании pipeline возникали странные артефакты в виде неправильного определения длины файла в Windows (на первый взгляд — из-за трактовки переносов строк). Наверняка рецепт исправления был прост, но поскольку времени уже не оставалось, решили просто обернуть процедуру чтения в OpenMP parallel for вместе с помещением чтения файла в критическую секцию.</p>
<p><strong>Вывод результатов</strong></p>
<p align="justify">Довольно значительную часть времени вывода результата занимает преобразование числа в строку. Для ускорения этого преобразования мы использовали стороннюю библиотеку <a href="http://code.google.com/p/stringencoders/">stringencoders</a>, а также кешировали последнее значение индекса окончания рефересной строки (именно он — "главный" в сортировке, поэтому при большом количестве результатов велика вероятность повторения).</p>
<p>Если у вас есть какие-либо вопросы — смело пишите. Я отвечу в комментариях или, если это будет возможно, дополню пост.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/21/x/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Вариант решения задачи конкурса Accelerate 2012</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/19/accelerate-2012-2/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/19/accelerate-2012-2/#comments</comments>
		<pubDate>Sat, 19 May 2012 13:17:22 +0000</pubDate>
		<dc:creator>flash2048</dc:creator>
				<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/19/accelerate-2012-2/</guid>
		<description><![CDATA[Совсем недавно закончился конкурс Accelerate Your Code 2012. Решил поделиться своим решением. Конечно, вряд ли оно будет оптимальным, но некоторые идеи могут пригодиться… Не буду приводить условие задачи, его можно найти на этой странице. Идея моего решения, заключалась в постройке хэш-таблицы для ref-строки. Хэш строил по 6 символам, это связано с тем, что минимальная длина [...]]]></description>
			<content:encoded><![CDATA[<p>Совсем недавно закончился конкурс <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-main/">Accelerate Your Code 2012</a>. Решил поделиться своим решением. Конечно, вряд ли оно будет оптимальным, но некоторые идеи могут пригодиться…<br />
Не буду приводить условие задачи, его можно найти на <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-problem/">этой странице</a>.<br />
Идея моего решения, заключалась в постройке хэш-таблицы для ref-строки. Хэш строил по 6 символам, это связано с тем, что минимальная длина M равна 6.<br />
Если находить хэши из строк, состоящих их 6 символов, из алфавита, включающего в себя 4 символа (A, C, G, T), то всего получится 4^6  = 4096 возможных вариантов.<br />
Моя функция хэширования приобрела следующий вид:</p>
<pre name="code" class="cpp">int digit(char a1, char a2, char a3, char a4, char a5, char a6){
	int d = 0;
//1
	switch (a1){
		case 'C':
			d += 1024;
		break;
		case 'G':
			d += 2048;
		break;
		case 'T':
			d += 3072;
		break;

		default:  break;
	}
//2
	switch (a2){
		case 'C':
			d += 256;
		break;
		case 'G':
			d += 512;
		break;
		case 'T':
			d += 768;
		break;
		default:  break;
	}
//3
	switch (a3){
		case 'C':
			d += 64;
		break;
		case 'G':
			d += 128;
		break;
		case 'T':
			d += 192;
		break;
		default:  break;
	}
//4
	switch (a4){
		case 'C':
			d += 16;
		break;
		case 'G':
			d += 32;
		break;
		case 'T':
			d += 48;
		break;
		default:  break;
	}
//5
	switch (a5){
		case 'C':
			d += 4;
		break;
		case 'G':
			d += 8;
		break;
		case 'T':
			d += 12;
		break;
		default:  break;
	}
//6
	switch (a6){
		case 'C':
			d += 1;
		break;
		case 'G':
			d += 2;
		break;
		case 'T':
			d += 3;
		break;
		default:  break;
	}
	return d;
}</pre>
<p>На основе хэша, в данном случае легко получить исходную строку, но для решения поставленной задачи этого не требовалось.<br />
Для хранения хэшей ref-строки использовались вектора: <code>vector&lt;size_t&gt; **L = new vector&lt;size_t&gt;*[4096];</code><br />
Для нахождения общих подстрок использовался такой же принцип, как и в предоставленном решении, только вместо массива использовались 2 строки.<br />
Вот собственно и все…</p>
<p><em>С нетерпением жду решение победителя, очень хочется увидеть оптимальный вариант решения данной задачи.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/19/accelerate-2012-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Поиск одинаковых участков в нуклеотидных цепочках с помощью индексации</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/19/2007637/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/19/2007637/#comments</comments>
		<pubDate>Sat, 19 May 2012 06:10:02 +0000</pubDate>
		<dc:creator>dyam</dc:creator>
				<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>
		<category><![CDATA[openmp]]></category>
		<category><![CDATA[конкурс]]></category>
		<category><![CDATA[Конкурсы и мероприятия]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/19/2007637/</guid>
		<description><![CDATA[Данный пост написан в рамках конкурса Accelerate Your Code 2012. Самый простой и самый не быстрый из методов поиска одинаковых участков в нуклеотидных цепочках - "наивный" перебор строк со смещением. Он хорошо подходит для коротких цепочек, так как не требует предварительной обработки и дополнительных объемов памяти. Но требование O(nm) по времени в общем случае нас [...]]]></description>
			<content:encoded><![CDATA[<p>Данный пост написан в рамках конкурса <a href="http://software.intel.com/ru-ru/articles/contest-acceler8-2011-main/">Accelerate Your Code 2012</a>.</p>
<p>Самый простой и самый не быстрый из <a href="http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B8">методов</a> поиска одинаковых участков в нуклеотидных цепочках - "наивный" перебор строк со смещением. Он хорошо подходит для коротких цепочек, так как не требует предварительной обработки и дополнительных объемов памяти.<br />
Но требование O(nm) по времени в общем случае нас не устроит, потому будем подбирать другие варианты.</p>
<p><strong>Подготовка данных</strong><br />
Для начала подумаем об ускорении используя подготовку данных.<br />
Так как мы будем проводить поиск в нуклеотидных цепочках, мы можем ограничить алфавит или же понизить битность на один символ. Всего таких символов у нас 4, а именно АЦГТ (ACGT). Это дает возможность использовать всего 2 бита вместо 8 (ANSII). Каким же образом построить соответствие? Если у нас нет дополнительных наполеоновских планов, можно в любом, но если вспомнить что эти нуклеотиды составляют пары (А-Т, Ц-Г) лучше на будущее провести такие аналогии:</p>
<ul>
<li>A = 00<sub>2</sub>=0</li>
<li>T = 11<sub>2</sub>=3</li>
<li>C = 01<sub>2</sub>=1</li>
<li>G = 10<sub>2</sub>=2</li>
</ul>
<p>Таким незамысловатым способом мы бы могли решить одну маленькую проблему - проверку пары нуклеотидов на комплементарность. В общем случае на это приходилось бы 8 проверок, в нашем же - только одна, проверка суммы значений на равенство 3. Уже неплохое ускорение в 8 раз. Правда времени потратим О(N). Ну да ладно - не смертельно, все-равно надо от мусора (перевод строк или пробелы) избавляться при считывании из неподготовленных данных.</p>
<p>Теперь подумаем вот о чем: а почему бы нам каким-нибудь образом сравнивать не по одной паре, а сразу по несколько. Ну что же, на помощь к нам придут хеш-функции для задания соответствия некоторой цепочки нуклеотидов длиной W и целого натурального числа. Таким образом, если два значения хеш-функции не совпадают, значит и цепочки гарантированно разные.<br />
С равенством возможны варианты. Если использовать хеш-функции без коллизий (например, представление цепочки как число по основанию 4), мы также сможем гарантировать и идентичность цепочек. Такая хеш-функция будет цикличной, что позволит использовать предыдущее рассчитанное значение при смещении на 1 нуклеотид. Но тут возникают некоторые трудности с хранением данных - в переменную размером К бит мы сможем сохранить только К/2 нуклеотида. Затраты на подготовку О(N), при использовании цикличных хеш-функций типа такой:</p>
<pre name="code" class="cpp">int32_t W_ = W - 1;
for (i = 0; i &lt; W_; i++) {
	key = ((key &lt;&lt; 2) + Seq[i]) &amp; mask;
}
for (i = W_, p = 0; i &lt; Seq_l; i++, p++) {
	key = ((key &lt;&lt; 2) + Seq[i]) &amp; mask;
	Table_Key[p] = key;
}</pre>
<p>Для снятия ограничения на максимальную длину цепочки можем, например, использовать несколько различных хеш-функций с коллизиями для уменьшения вероятности совпадений коллизий. Или просто проверить совпадение на идентичность.</p>
<p>Но ведь нам все-равно придется сравнивать одно и тоже по несколько раз. А если нам надо сравнить не одну пару, а некий набор пар в которые входит одна и та же строка? Для ускорения поиска можем построить индекс позиций подстрок у которых значения хеш-функций (одной или нескольких) совпадают. Этот этап мы можем организовать по-разному.</p>
<p><u>Первый способ</u><br />
После превращения цепочки в массив хешей, каждый элемент записать как пару хеш-позиция. Дальше нам предстоит отсортировать этот массив по порядку значений хешей. Затраты по времени от O(N*Log(N)).<br />
На этом можно и остановиться, а можно для ускорения поиска по нему преобразовать в двумерный массив. В нем элемент первого порядка будет соответствовать значению хеш-функции, а элементы второго порядка - позициям. При необходимости можно отсортировать и массив позиций - необходимо, если надо искать свертку одной строки на себя же (комплементарная связь, а не идентичность).<br />
Такой способ подойдет в том случае если значения хеш-функции находятся в большом диапазоне величин, но используемых намного меньше от суммарного.<br />
Поиск нужного значения хеш-функции возможен либо через бинарный поиск, либо через индексацию диапазонов с последующим бинарным поиском.</p>
<p><u>Второй способ</u><br />
По-другому можем поступить в случае, если хеш-функция имеет ограниченный в разумных рамках диапазон величин.<br />
Для примера используем хеш-функцию без коллизий, приведенную выше. В ней диапазон величин будет зависеть только от длины цепочки W, для которой мы строим соответствие. И этот диапазон составит от 0 до 4<sup>W</sup>-1. Таким образом мы сможем воспользоваться уже "линейной" сортировкой с временем О(N) для построения двумерного массива соответствий значений хеш-функции и позиции.<br />
Смысл простой. Создаем два массива: линейный S размером 4<sup>W</sup> и двумерный T размером по первому индексу также 4<sup>W</sup>, а по второму как получится (см. ниже).<br />
На первом этапе воспользуемся массивом S для построения гистограммы вхождений значений хеш-функции в массив за время O(N).<br />
На втором этапе выделяем память для подмассивов Т на количество элементов, указанных в соответствующей ячейке S.<br />
Для окончательного заполнения будем использовать массив S как счетчик заполненности. В Т первый индекс будет соответствовать значению хеш-функции, второй берем из S, значение - позиция хеша в начальном массиве. Время, затраченное на заполнение, также О(N).<br />
При желании и необходимости этот массив можно "причесать" - для хеш-функций соответствующих однородным строкам (АА..АА и подобных) выкинуть часть позиций по некоторым правилам.<br />
В итоге за линейное время получим двумерный массив-индекс Т для быстрого определения положений подстроки длиной W по части (так же длиной W) заданного шаблона.<br />
Затраты по времени общей подготовки О(N). Затраты на дополнительную память зависят от выбранной длинны W: массив хешей будет больше начальной строки в 2-4-... раза в зависим от выбранного типа данных (16, 32 и больше бит). Массив S будет содержать 4<sup>W</sup> элементов размером от 8 до 32+ бит в зависимости от размеров строк. Массив Т будет занимать по объему примерно такой же объем как S (первый уровень индексации) и начальный массив суммарно.<br />
Поиск нужного значения хеш-функции будет происходить за время О(1), но накладные затраты (массив S и первый уровень массива Т) могут быть больше чем полезная нагрузка (списки позиций).</p>
<p><strong>Поиск совпадений</strong><br />
Для коротких строк достаточно построение массива хеш-функций подстрок длиной W, с последующим расширением совпадающим участков по 1 или W нуклеотидов.<br />
Для длинных строк возможно будет иметь смысл сравнивать два массива индексов - только те позиции подстрок, хеши которых будут присутствовать в обоих.<br />
Разделение строк на длинные и короткие разное для разных случаев. И зависит от выигрыша в скорости работы.</p>
<p><strong>Сравнение с другими методами</strong><br />
Казалось бы, что приведенный выше метод не очень хорош, так как дает только "точки соприкосновения" двух подстрок. И дальнейшее расширение до необходимой минимальной длины может "съесть" все ускорение. Но:</p>
<ul>
<li>иногда минимальная необходимая длина достаточно маленькая (W = W<sub>min</sub>), что бы массив индексов был не столь уж и большим.</li>
<li>алгоритм поиска с помощью суффиксных деревьев/массивов точно потребует объем в много раз превышающий размер входных данных, при сравнимых скоростях поиска</li>
</ul>
<p><strong>Масштабируемость</strong><br />
В наше время когда корабли... О чем это мы... ах да... в наше время когда в каждом ПК минимум по 2 ядра, можно подумать и о параллельной обработке.<br />
Сразу оговорюсь, что использовал OpenMP. Ибо до этого параллельным программированием не пользовался, а данная технология оказалось весьма и весьма... простой в понимании и в реализации...<br />
Рассмотрим основные этапы алгоритма и мое решение по распараллеливанию.</p>
<p><u>1. Создание массива хешей подстрок</u><br />
Ну тут все довольно просто - делим начальную строку на участки, каждый участок своему потоку. Учитываем при этом, что на построение N хешей необходимо N+W-1 нуклеотидов. То есть нарезка происходит с перехлестами. Запись готовых результатов тоже разделена в памяти. Как дополнительные вычисления на каждый поток будет просчет первых W-1 хешей недостаточной длины. Но это явно мелочи по сравнению с выгодой в скорости. Но и кидаться в крайности тоже не стоит - должно выполняться условие ограничение разбиения P*W &lt;&lt; N, где P - количество участков.</p>
<p><u>2. Индексация подстрок</u><br />
Эту проблему я решил так же разбиением начального массива уже готовых хешей на несколько участков. Для разделения участков записи в конечных индекс воспользовался массивами счетчиками для каждого потока, с последующим суммирование нужных и использованием значения как сдвиг начала при записи. Основной проигрыш - использование дополнительных массивов под гистограммы и доступ к нужной ячейке с помощью сдвига. Тут тоже стоит себя ограничивать в бездумном дроблении - каждый массив под гистограмму имеет 4<sup>W</sup> элементов от 8 до 64 бит. Что при большом количестве потоков может занимать значимый объем памяти.</p>
<p><u>3. Поиск совпадений</u><br />
Тут еще проще - всю работу можно разделить на задания которые между собой не сильно связаны. Таким заданием, в случае сравнения массива и индекса, будет проверка совпадений для подстроки из шаблона и списка позиций возможных совпадений из индекса. Их будет приличное количество - динамическое распределение работы между потоками позволит адекватно балансировать нагрузку.<br />
Для сравнения двух индексов неделимым заданием может быть как сравнения списка позиций для совпадающих хешей, так и более мелким - просчет результата для пары из возможного списка комбинаций позиций. Это решит проблему перекоса в случае если в обоих индексах некоторые значения хеш-функций будут содержать значительно большее количество позиций, чем остальные.<br />
Еще вариант при котором будем отсекать некоторые варианты, если необходимо найти совпадение подстрок длиной намного больше чем 2*W. При этом возьмем две подстроки длиной W на таком расстоянии L, так что бы 2*W + L = W<sub>min</sub>. Дальше будем сравнивать массивы позиций из индекса для хешей этих двух строк. Если расстояние между определенными элементами будет также равно L - возможно совпадение больших подстрок, что подтвердится полной проверкой. Оценка по времени такой проверки на совпадения О(Log(n<sub>1</sub>)*Log(n<sub>2</sub>)), где n<sub>i</sub> - длины списков.<br />
Чтобы не использовать критические секции, для каждого потока можно использовать отдельные массивы для хранения найденных результатов. С последующим слиянием и сортировкой/выборкой за время O(К + К*Log(К)).</p>
<p>Общее время алгоритма стремится к О(N + K + K*Log(K)), где N общий объем входных данных, К - объем не фильтрованных выходных данных.</p>
<p>Данный пост написан в рамках конкурса <a href="http://software.intel.com/fr-fr/articles/AYC-early2012_home/">Accelerate Your Code 2012</a>.<br />
Спасибо за внимание. ЦУ буду рад прочитать в комментариях.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/19/2007637/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Использование s-дерева для нахождения общих подстрок генетических последовательностей</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/18/s/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/18/s/#comments</comments>
		<pubDate>Fri, 18 May 2012 11:15:23 +0000</pubDate>
		<dc:creator>iamfullofspam</dc:creator>
				<category><![CDATA[Академическое сообщество]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>
		<category><![CDATA[s-дерево]]></category>
		<category><![CDATA[s-массив]]></category>
		<category><![CDATA[задача поиска общих подстрок]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/18/s/</guid>
		<description><![CDATA[В&#160;этой статье мы&#160;опишем разработанный нами метод решения задачи с конкурса параллельного программирования Accelerate 2012. В&#160;задаче требовалось найти наибольшие общие подстроки у&#160;двух генетических последовательностей (то&#160;есть строк, состоящих из&#160;символов A,G,T,C), превосходящие по&#160;длине наперёд заданную величину M&#160;&#8805; 6. Пожалуй, первая мысль человека, знакомого с&#160;алгоритмами поиска подстроки&#160;&#8212; построить суффиксное дерево, однако после просмотра ограничений на&#160;размеры строк (а&#160;именно&#160;&#8212; они не&#160;длиннее [...]]]></description>
			<content:encoded><![CDATA[<div style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;font-size: 13px">
<p>В&nbsp;этой статье мы&nbsp;опишем разработанный нами метод решения задачи с <a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-problem/"> конкурса параллельного программирования Accelerate 2012.</a> В&nbsp;задаче требовалось найти наибольшие общие подстроки у&nbsp;двух генетических последовательностей (то&nbsp;есть строк, состоящих из&nbsp;символов A,G,T,C), превосходящие по&nbsp;длине наперёд заданную величину M&nbsp;&#8805; 6. </p>
<p>Пожалуй, первая мысль человека, знакомого с&nbsp;алгоритмами поиска подстроки&nbsp;&mdash; построить суффиксное дерево, однако после просмотра ограничений на&nbsp;размеры строк (а&nbsp;именно&nbsp;&mdash; они не&nbsp;длиннее 2<sup>32</sup>) мысль отпадает. Несмотря на&nbsp;небольшую константу в&nbsp;оценке O(n), размер дерева будет весьма велик, и&nbsp;кэш процессора практически не&nbsp;будет задействоваться. В&nbsp;итоге перемещение по&nbsp;дереву станет довольно медленным.</p>
<p>Для построения суффиксного дерева есть линейные алгоритмы, например, <a href="http://yury.name/internet/01ianote.pdf">алгоритм Укконена</a>, однако с&nbsp;распараллеливанием у&nbsp;них дела обстоят не&nbsp;самым радужным образом. </p>
<p>Это ни&nbsp;в&nbsp;коем случае не&nbsp;приговор суффиксным деревьям. Наверняка будут эффективные решения на&nbsp;их&nbsp;основе&nbsp;&mdash; результаты конкурса покажут. Мы&nbsp;просто изложили соображения, которые подтолкнули нас к&nbsp;поиску другой структуры данных.</p>
</p>
<p>Обратимся к&nbsp;<a href="http://habrahabr.ru/post/115346/">суффиксному массиву</a>&nbsp;&mdash; другому варианту представления строки.<br />
Суффиксный массив весьма компактен, однако строится непросто.<br />
Основной сложностью является то, что при упорядочивании его элементов нам приходится сравнивать строки, а&nbsp;на&nbsp;последних этапах суффиксы, находящиеся в&nbsp;суффиксном массиве достаточно близко, имеют достаточно длинные совпадающие начала. </p>
<p>Эта проблема решается, но&nbsp;пока что нас это интересовать не&nbsp;будет.</p>
</p>
<p>Возьмем строку <i>mississippi</i> (кстати, как раз алфавит из&nbsp;четырех символов!), и&nbsp;рассмотрим её суффиксный массив: </p>
<p><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree1.png" width="200" /></p>
<p>Теперь представьте, как мы&nbsp;<a href="http://ru.wikipedia.org/wiki/Суффиксный_массив">будем искать</a> в&nbsp;нем строку&nbsp;&mdash; мы&nbsp;будем применять бинарный поиск, каждый раз сравнивая нашу строку с&nbsp;очередным суффиксом.<br />
Строка длинная, её суффиксный массив, соответственно, тоже&nbsp;&mdash; а&nbsp;значит, кэш нам уже вряд&nbsp;ли поможет. Хотелось&nbsp;бы завести простую структуру, которая смогла&nbsp;бы нам указать небольшой сегмент, где нам стоит искать нашу подстроку.</p>
<p>Решение в&nbsp;виде следующей структуры-подсказки очевидно:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree2.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree2-300x207.png" alt="Структура-подсказка" width="300" height="207" class="alignnone size-medium wp-image-2007642" /></a></p>
<p>Структура просто показывает сегмент суффиксного массива, где находятся суффиксы, начинающиеся с&nbsp;определенного символа.</p>
<p>Давайте вспомним, что нам необходимо найти совпадение длины M. Заменим суффиксный массив на&nbsp;массив, &laquo;элементами&raquo; которого будут подстроки длины M. Он&nbsp;несильно отличается от&nbsp;суффиксного массива, как и&nbsp;техника его построения.<br />
В&nbsp;дальнейшем будем его называть <strong>s-массив</strong>.</p>
<p>s-массив сроки <i>missmississippi</i> (внимание&nbsp;&mdash; мы&nbsp;удлинили строку) для M=4 будет выглядеть так:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/streesarray.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/streesarray-184x300.png" alt="s-массив" width="184" height="300" class="alignnone size-medium wp-image-2007651" /></a></p>
<p>Теперь структуру-подсказку можно немного модифицировать, и&nbsp;она может подсказывать, используя, скажем, две-три первых буквы строки:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree41.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree41.png" alt="" width="500" class="alignnone size-full wp-image-2007645" /></a></p>
<p>Ещё один очевидный плюс от&nbsp;использования подсказки&nbsp;&mdash; мы&nbsp;можем теперь не&nbsp;сравнивать первые символы с&nbsp;элементами s-массива, поскольку мы&nbsp;уже установили, что они совпадают.</p>
<p>Однако что нам мешает сделать подсказку длины M? Понятно, что при M&gt;17, оперативной памяти может уже не&nbsp;хватить. Но, скажем, подсказку длины 14 построить можно.Ответ состоит в&nbsp;весьма возможной неравномерности распределения подстрок, то&nbsp;есть подстроки какого-то вида встречаются сильно чаще других. Это довольно характерно для генетических последовательностей. </p>
<p>В&nbsp;такой ситуации вероятен сценарий, когда&nbsp;б<em>о</em>льшая часть элементов длинной подсказки ведут к&nbsp;пустым сегментам, а&nbsp;часть&nbsp;&mdash; к&nbsp;сегментам большой длины, где поиск становится неэффективным (симптомы такого поведения заметны на&nbsp;предыдущей схеме).</p>
<p>Решение напрашивается: введем структуру-подсказку для длинных сегментов. Таким образом, структуры-подсказки образуют дерево, которое мы&nbsp;и&nbsp;назовем <strong>s-деревом</strong>. (В&nbsp;примере подсказки используют только один символ из&nbsp;соображений наглядности&nbsp;&mdash; ничто не&nbsp;мешает корню s-дерева использовать на&nbsp;реальных данных сразу 5 символов для подсказки, и&nbsp;это весьма эффективно). Количество символов,используемых подсказкой, назовем <strong>количеством ярусов</strong> узла s-дерева.</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree7.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree7.png" alt="Двухуровневое s-дерево" width="600" class="alignnone size-full wp-image-2007649" /></a></p>
<h2>Паралельное построение s-дерева и&nbsp;s-массива.</h2>
<p>Ранее мы&nbsp;подразумевали, что s-массив уже построен, и&nbsp;мы&nbsp;лишь строим структуру для ускорения поиска в&nbsp;нем. Однако все ещё интереснее&nbsp;&mdash; s-дерево и&nbsp;будет помогать строить s-массив. </p>
<p>Изначально произвольным образом инициализируем s-массив:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree3.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree3-213x300.png" alt="Инициализированный s-массив" width="213" height="300" class="alignnone size-medium wp-image-2007643" /></a></p>
<p>И&nbsp;начнем строить дерево от&nbsp;корня: </p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree5.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree5.png" alt="Развернули корневой элемент s-дерева" width="500" class="alignnone size-full wp-image-2007647" /></a></p>
<p>Заметим, что фактически мы&nbsp;разбили элементы s-массива по&nbsp;первому символу. Эта процедура проводится сортировкой подсчетом (независимо от&nbsp;того, сколько ярусов имеет узел) и&nbsp;занимает линейное время. Заметим попутно, что трудность, связанная с&nbsp;необходимостью посимвольного сравнения достаточно близких суффиксов, исчезла автоматически. </p>
<p>Далее продолжаем иерархически упорядочивать s-массив: </p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree6.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/stree6-1024x666.png" alt="После разворачивания узлов" width="600" class="alignnone size-large wp-image-2007648" /></a></p>
<p>На&nbsp;схеме выше мы&nbsp;<strong>развернули</strong> узлы <em>i</em> и&nbsp;<em>s&nbsp;</em>. То&nbsp;есть будем считать, что раньше эти узлы были <em>неразвернуты</em>, а&nbsp;теперь&nbsp;&mdash; <em>развернуты</em>. </p>
<p>В&nbsp;нашем примере увеличивать глубину дерева особенного смысла нет. Обратите внимание на&nbsp;то, что наш массив ещё не&nbsp;до&nbsp;конца упорядочен:</p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/streealgvsarray.png"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/streealgvsarray.png" alt="Сравнение." width="700" class="alignnone size-full wp-image-2007650" /></a></p>
<p>Можно провести упорядочивание до&nbsp;конца, отсортировав элементы в&nbsp;одном листе, но&nbsp;мы&nbsp;это не&nbsp;делали. </p>
<p>Таким образом, процедура поиска строки в&nbsp;s-дереве довольно проста </p>
<ol>
<li>Иерархически идем по&nbsp;дереву, пока это возможно.</li>
<li>Если мы&nbsp;дошли до&nbsp;листа, досравниваем свою строку со&nbsp;всеми	элементами суффиксного массива, относящимися к&nbsp;данному листу. Досравниваем&nbsp;&mdash; потому что начала нам уже сравнивать не&nbsp;надо.</li>
</ol>
<h2>Оптимизации</h2>
<p>Наверное, у&nbsp;читателя пока что складывается ощущение, что такая структура крайне неповоротлива. Покажем, как её можно адаптировать под разные ситуации и&nbsp;ускорить.</p>
<h4>Оптимизация 1. Ленивость.</h4>
<p>Предположим, что у&nbsp;нас действительно большая строка, для которой мы&nbsp;строим s-дерево. Если число строк, которые мы&nbsp;будем искать, существенно меньше длины исходной строки, то&nbsp;у&nbsp;s-дерева, вероятно, появится много узлов, которые в&nbsp;процедуре сравнения вообще не&nbsp;будут использоваться. Получается, что при построении мы&nbsp;сделали массу ненужной работы. </p>
<p>Давайте будем строить s-дерево лениво, то&nbsp;есть по&nbsp;требованию: скажем, мы&nbsp;построили уже корень s-дерева, и&nbsp;нам приходит запрос найти в&nbsp;дереве строку issi. В&nbsp;этот момент мы&nbsp;<strong>разворачиваем</strong> узел <em>i</em>. Если нам не&nbsp;пришлось искать строку, начинающуюся на&nbsp;<em>s</em>, узел <em>s</em> так и&nbsp;останется неразвернутым. </p>
<h4>Оптимизация 2. Многопоточность.</h4>
<p>На&nbsp;самом деле ту&nbsp;часть дерева, которая почти наверняка нам пригодится, мы&nbsp;построим заранее, причем это будет делаться сразу всеми доступными потоками. В&nbsp;частности, одно удовольствие строить корень s-дерева сразу многими потоками, потому что алгоритм сортировки подсчетом прекрасно распараллеливается. </p>
<p>После того, как эта часть дерева построена, все потоки начинают искать в&nbsp;дереве какие-то строки, если какой-то поток приходит в&nbsp;неразвернутый узел, он&nbsp;блокирует узел и&nbsp;разворачивает его (блокирует, чтобы другие потоки не&nbsp;пытались инициализировать узел в&nbsp;то&nbsp;же время). </p>
<h4>Оптимизация 3. Хранение строк.</h4>
<p>Обе строки будем хранить в&nbsp;массиве из&nbsp;int16 в&nbsp;следующем формате:<br />
каждому элементу в&nbsp;массиве будет соответствовать один элемент int16, который будет хранить в&nbsp;запакованном виде сам символ и&nbsp;семь, следующих за&nbsp;ним:
</p>
<pre style="font-family: monospace">

A  G  T  C  C  T  C  A  T     &lt;- cтрока
00 01 10 11 11 10 11 00 10    &lt;- коды символов

00 01 10 11 11 10 11 00       &lt;- бинарное представление первого элемента массива
   01 10 11 11 10 11 00 10    &lt;- второго
      10 11 11 10 11 00 10 00 &lt;- третьего (пустое место заполнили нулями)
                                 и т.д.
</pre>
<p></p>
<p>Подобный метод хранения данных позволяет быстро запрашивать от&nbsp;одного до&nbsp;восьми бит, а&nbsp;значит </p>
<ul>
<li>Быстро проводить подсчет в&nbsp;сортировке подсчетом по&nbsp;первым n&nbsp;символам,	если n&lt;9 </li>
<li>Быстро передвигаться при движении по&nbsp;s-дереву, когда для	передвижения в&nbsp;следующую вершину нам надо считать несколько символов.</li>
</ul>
<h4>Оптимизация 4. Эвристика совпавшего суффикса.</h4>
<p>Собственно, эвристика уже не&nbsp;связана напрямую с&nbsp;s-деревом, но&nbsp;имеет отношение к&nbsp;решению задачи.</p>
<p>Зададимся вопросом, как мы&nbsp;будем искать совпадения с&nbsp;другой строкой (например, <em>iispimispispi</em>). Мы&nbsp;будем двигаться с&nbsp;конца второй строки. Предположим, что мы&nbsp;ищем с&nbsp;указанного места совпадение минимальной длины M=4 с&nbsp;уже рассмотренной строкой <em>missmississippi</em>.
</p>
<pre style="font-family: monospace">
       &#8595;
iispimispispi
</pre>
<p></p>
<p>В&nbsp;процессе поиска в&nbsp;s-дереве мы&nbsp;выясним, что строка<br />
<em>missmississippi</em> не&nbsp;содержит даже подстроки <em>sp</em>. Очевидно, что мы&nbsp;уже не&nbsp;найдем совпадений длины 4, если начнем поиск с&nbsp;указанных позиций: </p>
<pre style="font-family: monospace">
     &#8595;&#8595;
iispimispispi
</pre>
<p></p>
<p>поэтому мы&nbsp;можем сразу сдвинуться на&nbsp;три позиции влево: </p>
<pre style="font-family: monospace">
    &#8595;
iispimispispi
</pre>
<p></p>
<p>
При достаточно большом параметре M&nbsp;это очень сильно ускоряет поиск, потому что мы&nbsp;будем смещаться почти на&nbsp;величину M. (Мы&nbsp;бы даже рискнули предположить, что все, кто окажется на&nbsp;пьедестале, будут использовать эту эвристику или подобную ей).
</p>
<p>Для тех, кто хочет поломать свою голову: </p>
<pre style="font-family: monospace">
    &#8595;
mmippi
</pre>
<p></p>
<p>В&nbsp;указанной выше ситуации мы&nbsp;<strong>не&nbsp;найдем</strong> совпадение <em>ippi</em> со&nbsp;строкой <em>missmissisippi</em>, если будем пользоваться построенным нами деревом. Задача: почему и&nbsp;как это исправить? </p>
<h2>Заключение </h2>
<p>Пожалуй, излагать ещё какие-то подробности будет излишним. </p>
<p>Подведем итог: </p>
<ol>
<li>Мы&nbsp;посмотрели на&nbsp;структуру, которую мы&nbsp;назвали s-деревом	<br />
	(вполне возможно, что она была уже раз десять изобретена до&nbsp;нас, но&nbsp;пока	мы&nbsp;об&nbsp;этом не&nbsp;в&nbsp;курсе:))</li>
<li>Посмотрели на&nbsp;процедуру построения этого дерева, которая может производиться	лениво&nbsp;&mdash; по&nbsp;мере надобности, а&nbsp;также хорошо распараллеливается.	</li>
<li>Познакомились с&nbsp;некоторыми приемами ускорения построения и&nbsp;использования	дерева
<ul type="disc">
<li>Ленивостью, которая позволяет экономить при малом количестве строк,	поданных на&nbsp;поиск в&nbsp;дереве <br />
	(переводя на&nbsp;язык	<a href="http://software.intel.com/ru-ru/articles/contest-accelerate-2012-problem/">	условий задачи</a>, при длине reference сильно больше длины input) </li>
<li>Эвристикой совпавшего суффикса, которая позволяет нам ускоряться при	большом значении параметра M</li>
<li>Так как при малой длине референсной строки дерево мало, мы&nbsp;защитились	от&nbsp;самых разных &laquo;перекосов&raquo; во&nbsp;входных данных.	</li>
</ul>
</li>
<li>Планируем поделиться умными мыслями, появившимися в&nbsp;процессе чтения статьи, в&nbsp;комментариях.	Да, это приглашение:)</li>
</ol>
<p>PS. Почему s-массив и&nbsp;s-дерево? Потому что почти суффиксные, но&nbsp;не&nbsp;совсем.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/18/s/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Модификация Алгоритма Рабина-Карпа для поиска общих подстрок</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/17/2007628/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/17/2007628/#comments</comments>
		<pubDate>Thu, 17 May 2012 12:16:38 +0000</pubDate>
		<dc:creator>yvanko</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[accelerate 2012]]></category>
		<category><![CDATA[Intel TBB]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/17/2007628/</guid>
		<description><![CDATA[Вот и подошел к концу период отправки решений на конкурс Accelerate Your Code 2012 и мы наконец-то можем поделиться своими идеями, использованными в решении задачи и пригласить остальных участников к обсужднию. Задача состояла в том, чтобы найти общие подстроки длины не менее N из заданной ref строки (представляющей собой участок ДНК и потом состоящей только [...]]]></description>
			<content:encoded><![CDATA[<p>Вот и подошел к концу период отправки решений на конкурс <a href="http://software.intel.com/fr-fr/articles/AYC-early2012_home/">Accelerate Your Code 2012 </a>и мы наконец-то можем  поделиться своими идеями, использованными в решении задачи и пригласить остальных участников к обсужднию.</p>
<p>Задача состояла в том, чтобы найти общие подстроки длины не менее N  из заданной ref строки (представляющей собой участок ДНК и потом состоящей только из символов  A, G, C, T) и некоторого набора in строк (найти нужно именно подстроки, не подпоследовательности, то есть связные общие части). Мы не сразу поняли одну особенность этого конкурса: здесь фактически нужно было не написать программу, полностью соответствующую описанию, а скорее оптимизировать <a href="http://intel-software-academic-program.com/contests/ayc/early2012/ayc.zip">прилагающееся референсное решение</a>, так как сама постановка была нечеткой, а на форуме мы получили ответ, что программа должна  дать абсолютно такой же результат как и референсный код. Ну что же, задание необычное, но тем более интересное.</p>
<p>Для начала проанализируем референсное решение. В исходном алгоритме для каждой in-строки динамически строится матрица <code>L[i][j]</code> - максимальная длина общей подстроки в ref и in строках, с позициями конца подстроки <code>i</code> и <code>j</code> соответственно.  После построения этой матрицы достаточно найти все элементы со значением не менее <code>N</code>. Также в референсном решении отсекаются случаи, когда можно сдвинуть правый конец одной или обоих подстрок на 1 вправо с неуменьшением длины общей подстроки.</p>
<p>Главная проблема исходного решения в том, что сложность и требование к памяти в этом решении – квадратичные. Это значит, что если входные данные будут иметь максимальный размер (по четыре миллиарда символов), то время исполнения будет исчисляться годами, не говоря уже про потребляемую память, поэтому в первую очередь мы стали искать более эффективный алгоритм. Существует <a href="http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A0%D0%B0%D0%B1%D0%B8%D0%BD%D0%B0_%E2%80%94_%D0%9A%D0%B0%D1%80%D0%BF%D0%B0">Алгоритм Рабина — Карпа</a> поиска подстроки в строке, вкратце его суть в том, что имея длинную строку и образец длины <code>m</code>, мы находим хеш подстроки и хеши всех подстрок длины <code>m</code> в большой строке. При аккуратном использовании хеш-функции (нужно считать новый хеш, исходя из старого за О(1), а не за O(m) ) </p>
<pre name="code" class="cpp">
h -= rem * int64(s[i-1]);
h = h * MOD + int64(s[i+minMatchLength-1]);
hash[i] = h;
</pre>
<p>мы потратим в среднем О(m+n) времени, просто сравнивая все хеши с хешем образца. Недостатком этого алгоритма является то, что в «плохих» случаях он может работать за квадратичное время, однако на форуме мы получили ответ, что плохих случаев в тестах не будет, что повлияло на выбор этого алгоритма. </p>
<p>Для данной конкретной задачи, конечно, пришлось делать модификации. Мы можем найти все хеши подстрок длины <code>N</code> из in строки и поместить их в хеш-таблицу, этот процесс можно распараллелить через библиотеку TBB. Далее мы можем пройтись по подстрокам длины <code>N</code> из ref строки и сверять их хеш с таблицей. При этом вставка в хеш-таблицу медленнее, чем проверка, поэтому заметим, что задача практически симметрична относительно ref и in строк, поэтому если ref строка длиннее, то создадим хеш таблицу для in строки и наоборот. Когда мы находим совпадение хешей, то находим наибольшую общую подстроку (зная начальные индексы) и запоминаем этот ответ. Есть еще небольшая сложность связанная с тем, что ответы получатся не в том порядке в котором они идут в референсном решении, поэтому придется еще применить сортировку для ответа, однако это не должно сильно замедлить алгоритм.</p>
<p>В итоге получается, что сложность этого алгоритма составляет O(N + KlogK), где N – суммарный размер входа, K – размер выхода, что уже является практически линейным решением.</p>
<p>Хотелось бы услышать комментарии, замечания или вопросы и от остальных участников и конечно, делитесь вашими идеями!</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/17/2007628/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Коллекция интересных ресурсов по тематике программирования на языке Си/Си++</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/16/2007556/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/16/2007556/#comments</comments>
		<pubDate>Wed, 16 May 2012 09:13:49 +0000</pubDate>
		<dc:creator>Andrey Karpov</dc:creator>
				<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[си плюс плюс]]></category>
		<category><![CDATA[Си++]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/16/2007556/</guid>
		<description><![CDATA[Если вы присутствуете на одном из этих сайтов, то приглашаю следовать за мной. Со временем вам попадется немало интересных статей. А чтобы заинтересовать вас, я решил сделать небольшую подборку материалов.]]></description>
			<content:encoded><![CDATA[<p>Я постоянно изучаю интернет на тему новых статей про программированию на языке Си/Си++/Си++11. Если мне они кажутся интересными, я делюсь ссылками на них в твиттере <a href="https://twitter.com/Code_Analysis" target="_blank">@Code_Analysis</a>, в реддите <a href="http://www.reddit.com/r/viva64/" target="_blank">/r/Viva64</a> и голосую в <a href="http://www.stumbleupon.com/stumbler/AndreyKarpov2010" target="_blank">StumbleUpon</a>. Если вы присутствуете на одном из этих сайтов, то приглашаю следовать за мной. Со временем вам попадется немало интересных статей. А чтобы заинтересовать вас, я решил сделать небольшую подборку материалов.</p>
<p>В первую очередь я, конечно, хочу порекомендовать наш сайт viva64.com. Здесь можно найти много <a href="http://www.viva64.com/ru/articles/">статей</a> и записей в <a href="http://www.viva64.com/ru/b/">блоге</a>, касающихся разработке качественного кода. Например, предлагаю познакомиться с <a href="http://www.viva64.com/ru/l/">28 уроками</a>, посвященными разработке 64-битных приложений на языке Си/Си++.</p>
<p>Для удобства я разбил ссылки на несколько тем. Желаю приятного чтения.</p>
<p><i>Примечание. Как правило, мы оборачиваем ссылки, чтобы контролировать их целостность. Про это бесстрашное сражение с хаосом, я писал в статье "<a href="http://www.viva64.com/ru/b/0075/"><i>Д'Артаньян и интернет, или работа над проблемой битых ссылок</i></a>". К сожалению, в этот раз я буду давать прямые ссылки. Эти материалы существенно увеличат базу ссылок, а значит, существенно прибавят нам работы в случае перемещения статей.</i></p>
<h2>Си++ живее всех живых</h2>
<ul>
<li>Большой список известных проектов, которые разрабатываются на языке Си/Си++: <a href="http://www2.research.att.com/~bs/applications.html" target="_blank">C++ Applications</a>. И после этого кто-то говорит, что язык Си++ вымирает?</li>
<li><a href="http://shootout.alioth.debian.org/u32/which-programming-languages-are-fastest.php" target="_blank">Computer Language Benchmarks Game</a>. Как всегда по скорости лидируют языки Fortran, Си и Си++. И вряд ли что-то в обозримом времени изменится. Если вы разрабатываете ресурсоемкое программное обеспечение, то Си++ может оказаться оптимальным выбором.</li>
<li><a href="http://quant.stackexchange.com/questions/1764/why-is-c-still-a-very-popular-language-in-quantitative-finance">Why is C++ still a very popular language in quantitative finance?</a></li>
<li><a href="http://lambda-the-ultimate.org/node/663">Why do they program in C++?</a></li>
<li><a href="http://www.cpprocks.com/2012/05/07/9-reasons-to-start-using-c11/">9 reasons to start using C++11</a></li>
</ul>
<h2>Интересные блоги</h2>
<ul>
<li><a href="http://stevehanov.ca/blog/" target="_blank">Steve Hanov's Blog</a></li>
<li><a href="http://blogs.msdn.com/b/vcblog/" target="_blank">Visual C++ Team Blog</a></li>
<li><a href="http://blogs.msdn.com/b/oldnewthing/" target="_blank">The Old New Thing</a></li>
</ul>
<h2>Арифметика с плавающей запятой</h2>
<ul>
<li><a href="http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html">What Every Computer Scientist Should Know About Floating-Point Arithmetic</a></li>
<li><a href="http://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format/">Tricks With the Floating-Point Format</a></li>
<li><a href="http://www.altdevblogaday.com/2012/01/21/stupid-float-tricks/" target="_blank">Stupid Float Tricks</a></li>
<li><a href="http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/" target="_blank">Comparing Floating Point Numbers</a>, 2012 Edition</li>
</ul>
<h2>Разные приёмы при программировании</h2>
<ul>
<li><a href="http://graphics.stanford.edu/~seander/bithacks.html" target="_blank">Bit Twiddling Hacks</a></li>
<li><a href="http://www.macieira.org/blog/2011/07/table-driven-methods-with-no-relocations/">Table-driven methods with no relocations</a></li>
<li><a href="">Reduce Compilation Dependencies</a> in Large Scale C++ Projects: Factory Pattern</li>
<li><a href="http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html">Exceptions in C with Longjmp and Setjmp</a></li>
<li>Catching Integer Overflows in C. Article <a href="http://www.fefe.de/intof.html">N1</a>, <a href="">N2</a></li>
<li><a href="http://www.endofunctor.org/~rpearl/blog/cool-c-tricks.html" target="_blank">Cool C Tricks</a></li>
</ul>
<h2>О кодировках символов и управляющих символах</h2>
<ul>
<li><a href="http://triptico.com/docs/unicode.html">Unicode, UTF-8 and all that</a>: An intentionally incomplete character set introduction for hasty C programmers</li>
<li><a href="http://code.alexreisner.com/articles/character-encoding.html">What the xxxx Is UTF-8?</a> A Character Encoding Primer.</li>
<li><a href="http://www.oocities.org/dtmcbride/tech/charsets/ascii.html" target="_blank">ASCII Characters</a></li>
<li><a href="http://en.wikipedia.org/wiki/Control_character" target="_blank">Control character</a></li>
<li>The <a href="http://www.utf8everywhere.org/" target="_blank">UTF-8 Everywhere</a> manifesto</li>
</ul>
<h2>Стандарты кодирования</h2>
<ul>
<li><a href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml" target="_blank">Google C++ Style Guide</a></li>
<li>CERT. <a href="https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Secure+Coding+Standard">C Secure Coding Standard</a>, <a href="">C++ Secure Coding Standard</a></li>
<li><a href="http://www2.research.att.com/~bs/JSF-AV-rules.pdf">Joint Strike Fighter (F-35) C++ Coding Standard</a></li>
</ul>
<h2>Группы</h2>
<ul>
<li>Reddit: <a href="http://www.reddit.com/r/cpp/" target="_blank">Cpp</a>, <a href="http://www.reddit.com/r/C_Programming/" target="_blank">C_Programming</a>.</li>
<li>Facebook: <a href="http://www.facebook.com/groups/CPPProgramming/">C++ Programming</a>, <a href="">For the love of C++</a>, <a href="http://www.facebook.com/groups/11366002065/">C Programming</a>, <a href="http://www.facebook.com/groups/2248174480/">Visual C++ Developer</a>, <a href="http://www.facebook.com/groups/2204920930/">C Plus Plus (C++)</a></li>
<li>LinkedIN: <a href="http://www.linkedin.com/groups?home=&amp;gid=133720&amp;trk=anet_ug_hm">C++ Community Group</a>, <a href="http://www.linkedin.com/groups?home=&amp;gid=87434&amp;trk=anet_ug_hm">C++ Developers Forum</a>, <a href="">C++ Developers Group</a>, <a href="http://www.linkedin.com/groups?home=&amp;gid=100895&amp;trk=anet_ug_hm">C++ Professionals</a></li>
</ul>
<h2>Разное</h2>
<ul>
<li><a href="http://www.conifersystems.com/whitepapers/gnu-make/">What's Wrong With GNU make?</a></li>
<li>Почему strncpy не безопаснее strcpy: <a href="http://blog.liw.fi/posts/strncpy/">strncpy? just say no</a></li>
<li>A Guide to Undefined Behavior in C and C++. Part <a href="http://blog.regehr.org/archives/213" target="_blank">N1</a>, <a href="http://blog.regehr.org/archives/226" target="_blank">N2</a>, <a href="http://blog.regehr.org/archives/232" target="_blank">N3</a></li>
<li>Компиляция 64-битных приложений в Visual C++ 2008 Express Edition. <a href="http://jenshuebel.wordpress.com/2009/02/12/visual-c-2008-express-edition-and-64-bit-targets/" target="_blank">Visual C++ 2008 Express Edition And 64-Bit Targets</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/aa290049%28VS.71%29.aspx">Windows Data Alignment on IPF, x86, and x64</a></li>
<li>C/C++ Low Level Curriculum. Part <a href="http://www.altdevblogaday.com/2011/11/09/a-low-level-curriculum-for-c-and-c/">N1</a>, <a href="http://www.altdevblogaday.com/2011/11/24/c-c-low-level-curriculum-part-2-data-types/">N2</a>, <a href="">N3</a>, <a href="http://www.altdevblogaday.com/2011/12/24/c-c-low-level-curriculum-part-4-more-stack/">N4</a>, <a href="">N5</a>, <a href="http://www.altdevblogaday.com/2012/03/07/c-c-low-level-curriculum-part-6-conditionals/">N6</a>, <a href="http://www.altdevblogaday.com/2012/04/10/cc-low-level-curriculum-part-7-more-conditionals/">N7</a>, <a href="">N8</a></li>
<li>Ссылки на различные ресурсы по тематике Си++. <a href="Internet%20sites%20and%20files%20of%20interest%20to%20C++%20users">Список</a> ссылается на много старых ресурсов, но его стоит посмотреть.</li>
<li><a href="http://www.altdevblogaday.com/2012/04/26/functional-programming-in-c/" target="_blank">Functional Programming in C++</a></li>
<li><a href="http://goldns.ru/cppmap-2012.png" target="_blank">The C++ Lands</a> (забавная картинка)</li>
<li><a href="http://www.cdecl.org/" target="_blank">Translate</a> C declaration</li>
<li><a href="http://dashmorethan.wordpress.com/2012/03/26/arbitrary-data-sizes/" target="_blank">Arbitrary Data Sizes with C</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/16/2007556/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Справочное руководство Intel® TBB – теперь в HTML!</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/15/intel-tbb-html/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/15/intel-tbb-html/#comments</comments>
		<pubDate>Tue, 15 May 2012 08:12:43 +0000</pubDate>
		<dc:creator>Vladimir Polin (Intel)</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Intel TBB]]></category>
		<category><![CDATA[Intel® Threading Building Blocks]]></category>
		<category><![CDATA[TBB]]></category>
		<category><![CDATA[Threading Building Blocks]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/15/intel-tbb-html/</guid>
		<description><![CDATA[У нас в Intel® TBB хорошая новость: Справочное руководство Intel® TBB, больше известное обществу как Reference.pdf, теперь доступно в HTML версии на нашем опен-сорс сайте. Добро пожаловать! Так же на страничке документации, как обычно, документы из последнего обновления и выложен новый вариант doxygen-документации. --Владимир]]></description>
			<content:encoded><![CDATA[<p>У нас в Intel® TBB хорошая новость: Справочное руководство Intel® TBB, больше известное обществу как Reference.pdf, теперь доступно в HTML версии на нашем опен-сорс сайте.</p>
<p><a href="http://threadingbuildingblocks.org/docs/help/">Добро пожаловать!</a></p>
<p>Так же на страничке документации, как обычно, документы из последнего обновления и выложен новый вариант <a href="http://threadingbuildingblocks.org/docs/doxygen/">doxygen-документации</a>.</p>
<p>--Владимир</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/15/intel-tbb-html/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>А Вы, случайно, не Цезарь?</title>
		<link>http://software.intel.com/ru-ru/blogs/2012/05/12/2007342/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2012/05/12/2007342/#comments</comments>
		<pubDate>Sat, 12 May 2012 08:12:27 +0000</pubDate>
		<dc:creator>andreythestudent</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[Accelerate]]></category>
		<category><![CDATA[accelerate 2012]]></category>
		<category><![CDATA[Intel TBB]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2012/05/12/2007342/</guid>
		<description><![CDATA[Я вот, например - нет. Я имею в виду, что делать одновременно несколько дел, подобно известному римскому императору, не умею. Ну разве что самых элементарных (например, смотреть телевизор и завтракать). Но я не умею делать одновременно два или более дела, требующих обдумывания каждого из них - например, я не могу одновременно читать один текст и [...]]]></description>
			<content:encoded><![CDATA[<p>Я вот, например - нет. Я имею в виду, что делать одновременно несколько дел, подобно известному римскому императору, не умею. Ну разве что самых элементарных (например, смотреть телевизор и завтракать). Но я не умею делать одновременно два или более дела, требующих обдумывания каждого из них - например, я не могу одновременно читать один текст и писать другой или участвовать в разговоре и решать задачу. Возможно, именно поэтому мне длительное время совсем не давалось параллельное программирование. Я понимал общие принципы, знал все нужны функции, но никак не мог построить в голове общую картину происходящего. У меня, получается, есть много потоков, которые что-то делают. Да еще часто с одними и теми же данными. При этом не понятно с какой скоростью они это делают, кому ОС отдаст больше времени, когда будет переключаться с одного потока на другой. Я ощущал себя кем-то вроде художника, который раньше рисовал картины классическим способом "1 мольберт - 1 кисть - 1 палитра", а тут ему заказали 100 картин, приделали сотню рук и сказали - рисуй, давай. При этом голова по-прежнему осталась одна и как держать в фокусе 100 объектов и координировать 100 действий одновременно - непонятно. Я чувствовал, что что-то здесь не так. Должен был быть другой путь.</p>
<p><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/tbb-150x150.jpg" alt="" width="120" height="120" class="alignnone size-thumbnail wp-image-2007343" align="right" />И он нашелся. Им оказалась библиотека Intel TBB с её концепцией планировщика задач (Task Scheduler). Для тех, кто с TBB не знаком я коротко объясню суть этого паттерна: вы абстрагируетесь от количества логических\физических процессоров, от числа потоков операционной системы и создаёте свой алгоритм в терминах "задач". Задаче передаётся что-то на вход, она что-то делает и возвращает что-то на выход. Задач может быть много (сильно больше числа процессоров), они могут быть независимы друг от друга, или порождаться друг другом. </p>
<p>На первый взгляд может показаться, что всё это ничем не отличается от классических потоков со всеми вытекающими плюсами и минусами. Но это только на первый взгляд. Копнув глубже, я понял, что уже не чувствую себя художником из примера выше. Я чувствую себя владельцем крупной арт-мастерской. Мне ничего не нужно делать самому! У меня есть некоторые производственные мощности и очень грамотный управляющий. Когда мне приходит заказ на 100 картин, я вызываю его к себе и говорю : "Вот что надо сделать. Вот ресурсы. Вот инструкции по работе. Вперед!". И дальше я могу удаляться на свою любимый тропический остров лежать в шезлонге, попивая мартини, и знать, что работа будет выполнена. Управляющий сам оценит объем работ и имеющиеся ресурсы (CPU), наймет нужное количество работников (Tasks), проследит, чтобы каждому из них достались средства для работы и достачный (но посильный) объем задач. При этом по ходу работы он будет контролировать нагрузку, перекидывать задачи и ресурсы для достижения равномерной загруженности, подстёгивать лентяев и разгружать перегруженных трудяг. Вся работа будет выполнена строго по моим инструкциям: что должно делаться последовательно - будет сделанно именно так, что может быть распараллелено - обязательно будет. Более того, я уверен, что этот управляющий сделает всё вышеуказанное намного лучше меня - ведь на его создание и обучение очень много умных людей (сильно умнее меня) потратили очень много времени. Вряд ли я обладаю знаниями того же уровня для координации всей этой работы. </p>
<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/intel_tbb21_before.jpg"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/intel_tbb21_before-300x199.jpg" alt="" width="300" height="199" class="alignnone size-medium wp-image-2007350" /></a></p>
<p>Лично мне такой подход к параллельному программированию нравится намного больше. Я не привязан к конкретным потокам, я не сильно беспокоюсь о низкоуровневых деталях. Конечно, кое-какие заморочки с синхронизацией остались (куда же без них!), но воспринимаются они совсем по-другому. В общем, я остался приятно удивлен библиотекой Intel TBB и советую всем, кто с ней еще не знаком - обязательно взглянуть.</p>
<p>Начать читать можно где-то <a href="http://threadingbuildingblocks.org/documentation.php">отсюда</a>.<br />
Советую начинать с <a href="http://threadingbuildingblocks.org/uploads/81/91/Latest%20Open%20Source%20Documentation/Tutorial.pdf">Tutorial</a> - там всё коротко и весьма понятно. Если решите использовать библиотеку серьёзно - читайте также <a href="http://threadingbuildingblocks.org/uploads/81/91/Latest%20Open%20Source%20Documentation/Reference.pdf">полную документацию</a> и <a href="http://threadingbuildingblocks.org/uploads/81/91/Latest%20Open%20Source%20Documentation/Design_Patterns.pdf">Design Patterns</a> </p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2012/05/12/2007342/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

