<?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; wadsl</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/author/wadsl/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>Конкурс Acceler8 - оптимизация работы с памятью</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/11/29/acceler8/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/11/29/acceler8/#comments</comments>
		<pubDate>Tue, 29 Nov 2011 13:16:34 +0000</pubDate>
		<dc:creator>wadsl</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[оптимизация]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/11/29/acceler8/</guid>
		<description><![CDATA[Совсем недавно закончился конкурс Acceler8 2011, в котором я принимал участие. В данном посте я бы хотел поделиться, какие шаги были сделаны в плане оптимизаций работы с памятью в процессе выполнения конкурсной задачи.]]></description>
			<content:encoded><![CDATA[<p><a href="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/out-of-memory-feels.jpg"><img src="http://software.intel.com/ru-ru/blogs/wordpress/wp-content/uploads/out-of-memory-feels-300x214.jpg" alt="" width="300" height="214" class="alignleft size-medium wp-image-2006153" /></a>Совсем недавно закончился конкурс Acceler8 2011, в котором я принимал участие. В данном посте я бы хотел поделиться, какие шаги были сделаны в плане оптимизаций работы с памятью в процессе выполнения конкурсной задачи.</p>
<p>Для поиска подматрицы с максимальной суммой элементов в матрице наша команда выбрала адаптированный одномерный алгоритм, предложенный Джеем Кадане в 1977 году. Насколько нам известно, для двумерного случая решения с помощью этого алгоритма не существует, или мы о нём пока не знаем. Поэтому пришлось адаптировать имеющийся одномерный алгоритм. Изменение было в следующем: мы складывали нужное количество строк матрицы в массив-аккумулятор и осуществляли поиск в нём с помощью известного алгоритма Кадане для числовых последовательностей.</p>
<p>Рассмотрим пример: пусть есть матрица 10 на 10. Сначала осуществляется одномерный поиск максимальной подматрицы в каждой из строк. Далее строки складываются попарно в массив-аккумулятор, и ищется максимальная подматрица в аккумуляторе. Т.е. складываем строки 0 и 1, ищем максимальную подматрицы, складываем строки 1 и 2, ищем максимальную подматрицу и т.д. Заканчивается последней подматрицей 10 на 10, которая совпадает с исходной матрицей.</p>
<p>Далее в ход пошли оптимизации:</p>
<h2>1.	Переиспользование результатов.</h2>
<p>Помогает снизить интенсивность использования памяти и увеличить эффективность использования кэша. Представим, что в нашей матрице, описанной выше, мы уже добрались до сложения по 4 строки. Т.е. складываем строки 0, 1, 2 и 3, ищем максимальную подматрицы, складываем строки 1, 2, 3 и 4, ищем максимальную подматрицу и т.д. Можно заметить, некоторые строки мы складывали по нескольку раз. Получается, что достаточно из аккумулятора, оставшегося после предыдущего шага, вычесть первую строку, добавить следующую, и можно получить готовый аккумулятор для следующего шага. Т.е. для шага 4 складываем строки 0, 1, 2 и 3, ищем максимальную подматрицу, из аккумулятора вычитаем строку 0 и добавляем строку 4, ищем максимальную подматрицу. Тем самым уменьшается количество сложений. И чем больше высота подматрицы, которую мы ищем, тем большей экономии по количеству операций мы достигаем.<br />
А если переставить операции вычитания и прибавления строк местами, то мы получим, что в какой-то промежуточный момент в аккумуляторе находилась сумма 5 строк. Можно использовать и этот момент. В итоге получилось следующее: сложили 4 строки, поискали максимум по 4-м строкам, добавили следующую строку, поискали максимум по 5ти строкам, вычли верхнюю строку, поискали максимум по 4м строкам, добавили и т.д. Пока не дошли до последней строки матрицы, имея в аккумуляторе последние 4 строки.<br />
И даже этот результат можно переиспользовать – только теперь считать максимум по 6 и 7 сложенным строками и двигаться не вниз, а вверх.</p>
<p>В конечном итоге получилось следующее. На исходной матрице ищем максимумы по отдельным строкам, потом в большом цикле ищем максимумы по 2 и 3 сложенным строкам, двигаясь по матрице вниз, далее ищем максимум по 4 и 5 сложенным строкам, двигаясь вверх, далее ищем по 6 и 7 сложенным строкам, двигаясь вниз и т.д. Этим достигается огромная экономия операций обращений в память.</p>
<h2>2.	Уменьшение разрядности.</h2>
<p>Не знаем, желая того или нет (первоначально акцент в конкурсе делался именно на параллелизацию), но организаторы сами опубликовали генерирующую функцию, свойства которая и являются источником большинства самых эффективных оптимизаций.<br />
Первое, что можно было пронаблюдать, это что по формуле псевдо-случайного алгоритма генерации данных получалось, что все данные гарантированно лежат внутри диапазона от -20000 до 20000. После перехода с типа int на тип short int, уровень потребления памяти упал в 2 раза, а скорость генерации исходных матриц и скорость алгоритма подсчёта выросла от 10% до 50% в зависимости от размера матрицы. Опять же, это достигалось за счёт более эффективного использования кэша процессоров.</p>
<h2>3.	Переход на «индексированные» матрицы.</h2>
<p>Последнее улучшение, сделанное нашей командой для работы с памятью, был переход на «индексированные матрицы». Если взять достаточно большую матрицу  и развернуть её в строку, то мы заметим циклически повторяющиеся значения. Это связанно с особенностями генерации исходных данных. Представим, что наша матрица 10 на 10 состоит  из циклически повторяющихся значений -5,-3,-1,1,3,5,7, и каждая строка начинается в «произвольном» месте данной последовательности. Получается, что нет необходимости хранить всю матрицу. Достаточно хранить только одномерный массив с несколькими повторяющимися последовательностями, и отдельно массив со смещениями, где хранится отступ в массиве, откуда начинается каждая строка. Максимальный размер массива, необходимого для данного представления матрицы, равен m + ширина исходной матрицы + высота исходной матрицы. В реальности, он ещё меньше, но мы оценивали максимальный необходимый массив. С такой оптимизацией мы достигли 70% экономии места под хранение матрицы. На больших матрицах экономия достигала 99%, и была полностью исключена необходимость выделять 800Мб памяти под максимальные матрицы, допускаемые задачей. </p>
<p>Существенное уменьшение объёма памяти для хранения матрицы позволило более эффективно осуществить распараллеливание непосредственно по матрицам в тесте, но свойства архитектуры нашего решения не входят в рамки этого описания.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/11/29/acceler8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

