<?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; ronsenval</title>
	<atom:link href="http://software.intel.com/ru-ru/blogs/author/ronsenval/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>Алгоритм поиска максимальной подматрицы в периодической матрице со сложностью O(np)</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/11/29/onp/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/11/29/onp/#comments</comments>
		<pubDate>Tue, 29 Nov 2011 09:12:15 +0000</pubDate>
		<dc:creator>ronsenval</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Разработка софта]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[matrix]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/11/29/onp/</guid>
		<description><![CDATA[Пусть матрица называется периодической, если ее элементы являются циклическим повторением некоторой последовательности чисел. Задача: дана периодическая матрица произвольного размера, необходимо найти подматрицу, сумма элементов которой максимальна. В дальнейших рассуждениях используется матрица (конкретные числа заменены номерами): С чего начнем? Цикл в ней составляют элементы 1, 2, 3, 4, 5, 6. Строки начинаются только с 1, 3 [...]]]></description>
			<content:encoded><![CDATA[<div style="text-align: justify">Пусть матрица называется <strong>периодической</strong>, если ее элементы являются циклическим повторением некоторой последовательности чисел.</p>
<p><strong>Задача:</strong> дана периодическая матрица произвольного размера, необходимо найти подматрицу, сумма элементов которой максимальна.</p>
<p>В дальнейших рассуждениях используется матрица (конкретные числа заменены номерами):
<div style="text-align: center"><img src="http://i082.radikal.ru/1111/63/c53347818669.png" alt="" /></div>
<p><strong>С чего начнем?</strong><br />
Цикл в ней составляют элементы 1, 2, 3, 4, 5, 6. Строки начинаются только с 1, 3 и 5. Для начала будем искать подматрицу высотой 1. Начнем искать лучшую подматрицу в первой строке (или с первого элемента цикла, что тоже самое), как в алгоритме Кадане. Когда мы дойдем до третьего элемента, начнем одновременно считать сумму и для второй строки. Аналогично на пятом элементе. Теперь посмотрим точнее, что происходит. Чтоб вычислить сумму на шестом элементе мы к каждой из имеющихся трех сумм добавляем одно и тоже число (6 элемент). На следующем шаге мы опять добавим к каждому из них одно и тоже число (1 элемент) и т.д. <br /><strong></strong><strong><br />
Каковы шансы каждой из рассматриваемых строк содержать лучшую сумму?</strong><br />
1. Если сумма в первой строке ни разу не сбрасывалась (по алгоритму Кадане это происходит, когда сумма становиться меньше 0), то только она может содержать максимальную сумму; <br />
2. Если она сбросилась на втором элементе, то она все еще имеет преимущество над суммой в строке, начавшейся с третьего элемента (т.к. дальнейшие числа у обеих строк одинаковые, а второй элемент может быть положительный &ndash; значение этого элемента и будет преимуществом);<br />
3. Если сумма первой строки сбросилась на третьем элементе, то тогда по возможной дальнейшей сумме с суммой второй строки они на равных, но вторая строка началась позднее, а значит и закончится позднее, и в ней может набраться большая сумма. Сумма в первой строке уже никогда не сможет превысить сумму во второй строке и ее можно исключить из рассмотрения;<br />
4. Если сумма первой строки сбросилась после третьего элемента, то рассуждения те же, что и в п.3.<br />
5. Если на текущем элементе строка заканчивается, то дальше продолжать ее мы не можем, и если она сейчас не содержит максимальную сумму, то уже никогда не будет.<br />
Аналогична для остальных строк. </p>
<p><strong>Когда можно убрать строку из рассмотрения?</strong><br />
1. Если сумма в строке <span style="font-weight: bold">А</span> обнулилась после элемента, с которого началась строка <span style="font-weight: bold">Б</span>, то строку <span style="font-weight: bold">А</span> можно исключить из рассмотрения;<br />
2. Если на этом элементе цикла строка должна закончиться, то ее следует исключить ее из рассмотрения (применительно к примеру, первую строку необходимо исключить из рассмотрения на 3 элементе 3 цикла, вторую на 5 элементе 3 цикла, третью &ndash; на 1 элементе 4 цикла);</p>
<p><strong>И что это нам дает?</strong><br />
Максимальную сумму может содержать только строка, появившаяся раньше других и не исключенная по правилам 1, 2. И наоборот: минимальную сумму может содержать только строка, появившаяся позднее других.</p>
<p><strong>Как осуществляются проверки на максимальную сумму и на удаление строк из рассмотрения?</strong><br />
Пусть имеем список, в конец которого мы добавляем новые строки для проверки. Получаем, что на каждом следующем элементе цикла: <br />Для проверки максимальной суммы достаточно проверять только для строки в начале списка. <br />Для удаления строк по правилу 1 достаточно проверять только вторую строку с конца пока не встретиться строка, сумма которой больше нуля (для последней строки у нас пока нет никаких гарантий, что в ней не будет лучшей суммы при дальнейшем расчете). <br />Для удаления строки по правилу 2 достаточно проверять только строку в начале списка. </p>
<p><strong>И как долго нам проверять эти правила?</strong><br />
После первого прохода цикла мы гарантированно начнем проверки всех возможных строк матрицы. Когда список строк будет пуст, поиск подматриц высоты 1 будет закончен: к этому моменту будут проверены все возможные варианты.<br />
Пусть <span style="font-weight: bold">m</span> &ndash; ширина матрицы, <span style="font-weight: bold">p</span> &ndash; длина цикла. Тогда: поиск всех подматриц высоты 1 будет завершен после прохода по <span style="font-weight: bold">p+m</span> элементам цикла (т.к. все строки появившиеся при первом проходе цикла (<span style="font-weight: bold">p</span>) будут гарантированно удалены по правилу 2 еще через <span style="font-weight: bold">m</span> элементов цикла).</p>
<p><strong>А если точнее, как долго нам проверять эти правила?</strong><br />
Рассмотрим случай, когда <span style="font-weight: bold">m &gt; 2p</span>. Тогда после первого прохода цикла далее к суммам строк будут добавляться те же самые значения цикл за циклом. Имеем два варианта: <br />
1. Если сумма элементов цикла больше нуля, то максимальная сумма будет в самом последнем цикле (при условии, что это полный цикл, в противном случае может быть и в предпоследнем). <br />
2. Если сумма элементов цикла меньше нуля, то суммы, полученные на втором проходе цикла будут больше соответствующих сумм каждого следующего прохода.<br />
Получаем, что для полной проверки всех элементов при <span style="font-weight: bold">m &gt; 2p</span>, необходимо обработать <span style="font-weight: bold">p</span> элементов в первом проходе цикла, плюс не более <span style="font-weight: bold">2p</span> элементов в оставшихся циклах (т.к. последний цикл может быть не полным). Итого <span style="font-weight: bold">3p</span> элементов.</p>
<p>Рассмотрим случай, когда <span style="font-weight: bold">m &lt;= 2p</span><span style="font-weight: bold"></span>. Необходимо обработать <span style="font-weight: bold">m+p</span> элементов цикла, или не более <span style="font-weight: bold">3p</span>.</div>
<p><strong>Какова сложность по времени обработки одного элемента цикла?</strong><br />
На каждом элементе цикла выполняются следующие действия:<br />
1. Проверка на добавление новой строки. Линейная сложность: только одна строка может быть добавлена. Если несколько строк начинаются с данного элемента цикла, то достаточно рассмотреть любую из них;<br />
2. Проверка на максимальную сумму. Линейная сложность: проверить достаточно только сумму строки в начале списка;<br />
3. Проверка на удаление строки по правилу 1. Не более <span style="font-weight: bold">p</span> проверок в сумме по всем элементам цикла, т.к. количество строк <span style="font-weight: bold">p</span> и удалить строку можно только 1 раз. В итоге линейная сложность;<br />
4. Проверка на удаление по правилу 2. Линейная сложность: на каждом элементе цикла могло быть добавлена не более 1 строки, следовательно и закончиться на каждом элементе может не более 1 строки.<br />
5. Проверка на опустошение списка строк. Линейная сложность.<br />
6. Обновить суммы строк. Линейная сложность: считается только общая сумма элементов <strong>sum</strong> (обнуление отрицательной суммы строки <strong>row_sum</strong> происходит присвоением ей значения <strong>sum</strong>, текущая сумма строки находиться как <strong>sum - row_sum</strong>, значения суммы остальных строк не меняются).<br />
Получаем: сложность действий, выполняемых на каждом элементе цикла, линейна.</p>
<p><strong>Какова сложность по времени поиска лучшей подматрицы высоты 1?</strong><br />
Для случая <span style="font-weight: bold">m&gt;2p</span> необходимо обработать более <span style="font-weight: bold">3p</span> элементов цикла, для <span style="font-weight: bold">m&lt;=2p</span> необходимо обработать не более <span style="font-weight: bold">3p </span>элементов, поэтому для любых <span style="font-weight: bold">m</span> и<span style="font-weight: bold"> p</span> необходимо обработать не более <span style="font-weight: bold">3p</span> элементов. <br />
Т.к. сложность обработки одного элемента линейна, то сложность обработки <span style="font-weight: bold">3p</span> элементов <span style="font-weight: bold">O(p)</span>. Тогда сложность поиска подматрицы высотой 1 в заданной матрице равна <span style="font-weight: bold">O(p)</span>.</p>
<p><strong>А что же делать  с подматрицами, имеющими большую высоту?</strong><br />
Поиск подматриц большей высоты можно свести к поиску аналогичному поиску подматрицы  высоты 1. Каждый столбец матрицы заменяется на сумму элементов этого столбца. Или ближе к практике: для подматриц высоты 2 значение элемента изначальной матрицы заменяется на сумму этого элемента и элемента стоящего под ним, для подматриц высоты 3 &ndash; на сумму этого элемента и двух элементов стоящих под ним и т.д.  А на практике: сумма находиться только для элементов цикла. И далее уже производиться поиск аналогичный поиску подматрицы высоты 1.</p>
<p><strong>На сколько сложен по времени поиск лучших подматриц имеющих большую высоту?</strong><br />
Проверять нужно строки от нуля до <span style="font-weight: bold">n-h</span>, где <span style="font-weight: bold">n</span> &ndash; высота матрицы, <span style="font-weight: bold">h</span> &ndash; высота подматрицы (нумерация строк с нуля). Уникальных строк в них будет не более <span style="font-weight: bold">p</span>. Поэтому сложность по времени будет складываться из:<br />
1. Сложности подготовки элементов матрицы для поиска подматриц текущей высоты. Требуется <span style="font-weight: bold">p</span> сложений для получения новых значений цикла из значений цикла для поиска подматрицы меньшей высоты (на 1 строку меньше). Итого <span style="font-weight: bold">O(p)</span>;<br />
2. Сложности поиска подматрицы текущей высоты <span style="font-weight: bold">O(p)</span>.<br />
Итого: сложность поиска подматрицы каждой высоты <span style="font-weight: bold">O(p)</span>.</p>
<p><strong>А если в общем, какая сложность по времени?</strong><br />
Различных высот подматриц может быть <span style="font-weight: bold">n</span>, поэтому сложность поиска подматрицы любой высоты будет <span style="font-weight: bold">O(np)</span>.</p>
<p><strong>Понятно, но что со сложностью по памяти?</strong><br />
В случае обработки в одном потоке необходимо хранить:<br />
1. <span style="font-weight: bold">p</span> элементов &ndash; значения цикла, составляющего матрицу;<br />
2. <span style="font-weight: bold">p</span> элементов &ndash; для хранения сумм столбцов подматриц, при поиске подматриц высоты большей 1;<br />
3. <span style="font-weight: bold">p</span> элементов  - для списка текущих проверяемых строк;<br />
4. единичные значения (лучшая сумма, текущая высота подматрицы, временные значения и т.д.). <br />
Итого: сложность поиска лучшей подматрицы по памяти <span style="font-weight: bold">O(p)</span>.</p>
<p><strong>Как все сказанное соотноситься с <a href="http://software.intel.com/ru-ru/articles/contest-acceler8-2011-problem/">задачей на конкурс Acceler8</a>?</strong><br />
1. Для матриц содержащих малое число повторений цикла время поиска может быть хуже времени поиска с помощью модифицированного алгоритма Кадане для матриц;<br />
2. Алгоритм может быть модифицирован для матриц, в которых только часть элементов составляют периодическую матрицу. Поиск по непериодической части матрицы осуществляется модифицированным алгоритмом Кадане для матриц.</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/11/29/onp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>О программировании, ошибках и опыте на примере сервера онлайн игры</title>
		<link>http://software.intel.com/ru-ru/blogs/2011/11/22/2005877/</link>
		<comments>http://software.intel.com/ru-ru/blogs/2011/11/22/2005877/#comments</comments>
		<pubDate>Tue, 22 Nov 2011 15:18:42 +0000</pubDate>
		<dc:creator>ronsenval</dc:creator>
				<category><![CDATA[Intel Software Network]]></category>
		<category><![CDATA[Игры]]></category>
		<category><![CDATA[Параллельное программирование]]></category>
		<category><![CDATA[Acceler8]]></category>
		<category><![CDATA[game architecture]]></category>
		<category><![CDATA[gamedevelopment]]></category>

		<guid isPermaLink="false">http://software.intel.com/ru-ru/blogs/2011/11/22/2005877/</guid>
		<description><![CDATA[<p>Около года назад передо мной встала  задача написания сервера и клиента для онлайн игры. В результате поиска  информации по этой теме оказалось, что описанных решений такой задачи либо нет,  либо их сложно найти. Оставался один вариант &#8211; самому решить задачу. В итоге &#8211; полгода работы, три версии сервера и значительное кол-во опыта.</p>]]></description>
			<content:encoded><![CDATA[<p>Около года назад передо мной встала  задача написания сервера и клиента для онлайн игры. В результате поиска  информации по этой теме оказалось, что описанных решений такой задачи либо нет,  либо их сложно найти. Оставался один вариант &ndash; самому решить задачу. В итоге &ndash; полгода работы, три версии сервера и значительное кол-во опыта.</p>
<p>Понятно, что сервер разрабатывался  под конкретную игру, и потому в определенной мере специфичен, но все же  постараюсь изложить общие подходы, которые, я надеюсь, могут быть применены и в  других проектах. Также возможны небольшие отступления, для лучшего понимания  вопроса.</p>
<p><b>Задача:</b>&nbsp; необходимо разработать сервер и клиент для  игры через интернет, с количеством одновременно играющих игроков не менее 1000. </p>
<p><b>Сама  игра:</b>&nbsp; игра состоит из сражений,  сражения никак не пересекаются друг с другом, в каждом сражении участвует несколько  игроков, игроки одновременно отдают команды, видят результаты действия других  игроков, можно сохранить состояние сражения и продолжить позднее.</p>
<h2>Выбор языка  программирования</h2>
<p>Из знакомых мне языков, с помощью  которых можно было бы реализовать, были следующие: <b><i>php</i></b>, <b><i>c#</i></b>, <b><i>c++</i></b>. Из плохо известных: <b>Java</b>. Остальные  не рассматривались ввиду отсутствия опыта работы с ними.</p>
<p>За год до этого был опыт  разработки более упрощенной задачи подобного вида, при этом стояло жесткое  условие: язык программирования <b>PHP</b>. Так что я сполна оценил все недостатки  этого языка применительно к подобной задаче (все же он предназначен для других целей,  хотя при желании и топором можно торт нарезать, но что из этого выйдет &ndash; вопрос  интересный). <b>С#</b> представлялось  сложным использовать на <b>Debian</b>. <b>Java</b> был отклонен  ввиду маленького опыта работы с ним. Вероятно, люди, хорошо в нем разбирающиеся,  смогли бы качественно реализовать подобный сервер. В итоге: C++. </p>
<p>Как показала дальнейшая практика,  решение оказалось вполне разумным. И сейчас понимаю, что реализовать его с той  же простотой и функционалом на PHP  просто невозможно (собственно, как и аккуратно порезать торт топором).</p>
<p> Вывод: стоит использовать наиболее подходящий инструмент, исходя из  требований, окружения, своих знаний, желания расширить свои профессиональные  качества (конечно если это не идет вразрез, например, со временем выполнения  задачи, а то ведь я и <b>Java</b> хотел получше изучить, но боюсь,  заказчик остался бы недоволен сроками <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<h2>Выбор инструментов языка</h2>
<p>Тут тоже было просто. Исходя из  того, что разработка велась на <b>Windows 7</b>, а работать должно на <b>Debian</b>,  все средства должны быть переносимы. В качестве основы кроссплатформенности  была выбрана библиотека <b>boost</b>. Основную роль сыграло то, что <b>boost</b>  является основой для вышедшего и следующих стандартов языка C++, других значительных &laquo;за и против&raquo;  применительно к данной задаче я не нашел (сравнивания например с <b>Qt</b>).  Как позже выяснилось, нужны были только базовые возможности библиотеки (потоки,  работа с файловой системой), а потому не было особой разницы, что выбирать.</p>
<p><b>Вывод:</b> если не находишь разницы между вариантами, выбери любой <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> , и делай, там будет видно.</p>
<h2>Проектирование</h2>
<p>Любопытна склонность к <b>необоснованному</b> преувеличению сложности  программы. Это проявляется как на уровне алгоритмов и структур данных (желание  сразу все оптимизировать), так и на уровне архитектуры приложения (к примеру,  попытка сделать все сверхпараллельно). И как следствие: рост сложности  программы, отсюда большее число ошибок и возрастающая трудность в их  исправлении, отсюда затягивание сроков, нервы и т.д. Кому такое надо?  Правильно, никому. </p>
<p>Как сказано в одной книге: &laquo;программирование &ndash; есть  управление сложностью&raquo;. </p>
<p>Взять хотя бы конкретный пример (<a href="http://software.intel.com/ru-ru/articles/contest-acceler8-2011-main/">задачи конкурса Acceller8</a>). За время конкурса решение  эволюционировало и прошло следующие этапы:      </p>
<ol>
<li>Используем библиотеку TBB. И сразу пошло-пошло распараллеливание  по задачам. Что будет дальше не знаю, но точно будет много потоков (не сделал,  осталось только в планах).</li>
<li>Запущенны N  потоков стандартной функцией, они из общего массива каждый в своем порядке  (чтобы не мешать друг другу) берут матрицы для вычисления и считают, когда  очередь заканчивается они просматривают сначала потоки, запущенные на этом же  процессоре, чтоб им помочь. Если на этом процессоре потоки всё посчитали, то  поиск производиться на других (реализовано).</li>
<li>Запущенны N  потоков стандартной функцией, они <b>из  очереди подряд</b> берут матрицы для вычисления и считают, когда очередь  заканчивается они <b>просматривают подряд  все другие потоки</b>, чтоб им помочь (реализовано).</li>
<li>Запущенны N  потоков стандартной функцией, они из очереди берут матрицы и считают (уже не  было времени рефакторить старый код из п.3 чтоб убрать лишний функционал. Это ж  надо, что получается: реализовываешь этот лишний функционал, реализовываешь,  время тратишь... Так чтоб его еще и убрать потом, нужно время тратить!</li>
</ol>
<p>Очевидно, что в пункте 1-3 было  потрачено время на реализацию некоторого функционала, который оказался не  нужен. Ведь не было оснований полагать, что доступ через очередь или массив  будет критически влиять на сложность, или тоже разделение по процессорам. Хотя  некоторые решения принимаются обоснованно: изначально не было быстрого  алгоритма, и потому тогда возможность совместной обработки потоками одной  матрицы была обоснованна (вычисление больших матриц занимало минуты).</p>
<p><b>Вывод: </b>если хочется сделать суперпрограмму, то стоит  обосновать действительную необходимость тех или иных &laquo;улучшений&raquo;, иначе, в  лучшем случае, они не нанесут вреда. Хотя, с другой стороны, это лишь вопрос  опыта и времени, требуемого на его приобретение. &nbsp; </p>
<p>Каждый из нас будет совершать  ошибки, независимо от своих желаний и взгляда на мир, и, единственный возможный  путь что-то делать - это что-то делать (некому не говорите <img src='http://software.intel.com/ru-ru/blogs/wordpress/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> , это страшшшный секрет). И, наверное, время от времени стоит задумываться <b>как</b> делаешь.</p>
<h2>Реализация сервера: от  сложного к простому</h2>
<p>Далее рассматриваются основные  сложности, которые возникли по ходу реализации сервера, описывается, как они  были разрешены. </p>
<p><strong>Параллельная обработка  бывает разная</strong></p>
<p>Главное требование к серверу &ndash;  справляться с текущей нагрузкой, а также иметь возможность улучшения/расширения/масштабируемости  для увеличения нагрузки в будущем. Очевидное решение &ndash; параллельная обработка.  Схема первоначально реализованного варианта работы потока представлена ниже:</p>
<p><img src="http://s017.radikal.ru/i407/1111/64/b4826ce8437d.png" align="middle" border="0" /></p>
<p>Есть управляющий поток, содержащий  списки активных соединений и текущие сражения. Каждые 100 мс (для разрабатываемой  игры +/- 100 мс не значительно) сражения и соединения передаются в очередь  менеджеру потоков на выполнение (для соединений проверяется поступление команд  игрока, для сражений &ndash; происходит обработка 100мс игрового времени).</p>
<p>Минусы, из-за которых пришлось  отказаться от данной модели:</p>
<ol>
<li> Сложность всвязи с возможностью  одновременного выполнения сражений и соединений, возникает необходимость  гарантировать, что состояние игры будет изменено в один момент времени либо  командой от игрока, либо внутренней обработкой. В результате, ко всем функциям,  изменяющим состояние игры необходимо добавлять мьютексы и т.д.</li>
<li>Сложность контроля за  выполнением потоков в менеджере. Выполнил он потоки? Или еще выполняет?</li>
</ol>
<p><img src="http://s017.radikal.ru/i407/1111/b6/07524fed5dc8.png" border="0" /></p>
<p>В результате была реализована модель  представленная ниже:</p>
<p ><img src="http://s017.radikal.ru/i436/1111/1e/d1a51df2d06b.png" border="0" /></p>
<p>Создается необходимое кол-во  потоков. Каждый поток обрабатывает часть сражений и соединения, относящиеся к  этим сражениям.&nbsp;</p>
<p>Алгоритм обработки внутри  потоков:</p>
<ol>
<li>последовательно проверить поступление новых команд от  пользователей и передать им данные,  </li>
<li>последовательно обработать сражения,</li>
<li>подождать до следующего цикла.</li>
</ol>
<p>Преимущества данной модели в отсутствии  недостатков прошлой:</p>
<ol>
<li>состояние сражения может измениться либо из-за команд  пользователя, либо во время обработки внутриигрового времени, но не  одновременно без дополнительного кода. Как результат можно вызывать любые  функции из класса &laquo;соединение с игроком&raquo; и внутри класса &laquo;сражения&raquo; не задумываясь  о том, что кто-то еще может изменить состояние игры.  </li>
<li>Также появилась  возможность контроля времени выполнения. Например, если после выполнения цикла  обработки до следующего цикла остается менее 10 мсек, то поток почти все время  тратит на обработку сражений, и, в случае увеличение нагрузки будут задержки.  Но мы ведь знаем это и поэтому можем реагировать на это (например, отказывать  новым игрокам в подключении, или сделать запись в лог, что в следующий раз  необходимо запустить большее число потоков).  </li>
</ol>
<p class="MsoNormal"><img src="http://s017.radikal.ru/i443/1111/17/ded560c04a02.png" border="0" /></p>
<p class="MsoNormal"></p>
<h2>Проблема большого кода</h2>
<p>Для представления сражения  реализован класс Game.  И все бы хорошо, если бы в итоге он не разросся до 70 функций. Большое количество  времени стало уходить на поиск нужного участка кода. Проблемой стал поиск  ошибок, ибо любая функция может взаимодействовать с любой другой и менять любые  данные, т.к. всё внутри одного класса. </p>
<p><img src="http://s017.radikal.ru/i406/1111/aa/facfc124a0a2.png" border="0" /></p>
<p> Когда я устал от этого  безобразия, было принято решения провести основательный рефакторинг и разделить  функционал класса. В итоге получилось иерархия из 10 классов по 6-8 логически  связных функций в каждом. Новые классы инкапсулируют данные относящиеся только  к ним, и взамен предоставляют соответствующий интерфейс. Таким образом,  ограничивается область сторонних эффектов работы каждого из них. </p>
<p><img src="http://s017.radikal.ru/i401/1111/1d/7e654cec6854.png" border="0" /></p>
<h2>Рыцарь атакует орка,  которого убивает эльф</h2>
<p>Рассмотрим пример с добрым рыцарем,  спящим орком и славным эльфом. Добрый рыцарь видит спящего орка, решает  атаковать его и начинает подкрадываться к нему, делая себе пометку, что на это  уйдет 10 сек. Славный эльф тоже видит сложившуюся ситуацию и решает помочь  доброму рыцарю, и потому в течении 5 сек выпускает пять стрел, которые убивают  орка. В реальной жизни славный рыцарь прокравшись в течении 10 сек (в  соответствии со своей пометкой), понял что его опередили и стал бы  подкрадываться к следующему спящему орку. В игре же возможен вариант, что  подкравшись к орку, он увидит &laquo;<b>Access violation at address 0A6C77BE</b>&raquo;, и, вероятно, упадет вместе с сервером.</p>
<p>Решение очевидно &ndash;  хранения счетчика ссылок на орков, а в общем случае &ndash;  на игровые объекты. Ниже приведена первоначальная реализация:</p>
<p>&nbsp;<img src="http://s017.radikal.ru/i430/1111/93/86ab8c613cd1.png" border="0" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>Происходит следующее, объект  &laquo;рыцарь&raquo; создает событие &laquo;нанести удар через 10 сек&raquo;, вызывая функцию <b>AddEvent</b>  менеджера событий. Одновременно с этим, он сообщает менеджеру объектов, что он  рассчитывает, что объект &laquo;орк&raquo; (или то, что от него останется) будет дожидаться  его в том же самом месте (через вызов функции <b>AddDependency</b>). &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>После наступления события,  &laquo;рыцарь&raquo; сообщает менеджеру объектов, что &laquo;орк&raquo; не представляет для него  интереса (через вызов <b>RemoveDependency</b>). Если от объекта больше никто не  зависит, то объект удаляется.&nbsp;&nbsp;&nbsp; Проблема  возникла, когда стало понятно, что на каждое событие приходиться писать по две  строчки кода для добавления зависимостей (по одной при создании и наступлении  события). </p>
<p>Если есть работа, которую  можно не делать, то ее лучше не делать. Потому модель была изменена на  следующую: </p>
<p><img src="http://s017.radikal.ru/i430/1111/d4/c1efb2c75d32.png" border="0" /></p>
<p>Параметры событий были стандартизированы,  так, чтобы менеджер событий смог &nbsp;разобраться,  к каким объектам требуется добавить зависимости, чем он теперь и занимается.  Соответственно, объектам теперь требуется создать только само событие.</p>
<h2>Сохранение сражения</h2>
<p>Требовалось предусмотреть  возможность сохранения сражения и освобождения, занимаемых им ресурсов, а также  последующая загрузка и продолжение сражения. Первоначальный вариант был  следующий:</p>
<p><img src="http://s017.radikal.ru/i443/1111/a0/4f59128e3213.png" border="0" /></p>
<p>В общем случае всё сражение строится  на объектах и событиях, поэтому задача сохранения &ndash; есть задача сохранения всех  объектов и событий (в реальной игре также необходимо сохранять: текущее время,  количество очков у игроков и т.д., эта часть задачи рассмотрена не будет&ndash; на  смысл примера это не повлияет). Итак, &nbsp;первоначальный вариант предполагал сохранение  данных путем их сериализации и последующей десериализации. Каждый объект  наследуется от базового класса с виртуальными функциями сериализации и десериализации,  объекты событий &ndash; аналогично. В качестве формата хранения данных предполагалось  использовать JSON.  А в качестве места хранения -<span style="font-weight: bold"> базу данных. </p>
<p>Проблема стала очевидна при дальнейшей&nbsp; модификации объектов (их наборов свойств),  ведь любое добавление или удаление вело к необходимости переписывания функций  сериализации/десериализации. Решением проблемы было сохранение дампа памяти:</p>
<p><img src="http://s017.radikal.ru/i417/1111/df/75421191cf07.png" border="0" /></p>
<p>Фактически, сохраняется область  памяти, в которой размещены данные менеджеров объектов и событий. Здесь  пришлось модифицировать программу таким образом, чтобы все данные сражения  (класс <strong>Game</strong>) располагались в  памяти последовательно (сюда относятся пулы для выделения памяти под объекты и  события, информация о ресурсах, игроках и т.д.). Сейчас сохранение занимает  порядка <strong>250КБ</strong>, сюда входит возможность одновременного присутствия в сражении <strong>1000</strong>  игровых объектов (монстров, зданий и т.д.) и <strong>3000</strong> событий (ничего не мешает при  необходимости увеличить их количество в будущем).</p>
<p>Также потребовалось: (1)  преобразование адресов в индексы, и обратно. Это пришлось сделать из-за  отсутствия гарантии на то, что память под класс будет выделена в том же самом  месте (внести это изменения было несложно, т.к. адреса объектов хранятся только  в менеджере событий, и в очень-очень малом числе игровых объектов). (2)  Потребовалось добавить проверки на изменение версии сервера и модели карты (т.к.  она хранится в памяти отдельно). </p>
<p>Думаю, что это небольшая плата за возможность не задумываться о том, как сохранится состояние сражения после каждой  модификации. Но здесь есть и отрицательный  момент: использование сохранений предыдущих версий при таком подходе становиться  нетривиальной задачей.</p>
<h2>Заключение</h2>
<p>Большинство рассмотренных вопросов  касались удобства написания и дальнейшей модификации программы. Не были  затронуты вопросы быстродействия (сейчас сервер проходит бета-тестирование под  небольшой нагрузкой). Оставлена за кадром организация клиент-серверного  взаимодействия, и то, к каким архитектурным решениям она привела. В  рассмотрение также не попала событийная модель и все вопросы, касающиеся  непосредственных алгоритмов и структур данных.  </p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/ru-ru/blogs/2011/11/22/2005877/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

