<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated on Sun, 08 Nov 2009 13:29:51 -0800 -->
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <atom:link href="http://software.intel.com/ru-ru/articles/feed/" rel="self" type="application/rss+xml" />
    <title>Intel Software Network articles фид</title>
    <link>http://software.intel.com/ru-ru/articles//all</link>
    <description></description>
    <language>ru-ru</language>
    <item>
      <title>Спам и методы борьбы с ним</title>
      <description><![CDATA[ <p>В настоящее время, не существует практически ни одного человека, кто не сталкивался бы со спамом. У вас есть почтовый ящик, аккаунт в Интернет-магазине, страница в социальной сети, блог в ЖЖ или просто ваш компьютер имеет доступ к Интернет – значит вы потенциальная цель для спамеров.</p>
<p>Рассмотрим основные цели, которые преследуют люди, занимающиеся этим:</p>
<h3 class="sectionHeading">Реклама</h3>
<p>Реклама для легальных компаний, которые таким образом пытаются привлечь внимание покупателей к своим товарам. В большинстве случаев вместо увеличения продажи товаров наблюдается обратный эффект. Хотя в настоящее время существует мнение, что есть несколько критериев, выполняя которые фирма может получить желаемый эффект и при этом не вызывать отрицательных эмоций у получателя писем:</p>
<ul type="disc">
<li>Организатором рассылки должен являться поставщик      услуг электронной почты;</li>
<li>Хорошо подобранная целевая аудитория (зачем      рассылать предложения продажи суперкомпьютеров, к примеру, домохозяйкам);</li>
<li>В тексте письма должно содержаться предупреждение о регулярности данной рассылки;</li>
<li>Наконец, предоставление пользователю удобных      механизмов блокирования подобных писем.</li>
</ul>
<p>Не смотря на то, что данные критерии можно оспорить, т.к. они весьма условны, не стоит забывать, что рекламные письма, рассылаемые пользователям с их согласия, не являются спамом.</p>
<h3 class="sectionHeading">Реклама незаконной продукции</h3>
<p>Несомненно, каждый из нас хотя бы один раз встречался с подобным. Здесь предлагается всё, начиная от, скажем, баз данных крупнейших сотовых операторов и заканчивая контрафактным программным обеспечением.</p>
<h3 class="sectionHeading">Антиреклама</h3>
<p>В противовес рекламе ставит своей целью всячески помешать объекту атаки (фирме или простому пользователю). Как правило, содержит заведомо ложную информацию с целью опорочить атакуемый объект.</p>
<h3 class="sectionHeading">Письма с целью выманивания денег</h3>
<p>Принцип довольно прост. Пользователю приходит письмо содержащее информацию о том, что он может получить крупную сумму денег, а отправитель может ему в этом помочь. Далее содержится просьба перевести небольшую часть денежных средств, для, например, оформления документов или под любым другим предлогом. Получение этих денежных средств и есть цель отправителя.</p>
<h3 class="sectionHeading">Фишинг</h3>
<p>Цель – выманить у получателя конфиденциальную информацию, номера кредитных карт, информацию о профиле в электронных платёжных системах и т.д. Здесь активно используется социальная инженерия.</p>
<h3 class="sectionHeading">Распространение вирусов</h3>
<p>Цель заразить как можно больше компьютеров, чтобы создать бот-нет или провести Dos атаку.</p>
<p>С развитием Internet развиваются и способы распространения спама. Как и раньше основная доля приходится на рассылку писем по электронной почте. Причём для этого используются не только почтовые серверы, но и «компьютеры-зомби».</p>
<p>Заходя на сайты, довольно часто мы сталкиваемся с всплывающими окнами, которые занимают большую часть экрана, и при закрытии выполняют переадресацию на некую целевую страницу.</p>
<p>Набирает обороты спам в системах мгновенных сообщений, так называемый <b>спим</b> (<b>SP</b>am + <b>I</b>nstant <b>M</b>essenger).</p>
<p>В настоящее время стали популярны web-сайты, на которых можно оставлять комментарии (форумы, блоги, социальные сети) или свободно редактируемые – wiki. Благодаря тому, что данные страницы возможно редактировать на них может быть размещён спам. Целью может служить продвижение какого-либо ресурса (в том числе и в поисковых системах), посредством размещения ссылок, всплывающих окон и/или сценариев автоматической переадресации. Так например, если основная тематика сайта, скажем, автомобили, а большинство комментариев содержит рекламу фармацевтических препаратов, то релевантность данного ресурса для поисковых систем будет снижена.</p>
<p>Также спам перешел «границу» Интернета и теперь рассылается и в СМС сообщениях. Это стало возможным, благодаря появлению дополнительной функциональности в системах обмена мгновенными сообщениями, системах голосовой связи, а так же сервисам, предоставляемым самими операторами сотовой связи.</p>
<p>К основным методам распространения спама относятся:</p>
<ul type="disc">
<li>Рассылка спама вручную.<br /> Его эффективность невелика      и с ним легко бороться, внося ip-адрес и почтовый адрес в чёрный список</li>
<li>Программы автоматической рассылки спама.<br /> Могут быть      выполнены в виде утилиты под Windows или скрипта для размещения на      web-сайте. В сущности, это автоматизированная разновидность метода 1.</li>
<li>Рассылка спама при помощи сети троянских прокси (Trojan-Proxy)      и спам-ботов. Этот метод наиболее популярен и актуален в настоящее время.</li>
</ul>
<p>Наиболее интересен 3 метод. Для построения сети Trojan-Proxy или спам-ботов, необходимо заразить ими компьютер пользователя. Это можно сделать с помощью: использования уязвимостей в программном обеспечении, установленном на компьютере жертвы; использования специальной программы, которая сама по себе не содержит вредоносного кода, и возможно, содержит какой-либо полезный функционал, но вместе с тем загружает под видом обновлений или в скрытом режиме вирус; использования почтовых и сетевых червей.</p>
<p>В случае заражения компьютера пользователя спам-ботом, последний связывается с сервером владельца, от которого получает информацию, необходимую для дальнейшей работы: список электронных адресов, параметры рассылки, шаблоны писем и так далее. Компьютер с такой программой очень быстро попадает в чёрные списки и у его владельца возникают сложности с отправкой обычной почты. Это особенно актуально для крупных фирм с собственным почтовым сервером. Представьте себе ситуацию, когда крупная компания оказывается «отрезанной» от остального мира, т.е. вся её электронная переписка не отправляется на другие почтовые серверы и не получается от них соответственно. Подобное может произойти, если даже хотя бы у одного корпоративного пользователя окажется заражённый компьютер. Создание сетей таких компьютеров является очень прибыльным бизнесом. Причём деньги можно получить не только предоставляя услуги рассылки спама, но и за продажу как всей сети, так и отдельного бота.</p>
<p>Ещё одна методика рассылки спама, заключается в использовании так называемых троянских прокси (Trojan-Proxy). Они позволяют злоумышленнику работать в сети от имени заражённой машины. По своей сути являются вирусами, т.к. всячески маскируют свое присутствие в системе, под видом системных процессов (наиболее часто под видом winlogon-а). Принцип работы в следующем, открывается на прослушивание некий TCP-порт (номер порта статический или динамический), после чего данный процесс связывается с владельцем и передаёт ему IP-адрес и номер порта, открытого для прослушивания. Затем он работает как обычный Proxy-сервер. Большинство троянских-прокси умеет размножаться по принципу сетевых червей или при помощи уязвимостей в программном обеспечении компьютера жертвы. Стоит упомянуть, что в настоящее время существует множество гибридов, так например спам-бот может обладать возможностью загружать обновления для себя и/или при наступлении некоего события переходить в прокси режим.</p>
<p>Что касается методов борьбы с подобного рода программами, то их присутствие легко детектируется с помощью любого сниффера (программы для пассивного перехвата трафика). В трафике заражённого компьютера будут преобладать SMTP и DNS.</p>
<p><img src="http://software.intel.com/file/23246" /></p>
<p>В данной ситуации можно порекомендовать проверить компьютер с помощью антивируса с последними обновлениями баз данных сигнатур вирусов.</p>
<h3 class="sectionHeading">Общие рекомендации по борьбе со спамом</h3>
<p>На сегодняшний день существует много способов борьбы со спамом, но универсального способа нет.</p>
<p>Создавать так называемые «чёрные списки», для фильтрации сообщений пришедших от определённого автора/адреса электронной почты/IP-адреса и т.п.</p>
<p>Использовать алгоритмы автоматической фильтрации сообщений/комментариев.</p>
<p>Применительно к комментариям можно выделить следующие методы:</p>
<ul>
<li>Позволять оставлять комментарии только зарегистрированным пользователям, с последующей проверкой данных комментариев. Если оставленный комментарий не соответствует каким либо критериям допустимости, характерным для каждого отдельного ресурса, то комментарий удаляется/не публикуется. Таким образом на сайте размещаются только проверенные администрацией комментарии. Данный метод может быть усилен путем разрешения оставления комментариев только «доверенными» пользователями, чьи комментарии были одобрены ранее.</li>
<li>Использовать службы аутентификации. Например такие как Google Account, «Яндекс.Паспорт» и т.д. Самым распространённым на сервис аутентификации TypeKey.<br /> <img src="http://software.intel.com/file/23247" alt="TypeKey" /> При регистрации на сайте данного сервиса, пользователь получает возможность оставлять комментарии на всех сайтах, где этот сервис поддерживается. В России используется OpenID. К плюсам использования систем аутентификации следует отнести то, что для доступа к ресурсу нужно зарегистрироваться и подтвердить свою учётную запись, что создает дополнительные трудности спамерам.</li>
<li>Изменение названий и переменных используемых в скриптах регистрации популярных движков таких как Movable Type и Wordpress. Цель – затруднить спамерам автоматическую публикацию комментариев, так как нужно определить не только новое местоположение скрипта и его название, но и названия внутренних переменных содержащих тело комментария.</li>
<li>Использование CAPTCHA – полностью автоматизированного публичного теста Тьюринга (Completely Automated Public Turing test to tell Computers and Humans Apart). Несомненно, с ним сталкивался каждый пользователь сети. Он представляет из себя сильно искаженный текст и/или циферно-буквенные комбинации. Данный метод используют все крупные интернет-сервисы, включая Google, MSN, «Яндекс», «Рамблер» и другие. Однако данный тест не является единственным. Так например вам может быть предложена картинка с искаженным текстом математической задачи, сложения целых чисел, или предложен звуковой файл содержащий простое слово, которое вы должны будете записать.</li>
</ul>
<p>Итак, потенциальной целью спамеров может стать любой. Но если не публиковать свои личные данные, адрес электронной почты, номера ICQ и т.д. то риск подвергнутся атаке будет заметно уменьшен. Также существуют рекомендации использовать несколько адресов электронной почты. Например, один для рабочей документации и важной информации, другой для личной переписки и неформального общения и т.д.</p>
<h3 class="sectionHeading">Об авторе</h3>
<p>Ирина Сергеевна Раткевич - студентка  3 курса НИЯУ (Национального Исследовательского Ядерного Университета, ранее СарФТИ) Экономико-Математического Факультета, специальность Информационные Системы в Экономике.</p> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/spam</link>
      <pubDate>Tue, 03 Nov 2009 01:05:48 -0800</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/spam#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/spam</guid>
      <category>Академическое сообщество</category>
    </item>
    <item>
      <title>Как мы тестируем анализатор кода</title>
      <description><![CDATA[ <h2>Аннотация</h2>
<p>В статье описаны технологии тестирования, используемые при разработке   статического анализатора кода PVS-Studio. Разработчики инструмента для   программистов делятся принциами тестирования собственного программного продукта,   которые могут быть интересны разработчикам аналогичных пакетов обработки   текстовых данных или исходных кодов.</p>
<h2>Введение</h2>
<p>Занимаясь разработкой и продвижением программного продукта <a href="http://www.viva64.com/ru/pvs-studio">PVS-Studio</a>, в своих рассказах мы   огромное внимание уделяем вопросам качества программного обеспечения, процессам   разработки и принципам организации труда программистов. При этом до сих пор   закрытым оставался вопрос о том, как же собственно мы сами разрабатываем свой   программный продукт? Используем ли те технологии, рекомендации и практики, о   которых пишем в статьях? Наконец, относится ли к нам фраза "сапожник без   сапог".</p>
<p>В этой статье мы решили рассказать о том, как мы тестируем программный   продукт PVS-Studio. С одной стороны, это делается для того, чтобы убедить наших   (потенциальных) пользователей в качестве нашего инструмента. С другой стороны -   рассказать об успешном опыте применения некоторых практик.</p>
<p>PVS-Studio - это статический анализатор кода, предназначенный для   разработчиков современных ресурсоемких приложений на языках Си и Си++ (более   подробная информация дана в статье "<a href="http://www.viva64.com/art-4-1-1796251700.html">Учебное пособие по   PVS-Studio</a>" [2]). Под современными мы понимаем 64-битные и/или параллельные   приложения. Разработка таких программ имеет ряд трудностей, отличных от проблем   традиционных программ. Ведь помимо обычных и всем известных ошибок вроде   неинициализированных указателей, которые обнаруживаются любым компилятором, есть   и специфичные виды проблем.</p>
<p>Речь идет об ошибках в программах, которые проявляются при миграции 32-битных   приложений на 64-битные платформы. Или при распараллеливании кода для поддержки   многопроцессорности или многоядерности. Разрабатывать такие приложения довольно   сложно из-за недостатка инструментов, облегчающих создание 64-битных и   параллельных программ. Анализатор PVS-Studio является именно таким   инструментом.</p>
<h2>Практики тестирования, используемые при разработке PVS-Studio</h2>
<p>Разрабатывая PVS-Studio, мы используем пять основных методики   тестирования:</p>
<ol>
<li>Статический анализ кода. Странно было бы разрабатывать статический   анализатор и не использовать при этом статический анализ.</li>
<li>Юнит-тесты уровня классов, методов, функций.</li>
<li>Функциональные тесты уровня самостоятельно написанных файлов</li>
<li>Функциональные тесты уровня отдельных файлов.</li>
<li>Функциональные тесты уровня отдельных сторонних проектов и решений (projects   and solutions).</li>
</ol>
<p>Кратко опишем здесь эти методики.</p>
<p>Поскольку PVS-Studio - это статический анализатор кода, то при его разработке   мы также используем методику статического анализа кода для поиска 64-битных   проблем в анализаторе. Во-первых, это нужно для того, чтобы нас не упрекнули в   том, что мы не пользуемся своим продуктом (шутка). А во-вторых, это реально   позволяет находить ошибки до того, как их найдут пользователи.</p>
<p>Юнит-тесты уровня классов, методов функций позволяют нам быть уверенными в   том, что добавление новой функциональности не портит имеющийся код. На этом   уровне проверяются отдельные программные сущности в виде простеньких маленьких   тестов. В общем, самые обычные юнит-тесты.</p>
<p>Функциональные тесты уровня самостоятельно написанных файлов позволяют быстро   проверять, что всё, что должно диагностироваться – диагностируется как и раньше.   Все обнаруживаемые потенциальные проблемы в коде должны обнаруживаться.</p>
<p>Функциональные тесты уровня отдельных файлов позволяют убедиться, что   различные отдельные файлы с кодом полностью и без проблем проверяются   анализатором без проблем. Под проблемами здесь понимаются срабатывающие ASSERT,   неожиданные сообщения об ошибках, а также падения. Ведь анализатор кода - это   такая же программа, как и другие, и падения в ее работе, увы, не редки.</p>
<p>Функциональные тесты уровня отдельных сторонних проектов и решений позволяют   убедиться в том, что анализатор все еще умеет проверять проекты, а количество   диагностических сообщений от версии к версии изменяется контролируемо, а не   хаотично.</p>
<p>Конечно же, помимо этих методик, есть и другие стандартные подходы вроде   "зоркий глаз программиста" или "у нас завтра релиз, проверьте ЭТО", но они   показали свои недостатки и мы стараемся их не применять.</p>
<p>Теперь же расскажем об используемых приемах более подробно.</p>
<h2>Статический анализ кода, выполненный статическим анализатором кода</h2>
<p>Заголовок раздела вызывает недоумение? Кто Что же еще может выполнять   статический анализ кроме статического анализатора? Однако при разработке   инструментов для программистов всегда есть нюансы.</p>
<p>Как вы наверняка знаете, первые версии компиляторов языков программирования   редко пишутся сразу же на этих языках. Как правило, для разработки компилятора   нового языка используется совсем другой язык, являющийся стандартом на тот   момент. Это сегодня все компиляторы Си++, пишутся на Си++, а первая версия была   написана на Си.</p>
<p>Точно так же, разрабатывая первую версию статического анализатора кода, мы не   могли ее проверить. Именно поэтому первая версия нашего продукта (тогда он   назывался еще Viva64) вовсе не была 64-битной! Зато уже версия 1.10, которая <a href="http://www.viva64.com/content/PVS-Studio-help-ru/Release_history.html">появилась</a> 16 января 2007 года, то есть через 17 дней после выпуска первой версии, среди   нововведений содержала строку:</p>
<ul>
<li>С помощью анализатора Viva64 мы подготовили 64-битную версию Viva64! Но   пользователю не надо беспокоиться о выборе подходящей версии. Правильная версия   выбирается автоматически во время установки.</li>
</ul>
<p>Таким образом, как только у нас появился статический анализатор, выявляющий   проблемы 64-битного кода, так мы сразу стали проверять им наш же код.</p>
<p>Помогает ли нам статический анализ нашего же продукта? Конечно же, да. Но   опять-таки из-за нашей специфики есть нюансы. Поскольку мы сами пишем статьи про   то, как надо делать 64-битный код, то 64-битных ошибок в новом коде мы все-таки   не допускаем. Однако от применения статического анализа мы извлекаем пользу в   плане улучшения диагностики. Например, мы можем посмотреть, какие типы   синтаксических конструкций дают явно ложное срабатывание и исключить их   диагностирование.</p>
<p>Таким образом, применение статического анализа для разработки статического   анализатора оказывается полезным для нас.</p>
<h2>Юнит-тесты уровня классов, методов, функций</h2>
<p>Юнит-тесты уровня классов, методов, функций представляют собой набор тестов   для проверки отдельных логических элементов программы. Вот некоторые примеры   функциональности, на которую у нас есть юнит-тесты:</p>
<ul>
<li>применение тех или иных правил диагностики потенциально опасных   конструкций;</li>
<li>вычисление типов в выражениях, в операциях приведения типов и т.п.;</li>
<li>работа операторов (сложение, вычитание и т.п.);</li>
<li>работа механизмов загрузки и предварительной обработки файлов;</li>
<li>проверка регистрационной информации;</li>
</ul>
<p>Естественно, этими областями наши юнит-тесты не ограничиваются, но являются   показательными с точки зрения примеров.</p>
<p>Как мы пользуемся этими юнит-тестами? При исправлении ошибки в анализаторе   или при добавлении новой функциональности юнит-тесты запускаются в режиме   release-сборки. Если тесты проходят без проблем, то тесты запускаются в режиме   debug-сборки под отладчиком. Это делается для того, чтобы проверить, не   срабатывают ли ASSERT, которых в коде предостаточно. Позднее будет понятно,   почему сразу нельзя запускать debug-версию.</p>
<p>Хотя такие тесты позволяют выявить ошибки, они не являются полноценным   решением. Дело в том, что у нас в PVS-Studio очень мало юнит-тестов по сравнению   с тем, сколько юнит-тестов нужно для проверки "почти компилятора". Поэтому   достичь юнит-тестами большого покрытия в нашем случае очень сложно. Это огромная   по трудозатратам задача, которую в настоящее время реализовать нет   возможности.</p>
<p>Тем не менее, юнит-тесты уровня классов, методов, функций - это первый   уровень "обороны" в нашей системе тестирования.</p>
<h2>Функциональные тесты уровня самостоятельно написанных файлов</h2>
<p>При разработке анализатора кода важно не "потерять" диагностику тех   потенциальных ошибок, которые уже давно обнаруживаются. Это достигается за счет   функциональных тестов уровня самостоятельно написанных файлов. Абсолютно все   обнаруживаемые потенциальные проблемы в виде кода собраны в отдельные файлы. Эти   файлы размечены определенным образом. В тех строках, где анализатор кода должен   обнаружить ошибки, стоят специальные маркерные символы. Причем в этих маркерах   указано, сколько ошибок в данной строке должно быть: одна, две и т.д. Как только   из-за ошибки разработчиков что-то перестает диагностироваться, мы сразу же это   видим. Раньше в строке номер 17 выдавалось сообщение о двух ошибках, а теперь   только об одной. Или наоборот, появились лишние сообщения.</p>
<p>Такой подход очень похож по принципу работы на функциональные тесты уровня   отдельных проектов (о которых будет сказано ниже), но отличается очень высокой   скоростью работы и тем, что проверяемые файлы написаны (и размечены)   самостоятельно.</p>
<p>Кроме того, эту систему можно использовать, разумеется, и при разработке   новых диагностических сообщений. Сначала надо в файлах вручную написать код, в   котором будет диагностироваться новая ошибка, затем разметить его и можно   приступать к реализации собственно диагностики ошибки. Пока реализации нет,   система тестирования будет сообщать, что в этом месте должна быть диагностируема   ошибка, но ее нет. Как только диагностика появится, тест будет проходить   нормально.</p>
<h2>Функциональные тесты уровня отдельных файлов</h2>
<p>Проверка не отдельных классов/методов или вручную сделанных файлов, а файлов   из реальных проектов позволяет нам добиться большего покрытия кода. Четвертым   уровнем в нашей системе тестирования является именно такая проверка. В наши   тесты включены отдельные полностью препроцессированные файлы из различных   проектов: wxWidgets, fox-toolit, CImg, Lame, Boost, CxImage, FreeType и многих   других. Также сюда входят препроцессированные файлы, построенные на основе   стандартных системных заголовочных файлов (CRT, MFC и так далее).</p>
<p>После добавления новой или исправления старой функциональности программист   запускает сначала release-версию тестов, а потом debug-версию. Почему сразу не   запускать debug-версию? Очень просто, Debug-версию не запускают сразу потому,   что release-версия тестов работает одну минуту, а debug-версия - пять минут.</p>
<p>Тестирование на уровне отдельных файлов – очень мощная вещь. Она позволяет   мгновенно выявить ошибку, если в результате развития анализатора какая-то   функциональность "отвалилась". Огромное количество ошибок было не допущено   благодаря этим тестам.</p>
<h2>Функциональные тесты уровня отдельных сторонних проектов и решений</h2>
<p>Самая мощная часть нашей системы тестирования - это ее пятый уровень, который   представляет собой проверку отдельных проектов и решений (projects and   solutions). Именно благодаря этой системе каждая новая версия PVS-Studio как   минимум не хуже предыдущей.</p>
<p>Выглядит эта система следующим образом. Имеется несколько десятков проектов и   решений различных доступных в интернете программ. Например: Lame, Emule, Pixie,   Loki и другие. Каждый из этих проектов проверен с помощью PVS-Studio, результаты   проверки (в виде log-файла) сохранены. После установки новой (разрабатываемой   версии) запускается специальная разработанная нами система, которая по очереди   открывает каждый проект, проверяет его с помощью PVS-Studio, сохраняет   результаты, а затем сравнивает их с эталонными. Если есть отличия, то она их   записывает в отдельный файл (аналог стандартного diff), который легко можно   посмотреть с помощью PVS-Studio и разобраться в причине появления этих отличий.</p>
<p>Например, в новой версии PVS-Studio появилось новое диагностическое сообщение   с кодом V118. Мы запускаем систему тестирования, и она должна сообщить, что в   некоторых проектах появились сообщения V118. Затем мы вручную просматриваем все   изменения в результатах и решаем, правильно ли выдано сообщение V118.</p>
<p>Если же при этом помимо появления сообщения V118 мы видим в результатах, что   пропали некоторые сообщения V115, то это означает, что тесты показали недостаток   текущей версии программы, и она отправляется обратно на доработку. В случае   признания всех изменений справедливыми новые файлы отчетов признаются   эталонными, и сравнение выполняется уже с ними.</p>
<p>Эта система имеет и другое назначение. Поскольку продукт PVS-Studio   предназначен для работы как в Visual Studio 2005, так и в Visual Studio 2008, то   мы всегда проверяем, чтобы диагностические сообщения всегда совпадали в разных   версиях Visual Studio. То есть если, к примеру, мы получили во всех проектах 10   000 диагностических сообщений в Visual Studio 2005, то и в Visual Studio 2008 мы   должны получить ровно столько же сообщений. Когда выйдет Visual Studio 2010, мы   добавим запуск тестов и в этой среде разработки.</p>
<p>Сколько времени занимает такая проверка? На этот вопрос нет однозначного   ответа, покольку база этих тестов постоянно растет за счет новых проектов. И,   естественно, время работы постоянно увеличивается. Год назад, когда анализатор   работал только с использованием одного ядра, тесты выполнялись около часа. Затем   мы сделали возможным работу в несколько потоков, и для двухъядерной машины время   работы сократилось почти вдвое. Со временем мы добавляли все новые проекты и в   тесты. Теперь на той же двухъядерной машине эти тесты отрабатывают чуть больше,   чем за час. Все это происходит, естественно, в release-версии. В ближайшее время   мы планируем перейти на машину с четырьмя ядрами и добавить еще некоторое число   проектов. В таком случае система снова будет работать не меньше часа.</p>
<h2>Что дальше?</h2>
<p>Известно, что нет предела совершенству. Мы продолжаем развивать нашу систему   тестов по всем направлениям. Естественно, мы постоянно увеличиваем базу всех   тестов. Кроме того, нам недостает системы тестирования GUI. Ошибки в этой   системе не так критичны для анализатора кода, в том смысле, что заметить их   можно и глазами. Но в любом случае, для качественного программного продукта   необходимо тестировать и GUI. .</p>
<h2>Заключение</h2>
<p>Из данной статьи вы узнали, как мы тестируем наш статический анализатор кода   PVS-Studio. Возможно, наш опыт поможет вам внедрить подобные практики   тестирования в своих рабочих проектах. А мы надеемся, что прочитав о том, как мы   тестируем PVS-Studio, у вас возникнет желание подробнее познакомиться с нашим   инструментом.</p>
<h2>Библиографический список</h2>
<ol>
<li>Анализатор кода PVS-Studio. <a href="http://www.viva64.com/ru/pvs-studio/">http://www.viva64.com/ru/pvs-studio/</a></li>
<li>Учебное пособие по PVS-Studio. <a href="http://www.viva64.com/art-4-1-1796251700.html">http://www.viva64.com/art-4-1-1796251700.html</a></li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/how-we-test-code-analyzer</link>
      <pubDate>Wed, 21 Oct 2009 03:40:59 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/how-we-test-code-analyzer#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/how-we-test-code-analyzer</guid>
      <category>Параллельное программирование</category>
    </item>
    <item>
      <title>Конкурс «Лето с Intel для профессионального роста»</title>
      <description><![CDATA[ <p><img height="120" width="745" src="http://software.intel.com/file/21987" alt="Конкурс проектов «Лето с Intel для профессионального роста»" /></p>
<p>Завершились летние каникулы, а вместе с ними и очередная молодежная школа-стажировка Intel. Начиная с 2000 года, и вот уже на протяжении 9 лет каждое лето лучшие молодые специалисты получали уникальную возможность проверить свои силы и знания в реальных проектах в одном из центров Intel, расположенных в Москве, Санкт-Петербурге, Нижнем Новгороде, Новосибирске и Сарове. Но этот год особенный – <a rel="nofollow" href="http://uni-schools.ru/">Летняя школа Intel</a> отметила свое десятилетие, обрела новый формат в партнерстве с ведущими вузами страны и получила поддержку местных администраций!</p>
<p>И впервые сообщество Intel® Software Network совместно с академической программой проводит конкурс проектов «Лето с Intel для профессионального роста», к участию в котором приглашаются студенты <a rel="nofollow" href="http://uni-schools.ru/">Летней школы</a> 2009 года.</p>
<p>На протяжении летних солнечных месяцев самые талантливые ребята, собранные со всей нашей необъятной страны, работали над увлекательными проектами. Надеемся, что это время не прошло бесследно и достойным дополнением к летнему отдыху стали новые знания и неоценимый опыт работы в сотрудничестве с международной компанией. Первый шаг в сторону будущей карьеры сделан, и теперь мы предлагаем сделать следующий - вспомнить самые яркие моменты лета и поделиться своими впечатлениями, полученными знаниями и достижениями, приняв участие в конкурсе. Всех представивших свои работы ждут памятные сувениры, а победителю достанется главный приз – нетбук Lenovo* IdeaPad*. Кроме того не забывайте, участие в конкурсе – это достаточно весомый пункт для вашего резюме ;)</p>
<h2 class="sectionHeading">Конкурсное задание</h2>
<p>Участникам конкурса необходимо опубликовать на сайте сообщества разработчиков программного обеспечения Intel® Software Network техническую статью с описанием хода и результатов разработанного проекта или выполненного исследования. Не забудьте включить в статью рассказ о впечатлениях от стажировки в Летней школе и ее влиянии на дальнейшие профессиональные взгляды. Это увеличит ваши шансы на победу!</p>
<p>Подробнее о задании, оформлении и порядке публикации работ читайте на страницах <a href="http://software.intel.com/ru-ru/articles/contest-summer-school-2009-rules/">официальных правил конкурса</a> «Лето с Intel для профессионального роста» и <a href="http://software.intel.com/ru-ru/articles/contest-summer-school-2009-article-requirements/">требований к статьям</a>.</p>
<h2 class="sectionHeading">Сроки и условия участия</h2>
<p>Конкурс «Лето с Intel для профессионального роста» проходит с 7 сентября по 26 октября 2009 года.<br />Cроки представления конкурсных работ истекают 6 октября 2009 года в 21:00 по московскому времени.<br />В первой половине октября 2009 года судейская комиссия определит победителей конкурса, занявших первое, второе и третье места.</p>
<p>Для участия в конкурсе необходимо:</p>
<ul>
<li><a href="https://fm1cedar.cps.intel.com/isn/registration/isnRegpage.aspx?Lang=RUS&amp;TARGET=http%3A%2F%2Fsoftware.intel.com%2Fru-ru%2F">Зарегистрироваться</a> в сообществе Intel® Software Network.</li>
<li>Заполнить учетную запись участника сообщества Intel® Software Network, включая контактную информацию: ФИО, географический адрес и телефон.</li>
<li>Опубликовать на сайте Intel® Software Network (<a href="http://software.intel.com/ru-ru/">http://software.intel.com/ru-ru/</a>) техническую статью с описанием разработанного проекта или выполненного исследования.</li>
</ul>
<p>Содержание и оформление конкурсных работ должны удовлетворять <a href="http://software.intel.com/ru-ru/articles/contest-summer-school-2009-article-requirements/">требованиям</a>, предъявляемым организаторами данного конкурса.<br />Подробнее о сроках и условиях участия читайте на странице <a href="http://software.intel.com/ru-ru/articles/contest-summer-school-2009-rules/">официальных правил</a> конкурса «Лето с Intel для профессионального роста».</p>
<h2 class="sectionHeading">Критерии и принципы оценки конкурсных работ</h2>
<p>Для определения победителей конкурса будут использованы следующие критерии оценки:</p>
<ul>
<li>Оригинальный, инновационный подход к исследованию/разработке;</li>
<li>Практические результаты, полученные в ходе работы над проектом;</li>
<li>Оформление статьи, стиль изложения, полнота предоставленных материалов;</li>
<li>Мнение сообщества, на основании количества просмотров статей и комментариев.</li>
</ul>
<p>Победителем конкурса является участник, статья которого получила наивысший суммарный рейтинг судейской комиссии с учетом мнения сообщества. <br />Подробнее о критериях оценки и принципах судейства читайте на странице <a href="http://software.intel.com/ru-ru/articles/contest-summer-school-2009-rules/">официальных правил</a> конкурса «Лето с Intel для профессионального роста».</p>
<a name="prizes" id="prizes"></a>
<h2 class="sectionHeading">Информация о призах</h2>
<p>Никто без приза не останется – всех ждут памятные сувениры. Участники, занявшие первое, второе и третье места получат:</p>
<p>Первое место:</p>
<p><img height="150" width="500" src="http://software.intel.com/file/22000" align="right" alt="Призы конкурса" /></p>
<ul type="disc">
<li>Нетбук Lenovo IdeaPad S10-2</li>
</ul>
<p>Второе место:</p>
<ul>
<li>Устройство для чтения книг PocketBook 301</li>
</ul>
<p>Третье место:</p>
<ul type="disc">
<li>Цифровая фоторамка Sony DPF–D82 Black</li>
</ul>
<a name="judges" id="judges"></a>
<h2 class="sectionHeading">Состав жюри</h2>
<p><strong>Александр Лазарев</strong>, инженер группы поддержки разработки программного обеспечения, Software Solutions Group<br />В Интел работает около 6 лет, занимается оптимизацией программного обеспечения сторонних производителей. Также работал инженером по тестированию Intel® Threading Tools на приложениях. До Интел занимался тестированием встроенного программного обеспечения средств связи Моторола и Алкатель. Закончил кафедру прикладной матемтики Физико-Механического факультета Санкт-Петебрургского Государственного Технического Университета.</p>
<p><strong>Алексей Александров</strong>, старший инженер по ПО, архитектор программных продуктов Intel для анализа производительности приложений.</p>
<p><strong>Николай Куртов</strong>, инженер-практикант по ПО. Разработчик библиотеки параллельного программирования Intel® Concurrent Collections for C++.</p>
<p><strong>Валерий Курякин</strong>, кандидат физико-математических наук. Работал в Intel менеджером по инициативам в области новых технологий и продуктов. В настоящее время занимается поиском и оценкой новых технологических и бизнес–проектов в области IT-приложений. Был руководителем и инициатором десятков проектов, принимал участие в оценке многих конкурсов.</p>
<p><strong>Максим Перминов</strong>, инженер-программист. Работает с приложениями из области HPC (High Performance Computing), занимается их адаптацией к архитектуре процессоров Интел, а также последовательной и параллельной оптимизацией.</p>
<p><strong>Дмитрий Мишура</strong>, старший инженер-программист. Область интересов: HPC, вычислительные кластеры, MPI. Занимается предпродажной поддержкой вендоров, оптимизацией ПО, бенчмаркингом.</p>
<p><strong>Андрей Марочко</strong>, в разработке ПО с 1992 года, профессионально - с 1999. Работал в различных областях - от численного анализа до сложных GUI решений и от распределенных клиент-серверных систем до написания библиотек для разработчиков. Начал активно использовать многопоточность в конце 90-х. C 2004 года – старший инженер в Интеле, сначала в отделе Threading Tools, а с 2007 - в Threading Runtimes. Основные направления деятельности - развитие функциональности планировщика задач TBB и параллельных алгоритмов, а также поддержка взаимодействия TBB c другими параллельными технологиями (OpenMP, Cilk) и с инстументами анализа корректности и производительности (Thread Checker, Thread Profiler, Parallel Amplifier).</p>
<p><strong>Андрей Чурбанов</strong>, старший инженер по программному обеспечению, работает в проекте по реализации стандарта OpenMP в компиляторе Интел.</p>
<p><strong>Роман Шарыгин</strong>, инженер программного обеспечения, Software Solutions Group. Работает в команде Performance, Analysis and Threading, которая отвечает за качество выпускаемых продуктов, как уже выпущеных (VTune, Parallel Studio), так и еще готовящихся к выпуску.</p>
<p><strong>Василий Липсюк</strong>, старший инженер ПО, работает в SSG-VCSD/CIP/IPP группе.</p>
<p><strong><a href="http://software.intel.com/ru-ru/blogs/author/dmitry-oganezov/">Дмитрий Оганезов</a></strong>, менеджер сообщества разработчиков ПО Intel Software Network.</p>
<h2 class="sectionHeading">Поддержка</h2>
<p>Если у вас появятся вопросы к организаторам конкурса, пожелания и замечания, пожалуйста, оставляйте их в <a href="http://software.intel.com/ru-ru/forums/contest-summer-school-2009/topic/68134/">специальной ветке форума</a> или в виде комментария к данной статье.</p> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/contest-summer-school-2009</link>
      <pubDate>Tue, 20 Oct 2009 06:39:36 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/contest-summer-school-2009#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/contest-summer-school-2009</guid>
      <category>ISN General</category>
      <category>Сообщество разработчиков программного обеспечения</category>
      <category>События</category>
    </item>
    <item>
      <title>Инструмент для поиска потенциальных возможностей проведения кэш-оптимизаций</title>
      <description><![CDATA[ <h2 class="sectionHeading">Аннотация</h2>
<p>В статье описывается работа над проектом, целью которого было создание инструмента для поиска потенциальных возможностей проведения кэш-оптимизаций, состоящего из трех компонент: инструментатора бинарного кода для сбора статистики доступа к RAM, статистического анализатора данных и графического интерфейса для представления полученных результатов.</p>
<h2 class="sectionHeading">Введение</h2>
<p>Г. Маркес был прав, говоря, что самое лучшее в нашей жизни происходит неожиданно. И случайно замеченное мной на просторах Интернета приглашение в Летнюю Школу Intel - яркий тому пример. Сегодня, находясь в Иркутске и выбрав тему диплома непосредственно связанную с тематикой моего проекта в Летней Школе, я понимаю, что один месяц, проведенный на стажировке в Новосибирском Академгородке, оказал достаточно сильное влияние на мою дальнейшую деятельность. В этой статье мне бы хотелось познакомить вас с идеей проекта, рассказать о уже проделанной работе, а также о перспективах развития данной темы.</p>
<h2 class="sectionHeading">Об идее проекта</h2>
<p>«Создание инструмента для поиска потенциальных возможностей проведения кэш-оптимизаций» - таково название проекта, над которым кроме меня работал Антон Астафьев, а курировал проект ментор от Intel Дмитрий Буданов.</p>
<p>В данном случае нам нужно было рассмотреть возможность повышения производительности программы посредством увеличения эффективности доступа к памяти, зависящей от структуры кода. Реализация проекта состояла из реализации трех его компонент, а именно: инструментатора бинарного кода для сбора статистики доступа к RAM, статистического анализатора данных, графического интерфейса для представления полученных данных. В общем виде суть проекта можно представить следующей схемой:</p>
<p><img src="http://software.intel.com/file/22433" alt="Суть проекта" /></p>
<p><i>Рис. 1. Суть проекта</i></p>
<p>Говоря о структуре кода, необходимо сказать о видах существующих цикловых оптимизаций, т.к. именно циклы зачастую оказываются «узкими местами» программы и, изменяя их структуру, в большинстве случаев, можно достичь более эффективной работы программы с памятью. Ниже приведены наиболее известные цикловые оптимизации:</p>
<ul>
<li>Loop invariant code motion (вынесение инвариантов цикла) - оптимизация, которая находит и выносит за пределы цикла выражения независящие от индексных переменных цикла;</li>
<li>Loop fusion (объединение циклов) - объединение циклов, увеличивающее инструкционный параллелизм и позволяющее интенсивнее загружать вычислительные устройства процессора;</li>
<li>Loop distribution (разбиение циклов) - разбиение циклов способное улучшить производительность за счет улучшения работы с памятью;</li>
<li>Loop unrolling (развертка цикла) - техника призванная уменьшить количество условных переходов в цикле. Несколько итераций цикла объединяются в одну;</li>
<li>Loop unswitching (размыкание цикла) - оптимизация, состоящая в вынесении условия за пределы цикла и дублирования тела цикла с помещением соответствующих вариантов в соответствующие ветви условия;</li>
<li>Loop interchange (Перестановка циклов) - оптимизация, при которой меняется порядок циклов;</li>
<li>Loop tiling (разбиение цикла на блоки) - метод оптимизации состоящий в разбиении пространства итерирования исходного цикла на небольшие блоки меньшего размера, что позволяет хранить используемые в этих небольших блоках данные в кэше полностью для их неоднократного использования в процессе выполнения блока.</li>
</ul>
<p>Таким образом, выявив с помощью статистического анализатора и представив визуально наиболее «проблемные» участки программного кода, можно предположить, что эти участки следует рассмотреть на предмет применения одной или ряда цикловых оптимизаций. Не следует забывать, что результаты, полученные при компиляции программы без оптимизаций компилятора и с ними, будут отличаться друг от друга, что в свою очередь позволяет использовать инструмент при тестировании компиляторов.</p>
<h2 class="sectionHeading">Средства, способы и алгоритмы реализации</h2>
<p>Для реализации первой компоненты нам понадобилась утилита Pin. <a target="_blank" href="http://www.pintool.org/" title="Pin">Pin</a> - Dynamic Binary Instrumentation Tool, утилита, которая внедряется в анализируемый процесс непосредственно перед стартом и позволяет отслеживать выполнение практически любых инструкций, предоставляет API доступа к содержимому регистров, контексту выполнения программы, символьной и отладочной информации. В нашем случае, мы использовали API, позволяющие инструментировать инструкции доступа к памяти.</p>
<p>Таким образом, в БД записывался адрес инструкции программы, происходящей в каждый n-ый момент времени, адрес ячейки памяти, к которой она обращалась и тип доступа. Т.к. при выполнении даже самой простой программы количество происходящих событий измеряется тысячами, то в качестве параметра n задавался шаг, с которым считывалась необходимая нам информация.</p>
<p>Что касается БД, то мы использовали <a target="_blank" href="http://www.sqlite.org/ ">SQLite</a> - свободно распространяемую реляционную базу данных, не использующую парадигму «клиент-сервер», а основанную на встраиваемом движке. SQLite хранит всю базу данных (включая определения, таблицы, индексы и данные) в единственном стандартном файле на том компьютере, на котором исполняется программа.</p>
<p>Наиболее сложным и самым важным оказалось создание статистического анализатора полученных данных, функция которого заключалась в выявлении областей (кластеров) с наибольшей плотностью обращения инструкций к сегменту памяти. В итоге нами были предложены следующие реализации:</p>
<p><b>Реализация №1:</b> Определение границ кластеров по каждому измерению с помощью фильтра LoG, фильтрация по плотности наполнения областей.</p>
<p>Фильтр LoG (Laplacian of Gaussian) - сглаживание ряда данных по методу Гаусса, взятие второй производной и определение границ кластера по смене её знака. Чтобы применить фильтр формируются временные таблицы следующей структуры: адрес инструкции, число её повторений (для выявления диапазонов адресов инструкций) и адрес ячейки памяти, число обращений к этой ячейки (для выявления диапазонов адресов ячеек памяти).</p>
<p><b>Реализация №2:</b> Сортировка собранной статистики пространственным деревом поиска (окто-деревом), фильтрация по плотности и восходящее объединение кластеров.</p>
<p>Кластеризацию на окто-дереве можно описать следующими характеристиками:</p>
<ul type="disc">
<li>3D пространство данных;</li>
<li>Равномерное распределение по каждому измерению - O(n);</li>
<li>Заполнение дерева - O( n x Log<sub>8</sub>n ) ~ 7n;</li>
<li>Автоматическая балансировка загрузки памяти;</li>
<li>Связная структура для быстрого объединения;</li>
<li>Гибкое распараллеливание;</li>
<li>Использование гипотезы λ-компактности: критерии одинаковой плотности и близости объемов.</li>
</ul>
<p>Результат обеих реализаций сведен к единому формату - списку кластеров. Кластер представляет собой пересечене диапазонов времени, адресов инструкций и адресов данных.</p>
<p>Надо сказать, что вопрос наиболее эффективного метода обработки данной статистики еще не закрыт и является основным направлением нашего дальнейшего исследования в этом проекте. В рамках Летней Школы мы представили результаты, полученные методами, описанными выше. Графический интерфейс был разработан на языке Java с помощью библиотек Swing и <a target="_blank" href="http://www.jfree.org/jfreechart/" title="JfreeChart">JfreeChart</a>, в среде NetBeans 6.5.</p>
<h2 class="sectionHeading">О проблемах</h2>
<p>Думаю, было бы странно, если при работе над проектом не возникло бы ни одного затруднения. В этом проекте мне пришлось впервые столкнуться с достаточно большими объемами данных. Индексация и обработка баз данных занимала довольно много времени на ПК средней производительности. Благодаря тому, что у нас был доступ к использованию кластера, нам удалось в разы сократить время работы инструмента. Кроме того, из-за большого объема данных (миллионы записей) терялась точность выявления проблемных областей. Объем данных в свою очередь был связан с выбором шага инструментации. В итоге было принято решение, менять шаг инструментации в соответствии с размером исследуемого кода.</p>
<h2 class="sectionHeading">Результаты</h2>
<p>Исследовав предметную область, выбрав средства реализации и устранив возникшие проблемы, мы перешли к реализации задуманного. Благодаря организованной работе команды (Антон занимался реализацией первого компонента и метода, основанного на окто-деревьях, а я реализацией третьего компонента и метода, использующего фильтр LoG) и грамотному управлению проектом со стороны нашего куратора, был создан инструмент, архитектура которого представлена на рис.2.</p>
<p><img src="http://software.intel.com/file/22435" alt="Архитектура инструмента" /></p>
<p><i>Рис. 2. Архитектура инструмента</i></p>
<p>На сегодня, так скажем, в первой версии программы, инструментатор бинарного кода отделен от статистического анализатора и графического интерфейса. То есть инструмент состоит из двух частей. На вход первой, консольной части, подается исполняемый бинарный код, на выходе формируется файл базы данных, содержащий необходимые для статистического анализатора данные и используемый во второй части. Во второй части сначала выбирается файл базы данных, затем метод кластеризации. Результаты формируются в виде таблицы кластеров со значимой плотностью, диаграмм диапазонов адресов памяти и адресов инструкций и графика пересечений этих диапазонов.</p>
<p>Ниже вы видите график пересечений диапазонов и общий вид графического интерфейса.</p>
<p><img src="http://software.intel.com/file/23142" alt="plot.jpg" title="plot.jpg" /></p>
<p><i>Рис. 3. График пересечений диапазонов</i></p>
<p>На графике по оси Х указаны адреса ячеек памяти, а по оси Y адреса инструкций. Синим цветом показано, что два диапазона адресов инструкций обращаются к одному и тому же диапазону адресов памяти. Данное пересечение указывает на потенциальную «проблемную» область.</p>
<p>К сожалению, нам не хватило времени протестировать инструмент на реальных приложениях, на скриншоте представлены результаты, полученные при работе с приложениями тестового набора Polyhedron.</p>
<p><img src="http://software.intel.com/file/23141" alt="gui.jpg" title="gui.jpg" /></p>
<p><i>Рис. 4. Общий вид графического интерфейса</i></p>
<h2 class="sectionHeading">Перспективы</h2>
<p>Я не случайно написала выше именно о первой версии программы. В рамках Летней Школы был заложен хороший фундамент для дальнейшего исследования данной предметной области и работы над проектом, поэтому останавливаться на достигнутом мы не собираемся. Развивать проект предполагается в следующих направлениях:</p>
<ul type="disc">
<li>Аналитическая обработка данных. Дальнейшее исследование предметной области и методов анализа данных необходимо для создания более эффективного статистического анализатора.</li>
<li>Визуализация в 3D. Для представления общей картины нужно будет сформировать куб, измерениями которого будут: адрес инструкции, адрес ячейки памяти, момент времени. Таким образом, интересующие нас фрагменты кода будут представлены в виде наиболее плотных областей в этом кубе. Тип доступа предполагается выделять цветом. </li>
<li>Объединение компонентов. Следует объединить инструментатор бинарного кода со статистическим анализатором и графическим интерфейсом пользователя, что возможно повлечет за собой незначительные изменения в структуре базы данных.</li>
<li>Экспертная система для помощи в выявлении потенциальных возможностей кэш-оптимизаций. На данном этапе инструмент предлагает в табличном и графическом виде только факты нахождения проблемных областей, и пользователь сам должен сделать выводы о том, какие корректировки можно провести в структуре кода. Было бы неплохо создать модуль, действующий на основе базы знаний о возможных оптимизациях.</li>
<li>Тестирование на реальных приложениях. Необходимо уделить больше времени тестированию инструмента, провести сравнительные анализы выполнения на разных аппаратных и программных платформах.</li>
</ul>
<h2 class="sectionHeading">Заключение</h2>
<p>Как видите, впереди много интересной работы. Возможно, пройдет время, и на сайте сообщества появится статья о второй версии инструмента, а пока я прощаюсь с вами. Благодарю за внимание.</p>
<h2 class="sectionHeading">Об авторе и летней школе Intel</h2>
<p>Жданова Наталия Юрьевна, студентка Байкальского Государственного Университета Экономики и Права ( факультет Экономической Кибернетики), проходила стажировку в рамках Летней Школы Intel в Новосибирске. Для меня стажировка в Летней Школе стала самым ярким событием этого лета. Интенсивная работа, интереснейшие лекции и тренинги, общение с единомышленниками, экскурсии - четыре сумасшедших недели за которые я расширила свой кругозор, нашла новых друзей, получила опыт работы в команде, изменила взгляд на науку и многое другое. Если бы стажировка длилась всё лето, я бы не задумываясь провела его с Intel, потому что как оказалось, лето и Intel очень даже совместимы!</p>
<h2 class="sectionHeading">Используемая литература и ссылки на ресурсы</h2>
<ol>
<li>Презентация «Проект №5: Создание инструмента для поиска потенциальных возможностей проведения кэш-оптимизаций» Астафьев А., Жданова Н.</li>
<li>Презентация «Современные оптимизирующие компиляторы» Ануфриенко А.</li>
<li><a target="_blank" href="http://www.ixbt.com/soft/intel-parallel-inspector.shtml">http://www.ixbt.com/soft/intel-parallel-inspector.shtml</a> - «Intel Parallel Inspector - поиск ошибок доступа к памяти» В. Цымбал.</li>
<li><a target="_blank" href="http://en.wikipedia.org/wiki/Loop_optimization">http://en.wikipedia.org/wiki/Loop_optimization</a> - Loop optimization.</li>
<li><a target="_blank" href="http://www.pintool.org/">http://www.pintool.org/</a> - сайт, посвященный утилите Pin(Dynamic Binary Instrumentation Tool.</li>
<li><a target="_blank" href="http://www.sqlite.org/">http://www.sqlite.org/</a> - официальный сайт БД SQLite.</li>
<li><a target="_blank" href="http://www.jfree.org/jfreechart/">http://www.jfree.org/jfreechart/</a> - официальный сайт графической библиотеки Jfreechart.</li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/potential_opportunities_of_cache_optimizations</link>
      <pubDate>Mon, 19 Oct 2009 06:40:46 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/potential_opportunities_of_cache_optimizations#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/potential_opportunities_of_cache_optimizations</guid>
      <category>Сообщество разработчиков программного обеспечения</category>
    </item>
    <item>
      <title>Безопасность 64-битного кода</title>
      <description><![CDATA[ <h2>Аннотация</h2>
<p>В статье рассматриваются вопросы обеспечения безопасности программного кода при его адаптации для 64-битных систем.</p>
<h2>Введение</h2>
<p>Не будем говорить об угрозе взлома программного обеспечения и о размере вреда, который эта угроза может причинить. Об этом написаны многие книги и статьи. Перейдем сразу к новому практическому вопросу в сфере повышения надежности программного кода, связанному с освоением <a href="http://www.viva64.com/terminology/64-bit_rus.html">64-битных</a> систем. И скорее всего, вас не удивит, что речь пойдет о языках Си/Си++, для которых вопросы обеспечения безопасности стоят наиболее остро.</p>
<p>Из-за ошибок и недочетов программный код при переносе с 32-битных на 64-битные системы может стать более восприимчив к атакам, основанных на переполнении буферов. Это связанно с изменением размеров базовых типов данных, что может дать возможность воспользоваться ранее недоступными путями атаки на код. Другими словами код, которой в рамках 32-битных систем был безопасен и не давал возможности использовать его для целей вторжения в систему, после перекомпиляции для 64-битных систем может начать представлять потенциальную угрозу.</p>
<p>Проблема безопасности 64-битного кода не является новой областью в сфере защиты информации. Проблемы различного поведения кода и возможности его взлома всегда зависели от используемой аппаратной платформы. Но массовый переход на 64-битные системы выделяет задачи по обеспечению безопасности 64-битного кода в отдельную категорию, которая заслуживает повышенного внимания и отдельного исследования. В данной статье мы сделаем попытку коснуться проблем безопасности 64-битного кода и показать разработчикам программного обеспечения и систем защиты на этот новый источник потенциальной опасности при разработке современных 64-битных решений.</p>
<h2>Анализ программного кода</h2>
<p>Существуют различные подходы к обеспечению безопасности программного кода. Мы остановимся на методе <a href="http://www.viva64.com/terminology/Static_code_analysis_rus.html">статического анализа кода</a>, так как он наиболее подходит для задачи поиска уязвимостей при переносе кода на другую платформу.</p>
<p>Существует достаточно много различных инструментов статического анализа, обеспечивающих диагностику потенциально опасных участков кода, которые могут быть использованы для различных видов атак. В качестве примера можно привести: <a href="http://www.citigal.com/its4/">ITS4</a>, <a href="http://www.cigital.com/">SourceScope</a>, <a href="http://www.dwheeler.com/flawfinder/">Flawfinder</a>, <a href="http://www.npo-echelon.ru/index.php">АК-ВС</a> [1].</p>
<p>Кстати, недавно познакомился с интересным фактом. Я всегда рассматривал инструменты статического анализа кода, как средства поиска ошибок в программах, с целью сделать ее более надежной и устойчивой к входным данным. Но оказывается, хакеры также используют инструменты статического анализа, но с противоположной целью [2]. Они выявляют потенциально ненадежные места в программах, для дальнейшего их подробного изучения. Вручную просматривать код современных приложений практически нереально из-за их размеров, и статический анализ оказывается им хорошим подспорьем. После дизассемблирования кода, хакеры с помощью статического анализа отсеивают наиболее интересные области кода для изучения. Например, они могут искать код, использующий копирование строк и при этом наличие рядом уменьшение/увеличение регистра или ячейки памяти на единицу. Программисты достаточно часто допускают ошибки при работе со строками, когда приходится резервировать дополнительный байт под терминальный символ 0x00 (конец строки). Этот код обычно содержит магические арифметические комбинации, где присутствует -1 или +1. И такой код конечно очень интересен для подробного изучения хакером, поскольку потенциально он может помочь осуществить атаку на основе переполнения буфера.</p>
<p>Но мы отвлеклись. Статические анализаторы помогают программисту выявить потенциально уязвимые участки кода в своих программах и их пользу нельзя недооценивать. Рассмотрим теперь некоторые примера кода, который станет опасным или даже некорректным при переносе на 64-битную систему.</p>
<h2>Примеры некорректного и уязвимого кода</h2>
<p>С большой коллекцией ошибок, которые возникают в 64-битных программах можно познакомиться в статьях "<a href="http://www.viva64.com/art-1-1-1958348565.html">20 ловушек переноса Си++ - кода на 64-битную платформу</a>" [<a href="http://www.viva64.com/art-1-1-1958348565.html">3</a>] и "<a href="http://www.viva64.com/art-1-1-1757483679.html">Проблемы 64-битного кода на примерах</a>" [<a href="http://www.viva64.com/art-1-1-1757483679.html">4</a>]. Но в данных статьях акцент сделан на ошибки, которые приводят к неработоспособности программы, а не с точки зрения ее уязвимости для атаки.</p>
<p>К сожалению, автору не удалось найти систематических работ по вопросам обеспечения безопасности 64-битного кода. И, по всей видимости, выделение паттернов уязвимостей, специфичных для 64-битных систем является новой задачей, еще ожидающей различных исследований. Попробуем все-таки рассмотреть некоторые примеры.</p>
<p>Одним из методов атаки может стать передачу в программу большого объема данных, превышающий, например, размер 4 Gb. Разберем пример чтения данных из файла.</p>
<pre name="code" class="cpp">void *SpecificMalloc(unsigned int size) {
return malloc(size);
} 
...
char *buf;
size_t len; 
read(fd, &amp;len, sizeof(len)); 
buf = SpecificMalloc(len);
read(fd, buf, len);
</pre>
<p>Напомним, что в 64-битных системах (Linux, Windows) размер типа int составляет 32-бита, а <a href="http://www.viva64.com/terminology/size_t_rus.html">size_t</a> - 64-бита. Ошибка заключается в приведении типа size_t к типу unsigned int при вызове функции SpecificMalloc. Если размер файла будет больше 4 гигабайт, то при чтении данных из файла мы выйдем за границы массива, что является недопустимой ситуацией. Конечно, ошибка в данном примере очевидна, но пример показывает опасность явного и неявного приведения типов, которое может возникнуть в 64-битном коде, смешено использующие 32-битные и 64-битные типы для хранения размеров, индексов и так далее.</p>
<p>Другая разновидность угроз кроется в использовании фиксированных размеров буферов и магических констант. Особенно этим грешит старый код, написанный с десяток лет назад программистами, которые не задумывались, что когда-то изменится размер указателя или перемененной типа time_t.</p>
<p>Рассмотрим простой пример переполнения буфера с жестко заданным размером:</p>
<pre name="code" class="cpp">char buf[9];
sprintf(buf, "%p", pointer);
</pre>
<p>Да, такое бывает в программах. Особенно в старых.</p>
<p>Приведем другой пример, где использование магического числа 4 приводит к ошибке выделения необходимого объема памяти:</p>
<pre name="code" class="cpp">LPARAM *CopyParamList(LPARAM *source, size_t n)
{
  LPARAM *ptr = (LPARAM *)malloc(n * 4);
  if (ptr)
    memcpy(ptr, source, n * sizeof(LPARAM);
  return ptr;
}
</pre>
<p>Неожиданному изменению может подвергнуться и логика работы программы:</p>
<pre name="code" class="cpp">int a = -2;
unsigned b = 1;
ptrdiff_t c = a + b;
if (c == -1)
{
  printf("Case: 32-bit\n");
} else {
  printf("Case: 64-bit\n");
}
</pre>
<p>В этом неаккуратном коде в зависимости от разрядности платформы будут выполняться различные ветки в оператора 'if'. Согласно правилам языка Си++ выражение "<a href="http://www.viva64.com/terminology/ptrdiff_t_rus.html">ptrdiff_t</a> c = a + b;" будет вычисляться следующим образом:</p>
<ul>
<li>Значение типа int, равное -2 будет преобразовано в тип unsigned со значением 0xFFFFFFFEu.</li>
<li>Произойдет сложение двух 32-битных значений 0x00000001u и 0xFFFFFFFEu, результатом чего станет число 0xFFFFFFFFu размерностью 32-бита.</li>
<li>Значение 0xFFFFFFFFu будет помещено в 64-битную переменную знакового типа. Для 32-битной системе это означает, что переменная будет содержать значение -1. В случае 64-битной системы переменная так и будет иметь значение 0xFFFFFFFF.</li>
</ul>
<p>Подобные эффекты опасны не только в логических выражениях, но и при работе с массивами. Определенное сочетание данных в следующем примере приведет к записи за пределами массива на 64-битнйо системе:</p>
<pre name="code" class="cpp">int A = -2;
unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };
int *ptr = array + 3;
ptr = ptr + (A + B);
*ptr = 10; // Обращение к памяти за пределами массива
           // в случае 64-битной среды.
</pre>
<p>Подобную ошибку можно использовать, если удастся установить некорректное значение переменных A и B таким образом, чтобы произвести запись в желаемую область памяти.</p>
<p>Ошибки в логике программы легко могут возникнуть и в коде обрабатывающем отдельные биты. Следующий тип ошибки связан с операциями сдвига. Рассмотрим пример:</p>
<pre name="code" class="cpp">ptrdiff_t SetBitN(ptrdiff_t value, unsigned bitNum) {
  ptrdiff_t mask = 1 &lt;&lt; bitNum;
  return value | mask;
}
</pre>
<p>Приведенный код работоспособен на 32-битной архитектуре и позволяет выставлять бит с номерами от 0 до 31 в единицу. После переноса программы на 64-битную платформу возникнет необходимость выставлять биты от 0 до 63. Но данный код никогда не выставит биты, с номерами 32-63. Обратите внимание, что "1" имеет тип int и при сдвиге на 32 позиции произойдет переполнение. В результате мы получим значение 0 или 1, что зависит от реализации компилятора. Заметим также, что неисправленный код приведет еще к одной интересной ошибке. При выставлении 31-ого бита на 64-битной системе результатом работы функции будет значение 0xffffffff80000000. Результатом выражения "1 &lt;&lt; 31" является отрицательное число -2147483648. Это число представляется в 64-битной целой переменной как 0xffffffff80000000.</p>
<p>Потенциально манипулирую с входными данными подобных некорректных функций можно получить недопустимые права, если например, происходит обработка масок прав доступа, заданных отдельными битами.</p>
<p>Если приводимые выше примеры кажутся вам надуманными и нереальными, то предлагаю познакомиться с еще одним кодом (в упрощенном виде), который использовался в реальном приложении в подсистеме UNDO/REDO, хотя выглядит он, пожалуй, наиболее странно:</p>
<pre name="code" class="cpp">// Здесь указатели сохранялись в виде строки
int *p1, *p2;
....
char str[128];
sprintf(str, "%X %X", p1, p2);
// А в другой функции данная строка
// обрабатывалась следующим образом:
void foo(char *str)
{
  int *p1, *p2;
  sscanf(str, "%X %X", &amp;p1, &amp;p2);
  // Результат - некорректное значение указателей p1 и p2.
  ...
}
</pre>
<p>Результатом манипуляций указателями с использованием %X стало некорректное поведение программы на 64-битной системе. Данный пример не столько демонстрирует проблему безопасности 64-битного кода, сколько показывает, как опасны потаенные дебри больших и сложных проектов, которые пишутся многими годами. Если проект достаточно велик и стар, то с большой вероятностью в нем существуют уязвимости и ошибки связанные с предположениями о размерах различных структурах данных, правилах выравнивания данных и много других сюрпризов.</p>
<h2>Диагностика уязвимых мест в 64-битном коде</h2>
<p>Вначале систематизируем типы целей, которые могут стать доступными для атаки при переносе кода на 64-битную систему:</p>
<ol>
<li>Опасны участки кода с арифметическими выражениями, в которых смешанно используются 32-битные и 64-битные типы данных.</li>
<li>Опасны участки кода с адресной арифметикой, где присутствуют операции с 32-битными типами данных.</li>
<li>Внимание заслуживают выражения, содержавшие магические константы, которые могут означать размер типов данных, максимально допустимые значения, смещение данных в структурах данных.</li>
<li>Целью атаки может стать код содержащий операторы сдвига или иные битовые операции. </li>
<li>Потенциальную опасность могут представлять различные операции явного и неявного приведения 32-битных и 64-битных типов между собой.</li>
<li>Высокому риску подвержен код, реализующий чтение или ввод данных в которых присутствуют типы, изменившие свой размер на 64-битной системе.</li>
</ol>
<p>Приведенный список пока нельзя назвать полным, поскольку, это, пожалуй, одна из первых исследовательских статей по теме безопасности кода переносимого на 64-битную систему. Однако даже верификация перечисленных объектов позволит существенно повысить надежность кода и устранить как многие уязвимости, так и ошибки, которые могут возникать даже на корректных данных.</p>
<p>На данный момент нет выделенного продукта для контроля безопасности кода при переносе его на 64-битные системы. Однако существует статический анализатор кода <a href="http://www.viva64.com/ru/pvs-studio/">PVS-Studio</a>, который полностью поддерживает диагностику всех описанных в этой статье 64-битных проблем уязвимости.</p>
<p>Программный продукт PVS-Studio представляет собой разработку российской компании ООО "Системы программной верификации" и предназначен для верификации современных приложений, использующих такие технологии, как параллельное программирование (OpenMP) и 64-битные архитектуры [<a href="http://www.viva64.com/art-4-1-1796251700.html">5</a>]. PVS-Studio встраивается в среду Microsoft Visual Studio 2005/2008, а также в справочную систему MSDN.</p>
<p>Входящая в состав PVS-Studio подсистема <a href="http://www.viva64.com/ru/viva64-tool/">Viva64</a>, помогает специалисту отслеживать в исходном коде Си/Си++-программ потенциально опасные фрагменты, связанные с переходом от 32-битных систем к 64-битным. Анализатор помогает писать безопасный корректный и оптимизированный код для 64-битных систем.</p>
<p>Возможности PVS-Studio покрывают диагностику описанных ранее проблем уязвимостей в 64-битном программном коде. Диагностические возможности данного анализатора избыточны для решения исключительно задач обеспечения безопасности 64-битного кода, поскольку он предназначен не только для выявления потенциальных ошибок, но и например для поиска неоптимальных структур данных. Впрочем, лишние диагностические сообщения легко отключить, используя настройки.</p>
<p>На всякий случай хочется подчеркнуть, что PVS-Studio предназначен для выявления ошибок, возникающих при переносе 32-битных программ в 64-битные системы или при разработке новых 64-битных программ. Но PVS-Studio не диагностирует ошибки, которые могут возникать при использовании небезопасных на любых платформах функций, таких как sprintf, strncpy и так далее. Для диагностики подобных ошибок не следует забывать про уже упомянутые инструменты, такие как ITS4, SourceScope, Flawfinder, АК-ВС. PVS-Studio дополняет ряд этих инструментов, закрывая брешь в диагностики 64-битных проблем, но не заменяет их.</p>
<h2>Заключение</h2>
<p>Занимаясь вопросами обеспечения безопасности, никогда не увлекайтесь каким-то одним направлением, будь то статический или динамический анализ, тестирование на некорректных входных данных и так далее. Надежность системы определяется ее самым слабым звеном. Бывает, что надежность системы можно повысить на порядок простым административным методом, например, таким как замок.</p>
<p>Существует толи легенда, толи правда, что однажды в одной компании при аудите безопасности ей была поставлена самая низкая оценка, еще даже до того, как специалисты приступили к проверке, дублируются ли данные, какое программное обеспечение установлено на сервере и так далее. Просто сервер стоял в одной из комнат с незапирающейся дверью, куда мог зайти кто угодно. Почему? Шумел сильно, вот и переставили его подальше от рабочих кабинетов, чтобы не мешал.</p>
<h2>Библиографический список</h2>
<ol>
<li>Касперски, Крис. Компьютерные вирусы изнутри и снаружи. - СПб. : Питер, 2006. - 526 с. : ил. - ISBN 5-469-00982-3.</li>
<li>Хогланд, Грег, Мак-Гроу, Гарри. Взлом программного обеспечения: анализ и использование кода.: Пер. с англ. - М.: Издательский дом "Вильямс", 2005. - 400 с. : ил. - Парал. тит. англ. ISBN 5-8459-0785-3. ББК 32.973.26-018.2.75.</li>
<li>Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ - кода на 64-битную платформу. <a href="http://www.viva64.com/art-1-1-1958348565.html">http://www.viva64.com/art-1-1-1958348565.html</a></li>
<li>Евгений Рыжков. Проблемы 64-битного кода на примерах. <a href="http://www.viva64.com/art-1-1-1757483679.html">http://www.viva64.com/art-1-1-1757483679.html</a></li>
<li>Евгений Рыжков. Учебное пособие по PVS-Studio. <a href="http://www.viva64.com/art-4-1-1796251700.html">http://www.viva64.com/art-4-1-1796251700.html</a></li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/safety-of-64-bit-code</link>
      <pubDate>Fri, 16 Oct 2009 01:04:19 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/safety-of-64-bit-code#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/safety-of-64-bit-code</guid>
      <category>Параллельное программирование</category>
    </item>
    <item>
      <title>Как оценить процесс 64-битной миграции Си/Си++ приложений? </title>
      <description><![CDATA[ <h2>Аннотация</h2>
<p>Статья посвящена вопросу оценки сложности и стоимости переноса приложений на   64-битные платформы. Рассматриваются такие аспекты, как доступность тех или иных   компонентов приложения, библиотек, средств разработки. Приводится пример   использования программного продукта PVS-Studio для оценки миграции. Хотя   упомянутый продукт PVS-Studio ориентирован на Си и Си++ приложения в системе   Windows, статья также будет полезна разработчикам под Unix и другими системами.</p>
<h2>Введение</h2>
<p>Фразой "не за горами тот день, когда все разработчики должны будут выпустить   64-битные версии своих приложений" я начинаю многие свои статьи еще с 2006 года,   хотя на момент написания статьи подходит к концу год 2009. Жизнь показала, что я   не прав. Тот день "за горами". До сих пор очень многие компании так и не   выпустили 64-битных версий продуктов. Отчасти этому способствует политика   компании Microsoft, которая одинаково успешно зарабатывает деньги как на продаже   32-битных версий Windows, так и 64-битных. Отчасти - тем, что далеко не всем   приложениям действительно необходимо иметь 64-битные версии.</p>
<p>Однако по моему опыту общения с людьми, ответственными за принятие решений о   необходимости разработки 64-битных версий приложений, я понял, что главным   сдерживающим фактором является очень простая вещь. Многие люди просто не знают,   как оценить время и стоимость процесса миграции приложения на 64-битную   архитектуру. Соответственно, это незнание заставляет их откладывать процесс   переноса кода на максимально возможный срок.</p>
<p>В данной статье я расскажу об одном из способов, которые позволяет оценить   этот процесс до начала миграции. Ведь точно сказать, сколько времени занимает   миграция конкретного приложения, можно только после того, как эта миграция   выполнена.</p>
<p>Статья основа на опыте сотрудников компании ООО "СиПроВер" как по переносу   различных приложений на 64-битные системы, так и по практике применения   анализатора кода PVS-Studio.</p>
<h2>"Хьюстон, Хьюстон. Разрешите старт!"</h2>
<p>Настоящая статья посвящена оценке процесса миграции, а не самому процессу   миграции. Поэтому желающим более детально изучить такой вопрос будет интереснее   ознакомиться со статьей "<a href="http://www.viva64.com/art-1-1-1148261225.html">7 шагов по переносу   программы на 64-битную систему</a>" [2]. Тем не менее, здесь я использую   некоторые положения из той статьи. Прежде чем оценивать нюансы миграции, надо   иметь четкие ответы на следующие вопросы:</p>
<ol>
<li>Необходимо ли выполнять именно миграцию кода на 64-битные системы или же   достаточно, чтобы 32-битное приложение работало на 64-битной операционной   системе?</li>
<li>Поддерживает ли используемое в проекте средство разработки генерацию   64-битного кода?</li>
<li>Существуют ли 64-битные версии всех используемых в проекте компонентов и   библиотек?</li>
</ol>
<p>Если вы ответили на все эти вопросы "да", то перед вами встает основной   вопрос, вынесенный в заголовок статьи.</p>
<h2>"Как же оценить процесс 64-битной миграции?"</h2>
<p>Итак, у вас есть несколько (десятков) мегабайт исходного кода, готового к   миграции. Конфигурации для сборки 64-битного кода пока нет, ни одного   компилирующегося в 64-битном режиме модуля, соответственно, тоже. Но прочитав   статью "<a href="http://www.viva64.com/art-1-1-1958348565.html">20 ловушек   переноса Си++ - кода на 64-битную платформу</a>" [4], вы понимаете, что процесс   поиска всех скрытых в коде ошибок будет непростым.</p>
<p>Для того чтобы понять, насколько непростым будет поиск всех ошибок, можно   использовать <a href="http://www.viva64.com/terminology/Static_code_analysis_rus.html">статический   анализ кода</a>. Существуют разные анализаторы кода (<a href="http://www.viva64.com/art-1-1-2853247683.html">сравнение анализаторов   кода</a> [3]), которые можно использовать при поиске проблем в 64-битном коде,   но в моей статье я остановлюсь на анализаторе кода PVS-Studio, поскольку именно   его разрабатывает наша компания.</p>
<p>Итак, в <a href="http://www.viva64.com/ru/pvs-studio/">PVS-Studio</a> 3.30   появилась возможность обнаружения проблем 64-битного кода даже в 32-битных   проектах. Именно эта возможность и позволить оценить сложность миграции ДО этапа   создания 64-битной конфигурации проекта. Ранее анализатор PVS-Studio позволял   проверять проекты только в 64-битной конфигурации.</p>
<p>Я хочу обратить внимание читателей на то, как работает проверка кода в   32-битном режиме. Прежде всего, вы должны понимать, что эта проверка, конечно   же, не может считаться полноценной проверкой, и исправление даже всех   обнаруженных проблем не является гарантией работоспособности кода в 64-битном   режиме. Ведь в коде любого серьезного приложения будут подобные фрагменты:</p>
<pre name="code" class="cpp:nocontrols:nogutter">#ifdef WIN64  ...  #endif  </pre>
<p>Естественно, при проверке в 32-битном режиме подобный код будет пропущен.   Вернее будет сказать так: на момент, когда 64-битной конфигурации пока еще нет,   такого кода в приложении может не быть. А когда он появится, его могут "забыть"   проверить.</p>
<p>Другой важный момент - в зависимости от конфигурации проекта типы данных   естественно отличаются. Поэтому проверка в 32-битном и в 64-битном режиме   практически всегда будет давать разные результаты.</p>
<p>Однако сильно ли будут различаться эти результаты? По результатам   экспериментов, проведенных в нашей компании, мы получили следующие данные:   списки диагностических сообщений от анализатора кода PVS-Studio при проверке   проектов в 32-битном и в 64-битном режимах совпадают на 95-97%! Это означает,   что отличаются не более 5% диагностических сообщений. Эти результаты были   получены следующим образом: мы взяли код реальных проектов, проверили его в   32-битном режиме, сохранили список обнаруженных проблем. Затем проверили код   этих же проектов в 64-битном режиме, сохранили список обнаруженных проблем.   После чего сравнили полученные списки и определили, сколько процентов   диагностических сообщений совпало. Поскольку вся процедура делалась в   автоматизированном режиме, то количество проанализированных проектов было   достаточным (больше 20 проектов с объемом кода в несколько мегабайт каждый).   Следовательно, эти цифры внушают доверие.</p>
<p>Конечно же, спешить исправлять выявленные потенциальные ошибки в 32-битной   конфигурации проекта не стоит - лучше подождать, пока станет доступна 64-битная   конфигурация. А вот оценить, сколько потребуется времени для разбора сообщений   от анализатора кода, вполне возможно.</p>
<p>Для получения оценки времени рекомендую поступить следующим образом:</p>
<ol>
<li>Выполнить анализ 32-битной конфигурации проекта с помощью PVS-Studio.</li>
<li>В течение одного дня программист, понимающий проблемы 64-битного кода,   просматривает сообщения анализатора кода и принимает решение о том, актуальна ли   данная ошибка для данного проекта или нет.</li>
<li>Общее количество сообщений от анализатора кода делится на количество   проанализированных и обработанных программистом сообщений за день.</li>
<li>Полученное число - это количество человеко-дней, необходимое для миграции   кода приложения на 64-битную платформу.</li>
</ol>
<p>Разумеется, в данном алгоритме оценки процесса миграции есть слабое место -   это программист, который будет в течение одного дня разбирать сообщения от   анализатора кода. Поэтому я рекомендую очень серьезно подойти к назначению   программиста, ответственного за оценку.</p>
<p>Вот несколько рекомендаций по выбору такого программиста:</p>
<ol>
<li>Он должен быть опытным программистом со стажем работы не менее трех лет и   желательно со знанием конкретного проекта, который будет портироваться.</li>
<li>Он должен быть знаком с проблемами 64-битного кода. Упоминавшаяся уже ранее   статья "<a href="http://www.viva64.com/art-1-1-1958348565.html">20 ловушек   переноса Си++ - кода на 64-битную платформу</a>" [4] - хороший источник   информации по теме.</li>
<li>Желательно, чтобы он понимал принципы работы со статическими анализаторами   кода. Это необязательное требование, но понимание технологии статического   анализа кода делает оценку процесса миграции более адекватной.</li>
</ol>
<p>Соблюдение этих рекомендаций позволит получить адекватную оценку стоимости и   времени процесса 64-битной миграции приложений.</p>
<h2>Заключение</h2>
<p>Для лучшего понимания этой статьи следует учитывать несколько моментов:</p>
<ol>
<li>Ни один инструмент (в том числе и PVS-Studio) не может выдавать каких-либо   оценок. Оценку всегда выдает человек. Иногда с применением инструментов, иногда   - без них.</li>
<li>Анализатор кода PVS-Studio не предназначен для оценки процесса 64-битной   миграции, он не выдает оценки в часах (днях, месяцах). Это инструмент,   выявляющий потенциальные ошибки в 64-битном коде. Но на основе такой информации   (при проверке 32-битного проекта) человек уже может давать оценки трудозатрат по   переносу проекта.</li>
<li>Качество оценки зависит от человека.</li>
</ol>
<p>Желаю успешной оценки 64-битной миграции кода и надеюсь, что инструмент <a href="http://www.viva64.com/ru/pvs-studio/">PVS-Studio</a> вам в этом   поможет.</p>
<h2>Библиографический список</h2>
<ol>
<li>Анализатор кода PVS-Studio. <a href="http://www.viva64.com/ru/pvs-studio/">http://www.viva64.com/ru/pvs-studio/</a> .</li>
<li>7 шагов по переносу программы на 64-битную систему. <a href="http://www.viva64.com/art-1-1-1148261225.html">http://www.viva64.com/art-1-1-1148261225.html</a> .</li>
<li>Сравнение диагностических возможностей анализаторов при проверке 64-битного   кода. <a href="http://www.viva64.com/art-1-1-2853247683.html">http://www.viva64.com/art-1-1-2853247683.html</a> .</li>
<li>20 ловушек переноса Си++ - кода на 64-битную платформу. <a href="http://www.viva64.com/art-1-1-1958348565.html">http://www.viva64.com/art-1-1-1958348565.html</a> .</li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/estimate-64-bit-migration</link>
      <pubDate>Thu, 15 Oct 2009 07:01:01 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/estimate-64-bit-migration#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/estimate-64-bit-migration</guid>
      <category>Параллельное программирование</category>
    </item>
    <item>
      <title>Неудачная попытка сравнить PVS-Studio (VivaMP) и Intel C/C++ (&amp;#34;Parallel Lint&amp;#34;)</title>
      <description><![CDATA[ <p><a href="#IDAEJI5B"><b>Краткая справочная информация</b></a><br />
<a href="#IDAAKI5B"><b>Введение</b></a><br />
<a href="#IDAKKI5B"><b>Подготовка к проверке ParallelSample с помощью Intel C++ "Parallel Lint"</b></a><br />
<a href="#IDAFNI5B"><b>Анализ проектов и сравнение анализаторов</b></a><br />
<a href="#IDAGPI5B"><b>Пояснение к пунктам сравнения, представленным в таблице 1</b></a><br />
<a href="#IDATVJ5B"><b>Заключение</b></a><br />
<a href="#IDAZWJ5B"><b>Библиографический список</b></a><br />

<h2>Аннотация</h2>
<p>Изначально статья должна была носить название "Сравнение диагностических возможностей PVS-Studio (VivaMP) и Intel C/C++ Compiler ("Parallel Lint"). Отсутствие на данный момент достаточного количества информации о "Parallel Lint" ограничило возможности автора, и статья представляет собой предварительный вариант сравнения. Полный вариант статьи с корректным сравнением будет доступен читателю в будущем. Обратите внимание, что содержание статьи актуально на момент публикации. В дальнейшем возможно изменение диагностических возможностей обоих продуктов.</p>

<h2>Краткая справочная информация<a name="IDAEJI5B"></a></h2>
<p><strong>PVS-Studio (VivaMP)</strong> - статический анализатор параллельного Си/Си++ кода, в котором используется технология параллельного программирования <a href="http://www.viva64.com/terminology/OpenMP_rus.html">OpenMP</a>. Анализатор позволяет диагностировать различные виды ошибок, приводящих к некорректной или неэффективной работе OpenMP программ. Например, анализатор способен выявить некоторые ошибки синхронизации, обнаружить выход исключений за границы параллельных регионов и так далее. VivaMP входит в состав программного продукта <a href="http://www.viva64.com/ru/pvs-studio/">PVS-Studio</a> и представляет собой модуль расширения к среде Visual Studio 2005/2008 [1].</p>

<p><strong>Intel C/C++ Compiler ("Parallel Lint")</strong> - подсистема статического анализа параллельного OpenMP кода, встроенная в компилятор Intel C/C++ начиная с версии 11.1. Для использования Intel C/C++ "Parallel Lint" необходимо создать специальную конфигурацию проекта, но в остальном работа с Intel C/C++ "Parallel Lint" мало чем отличается от обычной работы с компилятором Intel C/C++. Компилятор встраивается в среду Visual Studio 2005/2008. Более подробно с подсистемой "Parallel Lint" можно познакомиться, посмотрев вебинар Дмитрия Петунина "<a href="http://www.viva64.com/go.php?url=228">Static Analysis and Intel C/C++ Compiler ("Parallel Lint" overview)</a>".</p>

<h2>Введение<a name="IDAAKI5B"></a></h2>
<p>Мне как разработчику анализатора VivaMP было крайне интересно сравнить его диагностические возможности с возможностями подсистемы "Parallel Lint", реализованной в Intel C/C++. Дождавшись, когда Intel C/C++ версии 11.1 стал доступен для скачивания, и найдя время для начала исследований, я приступил к сравнению инструментов. К сожалению, это изучение и сравнение не увенчалось успехом, о чем свидетельствует название статьи. Но я уверен, что разработчики найдут в этой статье много интересной информации.</p>
<p>Для начала исследований я решил испробовать Intel C/C++ на демонстрационном примере ParallelSample, входящем в состав PVS-Studio и содержащем ошибки связанные с использованием OpenMP. Программа ParallelSample содержит 23 паттерна ошибок, которые обнаруживаются анализатором VivaMP. Анализ этого проекта должен подтвердить, что анализаторы осуществляют схожую диагностику и их сравнение корректно.</p>

<h2>Подготовка к проверке ParallelSample с помощью Intel C++ "Parallel Lint"<a name="IDAKKI5B"></a></h2>
<p>Скачивание и установка пробной версии Intel C/C++ Compiler версии 11.1.038 прошли легко и не вызвали никаких затруднений. Немного удивил размер дистрибутива, равный почти гигабайту. Но это понятно, учитывая, что я выбрал для скачивания самый полный вариант, включающий в себя MKL, TBB, поддержку IA64 и так далее. После завершения установки я решил, прежде чем приступить к анализу, собрать и запустить программу ParallelSample.</p>
<p>Но с компиляцией демонстрационного примера ParallelSample возникли сложности. Компилятор выдал сообщение "Catastrophic error: unable to obtain mapped memory (see pch_diag.txt)", как показано на рисунке 1.</p>

<p><img alt="" src="http://software.intel.com/file/22812" width="745" height="128"/></p>
<p><em>Рисунок 1. Сообщение компилятора, выданное при попытке собрать приложение ParallelSample.</em></p>
<p>При этом файл pch_diag.txt я у себя не обнаружил и обратился за помощью к Google. И почти сразу обнаружил <a href="http://software.intel.com/en-us/articles/cdiag674/">ветку</a> на форуме Intel, посвященную этой проблеме. Ошибка компиляции связана с использованием предкомпилируемых pch-файлов. В чем именно состоит проблема и как аккуратно изменить настройки, чтобы оставить действующей подсистему предкомпилируемых заголовочных файлов, я разбираться не стал. Для такого маленького проекта как ParallelSample это не имеет значения, и я просто отключил в проекте использование предкомпилируемых заголовков (см. рисунок N2).</p>

<p><img alt="" src="http://software.intel.com/file/22969" width="745" height="277"/></p>
<p><em>Рисунок 2. Отключение предкомпилируемых заголовков в настройках проекта.</em></p>
<p>После этого проект был успешно собран, хотя сразу запустить его не удалось (см. рисунок 3).</p>

<p><img alt="" src="http://software.intel.com/file/22814" width="560" height="218"/></p>
<p><em>Рисунок 3. Ошибка при первом запуске ParallelSample.</em></p><p>Это произошло потому, что я забыл прописать в окружение пути до необходимых DLL. Для этого следует воспользоваться пунктами "C++ Build Environment for applications running on IA-32" и "C++ Build Environment for applications running on Intel(R) 64", доступными через меню "Пуск", как показано на рисунке 4.</p>

<p><img alt="" src="http://software.intel.com/file/22970" width="682" height="486"/></p>
<p><em>Рисунок 4. Установка окружения для работоспособности приложений, собранных с использованием Intel C++.</em></p>
<p>Модификация окружения стала последним шагом, после которого приложение ParallelSample стало успешно компилироваться, запускаться и работать. Теперь можно приступить к самому интересному - к запуску "Parallel Lint".</p>
<p>Наиболее удобным вариантом использования Intel C++ "Parallel Lint" является создание отдельной конфигурации проекта. Дело в том, что нельзя одновременно получить исполняемый файл и диагностируемые сообщения от Intel C++ "Parallel Lint". Можно получить что-то одно. Постоянно менять настройки, чтобы собирать EXE-файл или чтобы получить диагностику от "Parallel Lint" - весьма неудобно. Следует пояснить такое поведение.</p>
<p>В "классическом" режиме компилятор создает объектные файлы с кодом, которые затем объединяются с помощью редактора связей (компоновщик). В результате процесс компиляции выглядит как показано на рисунке 5.</p>

<p><img alt="" src="http://software.intel.com/file/22971" width="745" height="209"/></p>
<p><em>Рисунок 5. Стандартный режим работы компилятора и компоновщика.</em></p>
<p>Для глубокого анализа параллельного кода необходимо владеть информацией обо всех модулях программы. Это позволяет понять, используется ли какая-то функция из одного модуля (cpp-файла) в параллельном режиме из другого модуля (cpp-файла), как показано на рисунке 6.</p>
<p><img alt="" src="http://software.intel.com/file/22817" width="456" height="168"/></p>
<p><em>Рисунок 6. Межмодульное взаимодействие.</em></p>
<p>Чтобы собрать всю необходимую информацию, Intel C++ использует следующую стратегию. Компилятор по-прежнему создает *.obj файлы, но это не объектные файлы, а файлы, содержащие данные, необходимые для последующего анализа. Именно поэтому сборка исполняемого EXE-файла невозможна. Когда компилятор обработает все *.cpp файлы, вместо редактора связей (компоновщика) начинает работать анализатор кода. Опираясь на данные, содержащиеся в *.obj файлах, он диагностирует потенциальные проблемы и выводит соответствующие сообщения об ошибках. В целом процесс анализа можно представить, как показано на рисунке 7.</p>
<p><img alt="" src="http://software.intel.com/file/22972" width="745" height="209"/></p>
<p><em>Рисунок 7. Сбор данных и последующая их обработка анализатором "Parallel Lint".</em></p>
<p>Создание новой конфигурации осуществляется с помощью "Configuration Manager", как показано на рисунках 8-10:</p>
<p><img alt="" src="http://software.intel.com/file/22973" width="745" height="538"/></p>
<p><em>Рисунок 8. Запускаем Configuration Manager.</em></p>
<p><img alt="" src="http://software.intel.com/file/22820" width="498" height="301"/></p>
<p><em>Рисунок 9. Создаем новую конфигурацию с именем "Intel C++ Parallel Lint", на основе существующей.</em></p>
<p><img alt="" src="http://software.intel.com/file/22974" width="745" height="538"/></p>
<p><em>Рисунок 10. Делаем вновь созданную конфигурацию активной.</em></p>
<p>Следующий шаг - непосредственная активация анализатора "Parallel Lint". Для этого необходимо перейти во вкладку "Diagnostics" в настройках проекта (см. рисунок N11).</p>
<p><img alt="" src="http://software.intel.com/file/22975" width="745" height="510"/></p>
<p><em>Рисунок 11. Вкладка "Diagnostics" в настройках проекта, где можно активировать "Parallel Lint".</em></p>
<p>Данная страница настроек содержит пункт "Level of Source Code Parallelization Analysis", задающий уровень анализа параллельного кода. Всего имеется три варианта, как показано на рисунке 12.</p>
<p><img alt="" src="http://software.intel.com/file/22823" width="717" height="231"/></p>
<p><em>Рисунок 12. Выбор уровня диагностики параллельных ошибок.</em></p>
<p>Я выставил максимальный третий уровень диагностики (см. рисунок N13). Включать анализ заголовочных файлов (Analyze Include Files) я не стал, так как проект ParallelSample содержит ошибки только в *.cpp файлах.</p>
<p><img alt="" src="http://software.intel.com/file/22824" width="718" height="241"/></p>
<p><em>Рисунок 13. Выбран максимальный уровень диагностики.</em></p>
<h2>Анализ проектов и сравнение анализаторов<a name="IDAFNI5B"></a></h2>
<p>Подготовка к началу анализа завершена,  теперь можно запустить компиляцию проекта, чтобы получить диагностические сообщения. Именно это и было осуществлено, в результате чего был получен список сообщений. Приводить его здесь нет смысла, тем более что он дополнительно содержит предупреждения, не относящиеся к OpenMP.</p>
<p>Из 23 трех примеров, содержащих ошибки, Intel C++ обнаружил 5 из них. Также он обнаружил одну дополнительную ошибку, возникающую в некорректной функции и не диагностируемую  VivaMP. Количество ложных срабатываний - 2.</p>
<p>Также я попробовал VivaMP на примере parallel_lint, входящем в состав дистрибутива Intel C++. В этом примере содержатся 6 ошибок. Здесь совпадение было полным. И VivaMP, и Intel C++ обнаружили эти 6 ошибок.</p>
<p>Подведем промежуточный итог. Полное совпадение диагностики на примере parallel_lint (из дистрибутива Intel C++) и совпадение диагностики 5 из 23 ошибок на примере ParallelSample (из дистрибутива PVS-Studio) говорит о том, что выбран верный путь. Данные анализаторы подобны, и их сравнение в сфере анализа параллельного OpenMP кода корректно.</p>
<p>Я приготовился к тому, чтобы начать сравнивать анализатор PVS-Studio (VivaMP) и Intel C++ ("Parallel Lint"). Сравнение я планировал осуществить на основании следующих паттернов ошибок:</p>
<ol>
<li>Все паттерны ошибок, которые диагностируются VivaMP и примеры которых представлены в проекте ParallelSample.</li>
<li>Паттерны ошибок, которые мне известны, но пока не реализованы в анализаторе VivaMP. О подобных ошибках можно узнать из статей: "<a href="http://www.viva64.com/art-3-1-464379766.html">32 подводных камня OpenMP при программировании на Си++</a>" [2] и <a href="http://www.viva64.com/art-3-1-1177384583.html">OpenMP и статический анализ кода</a> [3].</li>
<li>Все паттерны ошибок, которые диагностируются Intel C++ ("Parallel Lint") и описание которых должно содержаться в документации к компилятору Intel C++.</li>
</ol>
<p>Хороший план сравнения. Но меня ждало большое разочарование. Третий пункт оказался невозможным для исследования. Я не смог найти в документации Intel C++ описание тех проверок, которые осуществляет Parallel Lint. Поиск в интернете также не дал результатов. Мне удалось обнаружить только разрозненные примеры, демонстрирующие некоторые возможности Parallel Lint.</p>
<p>Тогда я обратился за помощью в сообщество разработчиков Intel, задав вопрос в форуме, где можно подробнее ознакомиться с Parallel Lint. Ответ был неутешителен:</p>
<p><em>Presently, there's no such list in our current product release, other than what's presented in the compiler documentation under Parallel Lint section. But, we are developing description and examples for all messages, planned as of now for our next major release. Will keep you updated as soon as the release containing such a list is out. Appreciate your patience till then.</em></p>
<p>Резюмируя - описание возможностей "Parallel Lint" и примеров нет, и в ближайшее время не будет. Я оказался перед выбором, оставить сравнение и продолжить его, когда появится документация на возможности "Parallel Lint" или провести неполное сравнение, а в дальнейшем пополнить его. Поскольку была проделана уже существенная часть работы, я решил все-таки продолжить написание статьи. В ней я проведу некорректное сравнение анализаторов. И подчеркиваю это. В том числе и названием самой статьи.</p>
<p>Сравнение будет осуществляться на доступных мне на данный момент данных. В будущем, когда данных будет больше, я создам вторую версию этой статьи, где возможности Intel C++ будут учтены более полно. Сейчас сравнение будет осуществлено на основании следующих паттернов ошибок:</p>
<ol>
<li>Все паттерны ошибок, которые диагностируются VivaMP и примеры которых представлены в проекте ParallelSample.</li>
<li>Паттерны ошибок, которые мне известны, но пока не реализованы в анализаторе VivaMP. О подобных ошибках можно узнать из статей: "<a href="http://www.viva64.com/art-3-1-464379766.html">32 подводных камня OpenMP при программировании на Си++</a>" и "<a href="http://www.viva64.com/art-3-1-1177384583.html">OpenMP и статический анализ кода</a>".</li>
<li>Разрозненные примеры паттернов ошибок, которые входят в состав демонстрационного примера parallel_lint, а также те, которые мне удалось собрать из статей о "Parallel Lint", найденных мной в интернете.</li>
</ol>
<p>В таблице 1 представлены результаты некорректного сравнения анализаторов VivaMP и "Parallel Lint". После таблицы будут даны пояснения к каждому из пунктов сравнения. Еще раз подчеркну, что результаты сравнения некорректны. Данные в таблице представлены однобоко. Рассмотрены все ошибки, обнаруживаемые VivaMP и только часть ошибок, обнаруживаемых Intel C++ ("Parallel Lint"). Возможно, Intel C++ ("Parallel Lint") обнаруживает еще 500 неизвестных мне паттернов OpenMP ошибок, и тем самым на порядок превосходит возможности VivaMP.</p>
<p><img alt="" src="http://software.intel.com/file/22968" height="1143" width="745"/></p>
<p><em>Таблица 1. Результаты некорректного (неполного) сравнения анализаторов PVS-Studio (VivaMP) и Intel C++ ("Parallel Lint").</em></p>
<h2>Пояснение к пунктам сравнения, представленным в таблице 1<a name="IDAGPI5B"></a></h2>
<p><em><strong>Пункт 1. Забытая директива parallel</strong></em></p>
<p>Ошибка возникает, когда забыта директива parallel. Эта ошибка относится к классу ошибок допускаемых по невнимательности и приводит к неожиданному поведению кода. Пример, где цикл не будет распараллелен:</p>





<pre name="code" class="cpp">#pragma omp for
for(int i = 0; i &lt; 100; i++) { ... }
</pre>
<p>При этом само по себе отсутствие ключевого слова "parallel" в паре, например со словом "for", вовсе не означает ошибку. Если директива "for" или "sections" находятся внутри параллельной секции, заданной директивой "parallel", то такой код не является подозрительным:</p>





<pre name="code" class="cpp">#pragma omp parallel
{
  #pragma omp for
  for(int i = 0; i &lt; 100; i++)
    ...
}
</pre>

<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1001.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 2. Забытая директива omp</strong></em></p>
<p>Ошибка возникает, когда явно забыта директива omp. Ошибка выявляется даже просто при компиляции кода (без использования Parallel Lint), но все-таки также учтем ее в нашем сравнении. Ошибка относится к классу ошибок по невнимательности и приводит к неожиданному поведению кода. Пример:</p>





<pre name="code" class="cpp">#pragma single
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1002.</p>
<p>Intel C++ (Parallel Lint): диагностируется как warning #161.</p>

<p><em><strong>Пункт 3. Забытая директива for</strong></em></p>
<p>Иногда программист может забыть вписать директиву "for", что повлечет к выполнению двух циклов, а не их распараллеливанию. Разумно предупредить программиста, что код содержит потенциальную ошибку. Пример подозрительного кода:</p>





<pre name="code" class="cpp">#pragma omp parallel num_threads(2)
for(int i = 0; i &lt; 2; i++)
  ...
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1003.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 4. Множественное распараллеливание циклов</strong></em></p>
<p>Код, содержащий множественное распараллеливание, потенциально может содержать ошибку или приводить к неэффективному использованию вычислительных ресурсов. Если внутри параллельной секции используется параллельный цикл, то, скорее всего, это избыточно и только снизит производительность. Пример кода, где возможно логично сделать внутренний цикл не параллельным:</p>





<pre name="code" class="cpp">#pragma omp parallel for
for(int i = 0; i &lt; 100; i++)
{
  #pragma omp parallel for
  for(int j = 0; j &lt; 100; j++)
    ...
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1004.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 5. Забытые order директивы</strong></em></p>
<p>Опасным следует считать совместное использование директив "for" и "ordered", если затем внутри цикла заданного оператором for не используется директива "ordered". Пример:</p>





<pre name="code" class="cpp">#pragma omp parallel for ordered
for(int i = 0; i &lt; 4; i++)
{
  foo(i);
} 
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1005.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 6. Не подключен заголовочный файл &lt;omp.h&gt;</strong></em></p>
<p>Отсутствие включения заголовочного файла &lt;omp.h&gt; в файле, где используются директивы OpenMP, например такие как "#pragma omp parallel for" теоретически может привести к ошибке и является плохим стилем.</p>
<p>Если программа использует OpenMP, то она должна импортировать vcomp.lib/vcompd.lib. В противном случае произойдет ошибка на этапе выполенния, если вы используете компилятор Visual C++. Импорт этой библиотеки осуществляется в  файле omp.h. Поэтому, если в проекте явно не указан импорт нужных библиотек, то #include &lt;omp.h&gt; должно присутствовать хотя бы в одном из файлов проекта. Или импорт библиотек должен быть явно задан в настройках проекта.</p>
<p>В PVS-Studio данное диагностическое сообщение имеет низкий приоритет и активно только в "Pedantic Mode". Поэтому не будет зря вас беспокоить.</p>
<p>Но несмотря на всю свою редкость, это настоящая ошибка, и именно поэтому она попала в сравнение. Приведу практический пример. Открываем проект parallel_lint, входящий в состав Intel C++. Компилируем его с использованием Visual C++. Запускаем и получаем ошибку при запуске исполняемого файла, как показано на рисунке 14.</p>
<p><img alt="" width="558" height="218" src="http://software.intel.com/file/22826" /></p>
<p><em>Рисунок 14. Результат запуска проекта Primes_omp1, собранного компилятором Visual C++ 2005.</em></p>
<p>Причина - забытый #include &lt;omp.h&gt;. Замечу, что если собрать проект с использованием Intel C++, ошибок не возникает.</p>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1006.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 7. omp_set_num_threads внутри параллельной секции</strong></em></p>
<p>Некорректным является вызов функции omp_set_num_threads внутри параллельной секции, заданного директивой "parallel". В Си++ это приводит к ошибкам во время выполнения программы и ее аварийному завершению. Пример:</p>





<pre name="code" class="cpp">#pragma omp parallel
{
  omp_set_num_threads(2);
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1101.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 8. Нечетность количества блокировок и разблокировок</strong></em></p>
<p>Опасным следует считать нечетное использование функций omp_set_lock, omp_set_nest_lock, omp_unset_lock и omp_unset_nest_lock внутри параллельной секции. Пример ошибочного кода:</p>





<pre name="code" class="cpp">#pragma omp parallel sections
{
  #pragma omp section
  {
    omp_set_lock(&amp;myLock);
  }
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1102.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 9. omp_get_num_threads в арифметических операциях</strong></em></p>
<p>Крайне опасным может являться использование omp_get_num_threads в некоторых арифметических выражениях. Пример некорректного кода:</p>





<pre name="code" class="cpp">int lettersPerThread = 26 / omp_get_num_threads();
</pre>
<p>Ошибка заключается в том, что если omp_get_num_threads вернет, например, значение 4, то произойдет деление с остатком. В результате часть объектов не будет обработано. С более подробным примером можно ознакомиться в статье "32 подводных камня OpenMP при программировании на Си++" или в демонстрационном примере Parallel Sample, входящий в  состав дистрибутива PVS-Studio.</p>
<p>Другие выражения, использующие omp_get_num_threads, могут быть вполне корректны. Пример, не вызывающий вывод диагностических сообщений в PVS-Studio:</p>





<pre name="code" class="cpp">bool b = omp_get_num_threads() == 2;
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1103.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 10. omp_set_nested внутри параллеьной секции</strong></em></p>
<p>Некорректным является вызов функции omp_set_nested внутри параллельной секции, заданной директивой "parallel". Пример:</p>





<pre name="code" class="cpp">#pragma omp parallel
{
  omp_set_nested(2);
}
</pre>
<p>Но если функция omp_set_nested находится во вложенном блоке, созданном директивой "master" или "single", то такой код вполне корректен:</p>





<pre name="code" class="cpp">#pragma omp parallel
{
  #pragma omp master
  {
    omp_set_nested(2);
  }
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1104.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 11. Параллельное использование общих ресурсов</strong></em></p>
<p>Опасным следует считать незащищенное использование функций в параллельных секциях, если эти функции используют общие ресурсы. Примеры функций: printf, функции OpenGL.</p>
<p>Пример опасного кода:</p>





<pre name="code" class="cpp">#pragma omp parallel
{
  printf("abcd");
}
</pre>
<p>Пример безопасного кода, когда вызов функции защищен:</p>





<pre name="code" class="cpp">#pragma omp parallel
{
  #pragma omp critical
  {
    printf("abcd");
  }
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): частично диагностируется как ошибка V1201.</p>
<p>Intel C++ (Parallel Lint): частично диагностируется как error #12158.</p>
<p>Диангоностика описана как частичная, по следующим причинам:</p>
<p>В VivaMP список опасных функций далеко не полон и требует расширения.</p>
<p>К сожалению, анализатор Parallel Lint выдает ложное предупреждение, когда вызов функции защищен. По поводу полноты списка функций, к сожалению, ничего не известно.</p>

<p><em><strong>Пункт 12. flush для указателя</strong></em></p>
<p>Директива flush служит для того, чтобы потоки обновили значения общих переменных. При использовании flush для указателя высока вероятность того, что программист ошибся, считая, что произойдет обновление данных по адресу, на которое ссылается указатель. Обновится значение именно переменной, содержащей указатель. Более того, в стандарте OpenMP явно сказано, что переменная-аргумент директивы flush не должна быть указателем.</p>
<p>Пример:</p>






<pre name="code" class="cpp">int *t;
...
#pragma omp flush(t)
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1202.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 13. Использование threadprivate</strong></em></p>
<p>Директива threadprivate является крайне опасной директивой и ее использование рационально только в крайних случаях. По возможности, стоит избегать использования данной директивы. Более подробно с опасностью threadprivate можно познакомится в статье "32 подводных камня OpenMP при программировании на Си++" [<a href="http://www.viva64.com/art-3-1-464379766.html">2</a>].</p>
<p>Пример опасного кода:</p>





<pre name="code" class="cpp">#pragma omp threadprivate(var)
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1203.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 14. Состояния гонки. Статические переменные</strong></em></p>
<p>Одна из ошибок <a href="http://www.viva64.com/terminology/Race_condition_rus.html">состояния гонки</a>. Недопустима инициализация  статической переменной в параллельной секции без специальной защиты. Пример:</p>





<pre name="code" class="cpp">#pragma omp parallel num_threads(2)
{
  static int cachedResult = ComputeSomethingSlowly();
  ...
}
</pre>
<p>Корректный пример с использованием защиты:</p>





<pre name="code" class="cpp">#pragma omp parallel num_threads(2)
{
  #pragma omp critical
  {
    static int cachedResult = ComputeSomethingSlowly();      
    ...
  }
}
</pre>

<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1204.</p>
<p>Intel C++ (Parallel Lint): диагностируется как warning #12246.</p>

<p><em><strong>Пункт 15. Состояние гонки. Общая переменная</strong></em></p>
<p>Одна из ошибок состояния гонки. Два или более потока модифицируют одну общую переменную, которая специально не защищена. Пример:</p>





<pre name="code" class="cpp">int a = 0;
#pragma omp parallel for num_threads(4)
for (int i = 0; i &lt; 100000; i++)
{
  a++;
}
</pre>
<p>Корректный пример с защитой:</p>





<pre name="code" class="cpp">int a = 0;
#pragma omp parallel for num_threads(4)
for (int i = 0; i &lt; 100000; i++)
{
#pragma omp atomic
  a++;
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1204.</p>
<p>Intel C++ (Parallel Lint): диагностируется как warning #12246, warning #12247, warning #12248.</p>

<p><em><strong>Пункт 16. Состояние гонки. Доступ к массиву</strong></em></p>
<p>Одна из ошибок состояния гонки. Два или более потока пытаются работать с одними и теми же элементами массива, используя для вычисления индексов различные выражения. Пример:</p>





<pre name="code" class="cpp">  int i;
  int factorial[10];
  factorial[0]=1;
  #pragma omp parallel for
  for (i=1; i &lt; 10; i++) {
   factorial[i] = i * factorial[i-1];
  }
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): диагностируется как warning #12246.</p>

<p><em><strong>Пункт 17. Состояние гонки. Опасная функция</strong></em></p>
<p>Одна из ошибок состояния гонки. Два или более потока вызывают функцию, принимающую в качестве формального аргумента значение по неконстантной ссылке или неконстантному указателю. При вызове такой функции в параллельной секции в нее передается общая переменная. Никакой защиты при этом не используется.</p>
<p>Пример потенциально опасного кода:</p>





<pre name="code" class="cpp">void dangerousFunction(int&amp; param);
void dangerousFunction2(int* param);
int a = 0;
#pragma omp parallel num_threads(4)
{
  #pragma omp for
  for (int i = 0; i &lt; 100000; i++)
  {
    dangerousFunction(a);
    dangerousFunction2(&amp;a);
  }
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1206.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 18. Состояние гонки. Модификация объекта</strong></em></p>
<p>Одна из ошибок состояния гонки. Два или более потока вызывают неконстантную функцию класса у общего объекта. Никакой защиты при этом не используется.</p>
<p>Пример потенциально опасного кода:</p>






<pre name="code" class="cpp">  MyClass obj;
  #pragma omp parallel for num_threads(2)
  for (int i = 0; i &lt; 100000; i++)
  {
    obj.nonConstMethod();
  }
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1207.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 19. Приватные указатели</strong></em></p>
<p>Опасным следует считать применение директив "private", "firstprivate" и "threadprivate" к указателям (не массивам).</p>
<p>Пример опасного кода:</p>





<pre name="code" class="cpp">int *arr;
#pragma omp parallel for private(arr)
</pre>
<p>Если переменная будет указателем, каждый поток получит по локальной копии этого указателя, и в результате все потоки будут работать через него с общей памятью. Высока вероятность, что в коде имеется ошибка.</p>
<p>Пример безопасного кода, где каждый поток работает со своим собственным массивом:</p>





<pre name="code" class="cpp">int arr[4];
#pragma omp parallel for private(arr)
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1209.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 20. Неосторожное применение lastprivate</strong></em></p>
<p>Директива lastprivate после параллельной секции присваивает переменной значение из лексически последней секции, либо из последней итерации цикла. Код, в котором в последней секции не модифицируется помеченная переменная, по всей видимости, ошибочен.</p>
<p>Пример:</p>





<pre name="code" class="cpp">#pragma omp sections lastprivate(a)
{
  #pragma omp section
  {
    a = 10;
  }
  #pragma omp section
  {
  }
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1210.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 21. Бессмысленный flush. Локальные переменные</strong></em></p>
<p>Бессмысленным следует считать использование директивы flush для локальных переменных (объявленных в параллельной секции), а также переменных помеченных как threadprivate, private, lastprivate, firstprivate.</p>
<p>Директива flush не имеет для перечисленных переменных смысла, так как эти переменные всегда содержат актуальные значения. И дополнительно снижает производительность кода.</p>
<p>Пример:</p>





<pre name="code" class="cpp">int a = 1;
#pragma omp parallel for private(a)
for (int i = 10; i &lt; 100; ++i) {
  #pragma omp flush(a);
  ...
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1211.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 22. Бессмысленный flush. Присутсвует неявный flush</strong></em></p>
<p>Неэффективным следует считать использование директивы "flush" там, где оно выполняется неявно. Случаи, в которых директива "flush" присутствует неявно и в ее использовании нет смысла:</p>
<ul>
<li>в директиве barrier;</li>
<li>при входе и при выходе из секции директивы critical;</li>
<li>при входе и при выходе из секции директивы ordered;</li>
<li>при входе и при выходе из секции директивы parallel;</li>
<li>при выходе из секции директивы for;</li>
<li>при выходе из секции директивы sections;</li>
<li>при выходе из секции директивы single;</li>
<li>при входе и при выходе из секции директивы parallel for;</li>
<li>при входе и при выходе из секции директивы parallel sections;</li>
</ul>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 23. Исключения. Явный throw</strong></em></p>
<p>Согласно спецификации OpenMP, все исключения должны быть обработаны внутри параллельной секции. Выход исключения за пределы параллельной секции приведет к сбою в программе и, скорее всего, к ее аварийному останову.</p>
<p>Пример недопустимого кода:</p>





<pre name="code" class="cpp">try {
  #pragma omp parallel for num_threads(4)
  for(int i = 0; i &lt; 4; i++)
  {
    throw 1;
  }
}
catch (...)
{
}
</pre>
<p>Корректный код:</p>





<pre name="code" class="cpp">size_t errCount = 0;
#pragma omp parallel for num_threads(4) reduction(+: errCount)
for(int i = 0; i &lt; 4; i++)
{
  try {
    throw 1;
  }
  catch (...)
  {
    ++errCount;
  }
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1301.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 24. Исключения. Оператор new</strong></em></p>
<p>Согласно спецификации OpenMP, все исключения должны быть обработаны внутри параллельной секции. Оператор "new" в случае ошибки выделения памяти генерирует исключение, и это необходимо учесть при его использовании в параллельных секциях.</p>
<p>Пример ошибочного кода:</p>





<pre name="code" class="cpp">try {
  #pragma omp parallel for num_threads(4)
  for(int i = 0; i &lt; 4; i++)
  {
    float *ptr = new (MyPlacement) float[1000];
    delete [] ptr;
  }
}
catch (std::bad_alloc &amp;)
{
}
</pre>
<p>Более подробную информацию на эту тему можно найти в следующих записях блога разработчиков PVS-Studio:</p>
<ol>
<li><a href="http://www.viva64.com/blog/ru/2009/03/16/76/">OpenMP и исключения (exceptions)</a></li>
<li><a href="http://www.viva64.com/blog/ru/2009/09/07/343/">Обработка исключений внутри параллельных секций</a></li>
</ol>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1302.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 25. Исключения. Функции</strong></em></p>
<p>Согласно спецификации OpenMP, все исключения должны быть обработаны внутри параллельной секции. Если в параллельной секции используется функция, помеченная как явно бросающая исключения, то исключение должно быть обработано внутри параллельной секции.</p>
<p>Пример ошибочного кода:</p>





<pre name="code" class="cpp">void MyThrowFoo() throw(...)
{
  throw 1;
}
try {
  #pragma omp parallel for num_threads(4)
  for(int i = 0; i &lt; 4; i++)
  {
    MyThrowFoo();
  }
}
catch (...)
{
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): диагностируется как ошибка V1303.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 26. Забытая инициализация. omp_init_lock</strong></em></p>
<p>Ошибочным следует считать использование переменных типа omp_lock_t / omp_nest_lock_t без их предварительной инициализации в функциях omp_init_lock / omp_init_nest_lock. Под использованием понимается вызов функции omp_set_lock и так далее.</p>
<p>Пример ошибочного кода:</p>





<pre name="code" class="cpp">omp_lock_t myLock;
#pragma omp parallel num_threads(2)
{
  omp_set_lock(&amp;myLock);
  omp_unset_lock(&amp;myLock);
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 27. Забытая инициализация. Локальные переменные</strong></em></p>
<p>Ошибочным следует считать использование переменных, объявленных в параллельном регионе локальными с использованием директив "private" и "lastprivate" без их предварительной инициализации. Пример:</p>





<pre name="code" class="cpp">int a = 0;
#pragma omp parallel private(a)
{
  a++;
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): диагностируется как error #12361.</p>

<p><em><strong>Пункт 28. Забытая инициализация после параллельного региона</strong></em></p>
<p>Ошибочным следует считать использование переменных после параллельного участка кода, к которым применялась директива "private", "threadprivate" или "firstprivate". Перед дальнейшим использованием они должны быть вновь инициализированы.</p>
<p>Пример некорректного кода:</p>





<pre name="code" class="cpp">#pragma omp parallel private(a)
{
  ...
}
b = a;
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): диагностируется как error #12352, error #12358.</p>

<p><em><strong>Пункт 29. Отсутствие конструктора копирования</strong></em></p>
<p>Опасным следует считать применение директив "firstprivate" и "lastprivate" к экземплярам классов, в которых отсутствует конструктор копирования.</p>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 30. Неэффективное использование критических секций</strong></em></p>
<p>Неэффективным следует считать использование <a href="http://www.viva64.com/terminology/Critical_section_rus.html">критических секций</a> или функций класса omp_set_lock, там где достаточно директивы "atomic". Директива atomic работает быстрее, чем критические секции, поскольку некоторые атомарные операции могут быть напрямую заменены командами процессора. Следовательно, эту директиву желательно применять везде, где требуется защита общей памяти при элементарных операциях. К таким операциям, согласно спецификации OpenMP, относятся операции следующего вида:</p>
<ul>
<li>x binop= expr</li>
<li>x++</li>
<li>++x</li>
<li>x--</li>
<li>--x</li>
</ul>
<p>Здесь х - скалярная переменная, expr - выражение со скалярными типами, в котором не присутствует переменная х, binop - не перегруженный оператор +, *, -, /, &amp;, ^, |, &lt;&lt;, или &gt;&gt;. Во всех остальных случаях применять директиву atomic нельзя (это проверяется компилятором).</p>
<p>Вообще, с точки зрения убывания быстродействия, средства защиты общих данных от одновременной записи располагаются так: atomic, critical, omp_set_lock.</p>
<p>Пример неэффективного кода:</p>





<pre name="code" class="cpp">#pragma omp critical
{
  e++;
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 31. Бессмысленная защита локальных переменных</strong></em></p>
<p>Любая защита памяти от одновременной записи замедляет выполнение программы, будь то атомарная операция, критическая секция или блокировка. Следовательно, в тех случаях, когда она не нужна, эту защиту лучше не использовать.</p>
<p>Переменную не нужно защищать от одновременной записи в следующих случаях:</p>
<p>если переменная является локальной для потока (а также если она участвует в выражении threadprivate, firstprivate, private или lastprivate);</p>
<p>если обращение к переменной производится в коде, который гарантированно выполняется только одним потоком (в параллельной секции директивы master или директивы single).</p>
<p>Пример, где защита записи в переменную "p" не имеет смысла:</p>





<pre name="code" class="cpp">#pragma omp parallel for private(p)
for (i=1; i&lt;10; i++)
{ 
  #pragma omp critical
  p = 0;
  ...
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): не диагностируется.</p>

<p><em><strong>Пункт 32. Лишний reduction</strong></em></p>
<p>Иногда в коде может использоваться директива reduction, хотя указанные в ней переменные не меняются в параллельном регионе. Это может свидетельствовать как об ошибке, так и просто о том, что какая-то директива или переменная была забыта и не удалена в процессе рефакторинга кода.</p>
<p>Пример, где не используется переменная abcde:</p>





<pre name="code" class="cpp">#pragma omp parallel for reduction (+:sum, abcde)
for (i=1; i&lt;999; i++)
{ 
  sum = sum + a[i];
}
</pre>
<p>Диагностика:</p>
<p>PVS-Studio (VivaMP): не диагностируется.</p>
<p>Intel C++ (Parallel Lint): диагностируется как error #12283.</p>

<h2>Заключение<a name="IDATVJ5B"></a></h2>
<p>Хотя сравнение двух анализаторов пока нельзя назвать законченным, эта статья позволяет читателю узнать о многих паттернах  параллельных OpenMP-ошибок и о методах их выявления на самых ранних этапах разработки. Возможность выявить ошибку на этапе кодирования является сильным преимуществом методики статического анализа, так как эти ошибки могут с большим трудом выявляться на этапе тестирования или вовсе не выявляться весьма продолжительное время.</p>
<p>Рассмотренные инструменты PVS-Studio (VivaMP) и Intel C/C++ ("Parallel Lint") будут крайне полезны разработчикам параллельных программ. Какой инструмент выбрать - зависит от разработчика. К сожалению, эта статья не дает ответ на этот вопрос. Пока можно сформулировать преимущества каждого из анализаторов следующим образом:</p>
<p>Преимущества анализатора Intel C/C++ ("Parallel Lint"):</p>
<ul>
<li>анализатор входит в состав компилятора, что позволяет разработчикам, использующим Intel C/C++, без каких либо дополнительных вложений или усилий производить верификацию параллельного кода;</li>
<li>анализатор собирает вместе данные по всем модулям программы и затем целостно их обрабатывает, что позволяет осуществлять некоторые сложные алгоритмы диагностики;</li>
<li>авторитет и опыт разработчиков Intel C++ в области параллельных решений не вызывает сомнений.</li>
</ul>
<p>Преимущества анализатора PVS-Studio (VivaMP):</p>
<ul>
<li>возможность проверить проект, разрабатываемый в Visual Studio 2005/2008 без необходимости его предварительной адаптации для сборки компилятором Intel C++;</li>
<li>разработчики, в том числе и автор статьи, доступны для оперативного общения и могут быстро реализовать в анализаторе те возможности, которые необходимы пользователям;</li>
<li>в состав анализатора входит подробное описание всех паттернов ошибок с примерами, интегрирующимися в справочную систему MSDN (доступной в online режиме здесь: <a href="http://www.viva64.com/content/PVS-Studio-help-ru/contents-ru.html">http://www.viva64.com/content/PVS-Studio-help-ru/contents-ru.html</a>);</li>
<li>интерфейс PVS-Studio позволяет интерактивно фильтровать диагностические сообщения несколькими методами, сохранять и загружать список предупреждений;</li>
<li>VivaMP является узкоспециализированным, активно развивающимся анализатором, с постоянно растущей базой паттернов параллельных ошибок. </li>
</ul>

<h2>Библиографический список<a name="IDAZWJ5B"></a></h2>
<ol>
<li>Евгений Рыжков. Учебное пособие по PVS-Studio. <a href="http://www.viva64.com/art-4-1-1796251700.html">http://www.viva64.com/art-4-1-1796251700.html</a></li>
<li>Алексей Колосов, Евгений Рыжков, Андрей Карпов. 32 подводных камня OpenMP при программировании на Си++. <a href="http://www.viva64.com/art-3-1-464379766.html">http://www.viva64.com/art-3-1-464379766.html</a></li>
<li>Андрей Карпов, Евгений Рыжков. OpenMP и статический анализ кода. <a href="http://www.viva64.com/art-3-1-1177384583.html">http://www.viva64.com/art-3-1-1177384583.html</a></li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/unsuccessful-attempt-compare-VivaMP-and-Parallel-Lint</link>
      <pubDate>Wed, 14 Oct 2009 04:06:54 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/unsuccessful-attempt-compare-VivaMP-and-Parallel-Lint#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/unsuccessful-attempt-compare-VivaMP-and-Parallel-Lint</guid>
      <category>Параллельное программирование</category>
      <category>Tools</category>
      <category>Компиляторы Intel® </category>
    </item>
    <item>
      <title>Что такое size_t и ptrdiff_t</title>
      <description><![CDATA[ <p><a href="#IDADJY1E"><strong>Введение</strong></a><br /> <a href="#IDAHKY1E"><strong>Тип size_t</strong></a><br /> <a href="#IDARKY1E"><strong>Тип ptrdiff_t</strong></a><br /> <a href="#IDAZKY1E"><strong>Переносимость size_t и ptrdiff_t</strong></a><br /> <a href="#IDAHLY1E"><strong>Безопасность типов ptrdiff_t и size_t в адресной арифметике</strong></a><br /> <a href="#IDAVNY1E"><strong>Быстродействие кода, использующего типы ptrdiff_t и size_t</strong></a><br /> <a href="#IDAZOY1E"><strong>Рефакторинг кода с целью перехода на типы ptrdiff_t и size_t</strong></a></p>
<h2>Аннотация</h2>
<p>Статья поможет читателю разобраться, что представляют собой типы size_t и ptrdiff_t, для чего они нужны и когда целесообразно их использование. Статья будет интересна разработчикам, начинающим создание 64-битных приложений, где использование  типов size_t и ptrdiff_t обеспечивает быстродействие, возможность работы с большими объемами данных и переносимость между разными платформами.</p>
<h2>Введение<a name="IDADJY1E"></a></h2>
<p>Типы size_t и ptrdiff_t были созданы для того, чтобы осуществлять корректную <a href="http://www.viva64.com/terminology/Address_arithmetic_rus.html">адресную арифметику</a>. Долгое время было принято считать, что размер int совпадает с размером машинного слова (разрядностью микропроцессора) и его можно использовать в качестве индексов, для хранения размеров объектов или указателей. Соответственно адресная арифметика также строилась с использованием типов int и unsigned. Тип int используется в большинстве обучающих материалов по программированию на Си и Си++ в телах циклов и в качестве индексов. Практически каноническим выглядит пример следующего вида:</p>
<pre name="code" class="cpp">for (int i = 0; i &lt; n; i++)
  a[i] = 0;
</pre>
<p>С развитием микропроцессоров и ростом их разрядности стало нерационально дальнейшее увеличение размерностей типа int. Причин для этого много: экономия используемой памяти, максимальная совместимость и так далее. В результате появилось несколько моделей данных, описывающих соотношение размеров базовых типов языка Си/Си++. В таблице N1 приведены основные <a href="http://www.viva64.com/terminology/Data_model_rus.html">модели данных</a> и перечислены наиболее популярные системы их использующие.</p>
<p><img src="http://software.intel.com/file/22898" title="Модели данных (data models)" alt="Модели данных" /></p>
<p><i>Таблица 1. Модели данных (data models)</i></p>
<p>Как видно из таблицы, не так просто выбрать тип переменной для хранения указателя или размера объекта. Чтобы наиболее красиво решить эту проблему и появились типы size_t и ptrdiff_t. Они гарантированно могут использоваться для адресной арифметики. Теперь каноническим должен стать следующий код:</p>
<pre name="code" class="cpp">for (ptrdiff_t i = 0; i &lt; n; i++)
  a[i] = 0;
</pre>
<p>Именно он может обеспечить надежность, переносимость, быстродействие. Почему - станет ясно из дальнейшего текста статьи.</p>
<h2>Тип size_t<a name="IDAHKY1E"></a></h2>
<p>Тип size_t - базовый беззнаковый целочисленный тип языка Си/Си++. Является результатом выполнения оператора sizeof. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива. Например, на 32-битной системе size_t будет занимать 32-бита, на 64-битной - 64-бита. Другими словами в переменную типа size_t может быть безопасно помещен указатель. Исключение составляют указатели на функции классов, но это особый случай. Тип size_t обычно применяется для счетчиков циклов, индексации массивов, хранения размеров, адресной арифметики.</p>
<p>Максимально допустимым значением типа size_t является константа SIZE_MAX.</p>
<h2>Тип ptrdiff_t<a name="IDARKY1E"></a></h2>
<p>Тип ptrdiff_t - базовый знаковый целочисленный тип языка Си/Си++. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива. На 32-битной системе ptrdiff_t будет занимать 32-бита, на 64-битной - 64-бита. Как и в size_t в переменную типа ptrdiff_t может быть безопасно помещен указатель, за исключением указателя на функцию класса. Также ptrdiff_t является результатом выражения, где один указатель вычитается из другого (ptr1-ptr2). Тип ptrdiff_t обычно применяется для счетчиков циклов, индексации массивов, хранения размеров, адресной арифметики.</p>
<h2>Переносимость size_t и ptrdiff_t<a name="IDAZKY1E"></a></h2>
<p>Код созданный с использованием типов size_t и ptrdiff_t является хорошо переносимым. Размер size_t и ptrdiff_t всегда совпадают с размером указателя. По этой причине именно эти типы следует использовать в качестве индексов больших массивов, для хранения указателей и арифметики с указателями.</p>
<p>Разработчики Linux приложений часто используют для этих целей тип long. В рамках 32-битных и 64-битных моделей данных, принятых в Linux  это действительно работает. Размер типа long совпадает с размером указателя. Но такой код несовместим с моделью данных Windows и соответственно его нельзя считать хорошо переносимым. Более правильным решением будет использование типов size_t и ptrdiff_t.</p>
<p>Разработчики Windows в качестве альтернативы size_t и ptrdiff_t могут использовать типы DWORD_PTR, SIZE_T, SSIZE_T и так далее. Но желательно также ограничиваться типами size_t и ptrdiff_t.</p>
<h2>Безопасность типов ptrdiff_t и size_t в адресной арифметике<a name="IDAHLY1E"></a></h2>
<p>Проблемы адресной арифметики стали активно проявлять себя с началом освоения 64-битных систем. Наибольшее число проблем при переносе 32-битных приложений на 64-битные системы связанно с использованием неподходящих для работы с указателями и массивами типов, таких как int и long. Этим проблемы переноса приложений на 64-битные системы не ограничивается, но большинство ошибок связанны именно с адресной арифметикой и работе с индексами.</p>
<p>Возьмем самый простой пример:</p>
<pre name="code" class="cpp">size_t n = ...;
for (unsigned i = 0; i &lt; n; i++)
  a[i] = 0;
</pre>
<p>Если мы работаем с массивом, состоящим более чем из UINT_MAX элементов, то данный код является некорректным. При этом выявить ошибку и предсказать поведение данного кода не так просто. Отладочная (debug) версия зависнет, но редко кто в отладочной версии будет обрабатывать гигабайты данных. А вот рабочая (release) версия в зависимости от настроек оптимизации и особенностей кода, может как зависнуть, так и неожиданно корректно заполнить все ячейки массива, создавая иллюзию корректной работы. В результате в программе появляются  плавающие ошибки, возникающие или пропадающие после малейшего изменения кода. Подробнее о таких фантомных ошибках и их опасностях можно познакомится в статье "<a href="http://www.viva64.com/art-1-1-1064884779.html">64-битный конь, который умеет считать</a>" [1].</p>
<p>Пример еще одной дремлющей ошибки, которая проявит себя при определенном сочетании входных данных (значении переменных A и B):</p>
<pre name="code" class="cpp">int A = -2;
unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };
int *ptr = array + 3;
ptr = ptr + (A + B); //Error
printf("%i\n", *ptr);
</pre>
<p>Данный код будет успешно выполняться в 32-битном варианте и печатать на экране число "3". После компиляции 64-битном режиме при выполнении кода возникнет сбой.  Рассмотрим последовательность выполнения кода и причину ошибки:</p>
<ul>
<li>Переменная A типа int приводится к типу unsigned;</li>
<li>Происходит сложение A и B. В результате мы получаем значение 0xFFFFFFFF типа unsigned;</li>
<li>Вычисляется выражение "ptr + 0xFFFFFFFFu". Результат зависит от размерности указателя на данной платформе. В 32-битной программе, выражение будет эквивалентно "ptr - 1" и мы успешно распечатаем число 3. В 64-битной программе к указателю прибавится значение 0xFFFFFFFFu, в результате чего указатель окажется далеко за пределами массива.</li>
</ul>
<p>Приведенные ошибки можно легко избежать, используя тип size_t или ptrdiff_t. В первом случае, если тип переменной "i" будет size_t, то не возникнет зацикливания. Во втором, если мы используем типы size_t или ptrdiff_t для переменных "A" и "B", то корректно распечатаем число "3".</p>
<p>Сформулируем совет: везде, где присутствует работа с указателями или массивами следуeт использовать типы size_t и ptrdiff_t.</p>
<p>Более подробно с тем, каких ошибок можно избежать, используя типы size_t и ptrdiff_t можно познакомиться в следующих статьях:</p>
<ul>
<li><a href="http://www.viva64.com/art-1-1-1958348565.html">20 ловушек переноса Си++ - кода на 64-битную платформу</a> [2];</li>
<li><a href="http://www.viva64.com/art-1-1-38488545.html">Безопасность 64-битного кода</a> [3];</li>
<li><a href="http://www.viva64.com/art-1-1-329725213.html">Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows</a> [4].</li>
</ul>
<h2>Быстродействие кода, использующего типы ptrdiff_t и size_t<a name="IDAVNY1E"></a></h2>
<p>Использование типов ptrdiff_t и size_t в адресной арифметике помимо повышения надежности кода может дать дополнительный выигрыш в производительности. Например, использование в качестве индекса типа int, размерность которого отличается от размерности указателя приводит к тому, что в двоичном коде будут присутствовать дополнительные команды преобразования данных. Речь идет о 64-битном коде, в котором размер указателей стал равен 64-битам, а размер типа int остался 32-битным.</p>
<p>Показать короткий пример преимущества size_t над unsigned не простая задача. Чтобы быть объективным необходимо использовать оптимизирующие возможности компилятора. А два варианта оптимизированного кода часто становятся слишком непохожим, чтобы легко было продемонстрировать отличие. Попытка создать нечто близкое к простому примеру увенчалась успехом только с шестой попытки. И все равно пример не идеален, так как показывает не лишние преобразование типов данных, о которых сказано выше, а то, что компилятор смог построить более эффективный код при использовании типа size_t. Рассмотрим код программы, переставляющий элементы массива в обратном порядке:</p>
<pre name="code" class="cpp">unsigned arraySize;
...
for (unsigned i = 0; i &lt; arraySize / 2; i++)
{
  float value = array[i];
  array[i] = array[arraySize - i - 1];
  array[arraySize - i - 1] = value;
}
</pre>
<p>В примере переменные "arraySize" и "i" имеют тип unsigned. Этот тип легко можно заменить на тип size_t и сравнить небольшой участок ассемблерного кода, показанныый на риснуке 1.</p>
<p><img src="http://software.intel.com/file/22899" title="Сравнение 64-битного ассемблерного кода" alt="Сравнение 64-битного ассемблерного кода" /></p>
<p><i>Рисунок 1. Сравнение 64-битного ассемблерного кода при использовании типов unsigned и size_t</i></p>
<p>Компилятор смог построить более лаконичный код, когда использовал 64-битные регистры. Автор не берется утверждать, что код, созданный при использовании типа unsigned (текст слева), будет работать медленнее, чем код с использованием size_t (текст слева). Сравнить скорость выполнения кода на современных процессорах крайне сложная задача. Но из примера видно, что когда компилятор работает с массивами, используя 64-битные типы, он может строить более короткий и быстрый код.</p>
<p>По личному опыту автора, грамотная замена типов int и unsigned на ptrdiff_t и size_t может дать на 64-битной системе дополнительный прирост производительности до 10%. С одним из примеров увеличения скорости от использования типов ptrduff_t и size_t можно познакомиться в четвертой главе статьи "<a href="http://www.viva64.com/art-1-1-640027853.html">Разработка ресурсоемких приложений в среде Visual C++</a>" [5].</p>
<h2>Рефакторинг кода с целью перехода на типы ptrdiff_t и size_t<a name="IDAZOY1E"></a></h2>
<p>Как читатель уже убедился, использование типов ptrdiff_t и size_t имеет ряд преимуществ для 64-битных программ. Но и заменить, скажем все unsigned на size_t - не является выходом.  Во-первых, это не гарантирует корректность программы на 64-битной системы. Во-вторых, скорее всего из-за такой замены возникнут новые ошибки, нарушится совместимость форматов данных и так далее. Не стоит забывать, что после такой замены существенно возрастет и объем потребляемой программой памяти. Причем увеличение объема требуемой памяти может замедлить работу приложения, так как в кэш будет помещаться меньше объектов, с которыми идет работа.</p>
<p>Следовательно, внедрение в старый код типов ptrdiff_t и size_t является задачей постепенного рефакторинга, требующего большого количества времени. Фактически необходимо просмотреть весь код и внести необходимые исправления. Такой подход практически является слишком дорогостоящим и неэффективным. Можно предложить 2 варианта:</p>
<ol>
<li>Использовать специализированные инструменты, такие как Viva64, входящий в состав <a href="http://www.viva64.com/ru/pvs-studio/">PVS-Studio</a>. Viva64 это статический анализатор кода, обнаруживающий места, где рационально изменить типы данных, чтобы программа была корректна и эффективно работала на 64-битных системах. Подробнее смотрите "<a href="http://www.viva64.com/art-4-1-1796251700.html">Учебное пособие по PVS-Studio</a>" [6].</li>
<li>Если 32-битную программу не планируется адаптировать для 64-битных систем, то и нет смысла заниматься рефакторингом типов данных. 32-битная программа не получит никаких преимуществ от использования типов ptrdiff_t и size_t.</li>
</ol>
<h2>Библиографический список</h2>
<ol>
<li>Андрей Карпов. 64-битный конь, который умеет считать. <a href="http://www.viva64.com/art-1-1-1064884779.html">http://www.viva64.com/art-1-1-1064884779.html</a></li>
<li>Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ - кода на 64-битную платформу. <a href="http://www.viva64.com/art-1-1-1958348565.html">http://www.viva64.com/art-1-1-1958348565.html</a></li>
<li>Андрей Карпов. Безопасность 64-битного кода. <a href="http://www.viva64.com/art-1-1-38488545.html">http://www.viva64.com/art-1-1-38488545.html</a></li>
<li>Андрей Карпов, Евгений Рыжков. Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Windows. <a href="http://www.viva64.com/art-1-1-329725213.html">http://www.viva64.com/art-1-1-329725213.html</a></li>
<li>Андрей Карпов, Евгений Рыжков. Разработка ресурсоемких приложений в среде Visual C++. <a href="http://www.viva64.com/art-1-1-640027853.html">http://www.viva64.com/art-1-1-640027853.html</a></li>
<li>Евгений Рыжков. Учебное пособие по PVS-Studio. <a href="http://www.viva64.com/art-4-1-1796251700.html">http://www.viva64.com/art-4-1-1796251700.html</a></li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/size_t-and-ptrdiff_t</link>
      <pubDate>Tue, 13 Oct 2009 05:44:18 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/size_t-and-ptrdiff_t#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/size_t-and-ptrdiff_t</guid>
      <category>Сообщество разработчиков программного обеспечения</category>
    </item>
    <item>
      <title>64-битная версия Loki</title>
      <description><![CDATA[ <p><strong>Авторы:</strong> Андрей Александреску, Андрей Карпов, Евгений Рыжков, Питер Кюмель, Рич Спозато.<br />
<a href="http://sourceforge.net/projects/loki-lib">Loki Team</a> и <a href="http://www.viva64.com">ООО "Системы программной верификации"</a>.</p>
<p><a href="#IDAUJAI"><strong>Введение</strong></a><br /> <a href="#IDAGKAI"><strong>Сборка 64-битной Loki в Microsoft Visual Studio 2005</strong></a><br /> <a href="#IDA0OAI"><strong>Проверка 64-битной Loki с помощью Viva64</strong></a><br />

<a href="#IDAFPAI">&nbsp;&nbsp;&nbsp;&nbsp;1 Некорректно используемая константа LONG_MIN</a><br /><a href="#IDAOZAI">&nbsp;&nbsp;&nbsp;&nbsp;2 Некоторые виды магических чисел - ошибка или нет?</a><br /><a href="#IDAM0AI">&nbsp;&nbsp;&nbsp;&nbsp;3 Прибавление int  к указателю - потенциальная проблема</a><br /><a href="#IDAU1AI">&nbsp;&nbsp;&nbsp;&nbsp;4 Использование int для индексации массивов некорректно</a><br /><a href="#IDA21AI">&nbsp;&nbsp;&nbsp;&nbsp;5 Правильные аргументы функций</a><br /><a href="#IDAT2AI"><strong>Библиотека Loki совместима с 64-битными системами - значит программа, использующая ее также совместима?</strong></a><br /> <a href="#IDAV4AI"><strong>Выводы</strong></a><br /></p>

<h2>Аннотация</h2>
<p>Статья представляет собой отчет о проверки библиотеки Loki на совместимость с 64-битными системами с помощью анализатора кода Viva64 компании ООО "СиПроВер". Содержатся рекомендации пользователям библиотеки. Статья будет полезна также пользователям других библиотек, построенных на шаблонах, так как раскрывает особенности анализа подобных библиотек.</p>

<a name="IDAUJAI" id="IDAUJAI"></a><h2>Введение</h2>
<p>Библиотека Loki разработана Андреем Александреску как часть книги "Современное проектирование на С++: Обобщенное программирование и прикладные шаблоны проектирования". Аннотация к книге гласит: "В книге изложена новая технология программирования, представляющая собой сплав обобщенного программирования, метапрограммирования шаблонов и объектно-ориентированного программирования на C++. Настраиваемые компоненты, созданные автором, высоко подняли уровень абстракции, наделив язык C++ чертами языка спецификации проектирования, сохранив всю его мощь и выразительность".</p>
<p>Компания ООО "СиПроВер", создающая анализатор кода <a href="http://www.viva64.com/ru/viva64-tool/">Viva64</a> для разработки 64-битных приложений, активно общается с авторами различных программных проектов. Однажды к нам обратился Рич Спозато (Rich Sposato), один из администраторов проекта Loki, и попросил проверить код библиотеки на предмет совместимости с 64-битными системами с помощью нашего инструмента Viva64. Мы сразу же согласились. Ведь это не только возможность принести пользу сообществу разработчиков, но и настоящий "тест выносливости" для нашего анализатора кода Viva64. Всем известно, что библиотека Loki написана с применением самых современных и мощных возможностей языка Си++. И если анализатор Viva64 справится с Loki, то и другие более простые проекты не вызовут никаких проблем.</p>
<p>Надо заметить, что данная статья основана на версии Loki от мая 2009 года (новее, чем официальная на тот момент версия Loki 0.1.7), поэтому в более новых версиях Loki указанные проблемы будут устранены.</p>
<p>Итак, мы скачали последнюю версию Loki из SVN-репозитория и приступили к работе.</p>

<a name="IDAGKAI" id="IDAGKAI"></a><h2>Сборка 64-битной Loki в Microsoft Visual Studio 2005</h2>
<p>Анализатор кода Viva64 интегрируется в среду разработки Microsoft Visual Studio, поэтому разумно было собрать версию Loki именно для этой среды. В составе пакета Loki есть готовые файлы решений для Visual Studio 2005 и Visual Studio 2008. Однако на момент мая 2009 года эти решения содержат лишь 32-битные конфигурации. Поэтому необходимо в Visual Studio создать конфигурации для платформы x64. Добавив нужные конфигурации можно запустить компиляцию 64-битной версии.</p>
<p>Библиотека Loki предназначена для работы на большом количестве различных платформ и собирается с помощью многих наиболее известных компиляторов. Именно поэтому 64-битная версия Loki скомпилировалась практически сразу же. Из 20 проектов, входящих в решение, не собрался только один:</p>





<pre name="code" class="cpp:nogutter:nocontrols">========== Build: 19 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
</pre>
<p>Это был проект SafeFormat:</p>





<pre name="code" class="cpp:nogutter:nocontrols">16&gt;------ Build started: Project: SafeFormat, Configuration: Debug x64 ------
16&gt;Compiling...
16&gt;main.cpp
16&gt;.\main.cpp(255) : error C3066: there are multiple ways 
that an object of this type can be called with these arguments
16&gt;        ..\..\include\loki/SafeFormat.h(109): could be 'Loki::PrintfState&lt;Device,Char&gt; 
&amp;Loki::PrintfState&lt;Device,Char&gt;::operator ()(bool)'
...
16&gt;        while trying to match the argument list '(UInt)'
</pre>
<p>Текст ошибки несколько сокращен, поскольку полностью он займет целую страницу. Посмотрим на код, который приводит к ошибке:</p>





<pre name="code" class="cpp">void test_dword()
{
    typedef signed int Int;
    typedef unsigned int UInt;
    typedef signed long Long;
    typedef unsigned long ULong;
    Int i(0);
    UInt ui(0);
    Long l(0);
    ULong ul(0);
    Printf("%d")(i);
    Printf("%d")(ui); // Проблема в этой строке
    Printf("%d")(l);
    Printf("%d")(ul);
}
</pre>
<p>Printf - это функция, внутри которой используется макрос LOKI_PRINTF_STATE_FORWARD. С помощью этого макроса задается ряд вспомогательных функций. В нем-то и кроется проблема. Внутри файла SafeFormat.h вместо следующего фрагмента:</p>





<pre name="code" class="cpp">
#if (defined(_WIN32) || defined(_WIN64))
        LOKI_PRINTF_STATE_FORWARD(unsigned long)
#else
</pre>
<p>необходимо написать так:</p>





<pre name="code" class="cpp">#if (defined(_WIN32) || defined(_WIN64))
#if (defined(_WIN64))
        LOKI_PRINTF_STATE_FORWARD(unsigned int)
#endif
        LOKI_PRINTF_STATE_FORWARD(unsigned long)
#else
</pre>
<p>После этого исправления ошибка компиляции пропадет и все 20 проектов библиотеки скомпилируются.</p>
<p>Однако среди прочих диагностических сообщений (warnings) компилятора мы видим сообщение, касающееся 64-битного кода:</p>





<pre name="code" class="cpp">2&gt;.\CachedFactoryTest.cpp(204) : warning C4267: 'argument' : 
conversion from 'size_t' to 'const int', possible loss of data
2&gt;        .\CachedFactoryTest.cpp(238) : see reference to function template
instantiation 'milliSec typicalUse&lt;Cache&gt;
(Cache &amp;,unsigned int,unsigned int,unsigned int)' being compiled
2&gt;        with
2&gt;        [
2&gt;            Cache=CRandomEvict
2&gt;        ]
2&gt;        .\CachedFactoryTest.cpp(252) : see reference to function template
instantiation 'void displayTypicalUse&lt;CRandomEvict&gt;
(Cache &amp;,unsigned int,unsigned int,unsigned int)' being compiled
2&gt;        with
2&gt;        [
2&gt;            Cache=CRandomEvict
2&gt;        ]
</pre>
<p>Это сообщение говорит о потенциально опасном преобразовании типа <a href="http://www.viva64.com/terminology/size_t_rus.html">size_t</a> внутри функции typicalUse() в файле CachedFactoryTest.cpp:</p>





<pre name="code" class="cpp">// Registering objects
for(size_t i=0;i&lt;objectKind;i++)
  CC.Register(i, createProductNull);
</pre>
<p>Граница цикла (переменная objectKind) представлена типом unsigned. Поэтому использовать для счетчика цикла переменную типа size_t не имеет смысла. После исправления типа счетчика цикла проблема исчезнет:</p>





<pre name="code" class="cpp"> // Registering objects
for(unsigned i=0;i&lt;objectKind;i++)
  CC.Register(i, createProductNull);
</pre>
<p>После этих небольших исправлений мы имеем 64-битную библиотеку, которая успешно компилируется и не выдает никаких диагностических сообщений (warnings) насчет 64-битности. Но насколько в действительности код библиотеки корректен? Это мы определим с помощью нашего анализатора кода Viva64.</p>

<a name="IDA0OAI" id="IDA0OAI"></a><h2>Проверка 64-битной Loki с помощью Viva64</h2>
<p>Для того чтобы убедится в совместимости Loki с 64-битными системами, выполним анализ кода с помощью Viva64. Анализатор кода <a href="http://www.viva64.com/ru/viva64-tool/">Viva64</a> предназначен для разработки новых 64-битных приложений и для миграций существующих 32-битных на 64-битную платформу.</p>
<p>Запуск Viva64 для анализа Loki выявил список из 89 потенциально-опасных синтаксических конструкций. Это не значит, что в библиотеке Loki содержится 89 ошибок, связанных с 64-битным кодом. Но все эти 89 мест должен просмотреть разработчик для того, чтобы понять, действительно ли там возможна ошибка или нет. Разумеется, мы проанализировали эти ошибки.</p>
<a name="IDAFPAI" id="IDAFPAI"></a><h3 class="sectionHeading">1 Некорректно используемая константа LONG_MIN</h3>
<p>Начнем с ошибки, связанной с некорректно используемой константой LONG_MIN в следующей функции:</p>





<pre name="code" class="cpp">char* RenderWithoutSign(LOKI_SAFEFORMAT_SIGNED_LONG n, 
    char* bufLast, unsigned int base, bool uppercase)
</pre>
<p>Она находится в файле SafeFormat.h. Проблема в строке:</p>





<pre name="code" class="cpp">if (n != LONG_MIN) {
</pre>
<p>Тип LOKI_SAFEFORMAT_SIGNED_LONG объявляется как тип, способный вмещать 64-битные значения в 64-битной системе. В Unix-системах (с <a href="http://www.viva64.com/terminology/Data_model_rus.html">моделью данных</a> <a href="http://www.viva64.com/terminology/LP64_rus.html">LP64</a>) для этого используют тип long. Но в 64-битных Windows-системах (модель данных <a href="http://www.viva64.com/terminology/LLP64_rus.html">LLP64</a>) тип long остался 32-битным. Поэтому в Loki тип LOKI_SAFEFORMAT_SIGNED_LONG объявлен так:</p>





<pre name="code" class="cpp">#if defined(_WIN32) || defined(_WIN64)
  #define LOKI_SAFEFORMAT_SIGNED_LONG intptr_t
  #define LOKI_SAFEFORMAT_UNSIGNED_LONG uintptr_t
#else
  #define LOKI_SAFEFORMAT_SIGNED_LONG signed long
  #define LOKI_SAFEFORMAT_UNSIGNED_LONG unsigned long
#endif
</pre>
<p>Поскольку тип long в 64-битных Windows-системах остался 32-битным, то и константа LONG_MIN задает минимальное значение 32-битной переменной. А это значит, что ее использование некорректно при работе с 64-битными типами (в нашем случае с intptr_t). Решением будет использование собственной константы. Например, исправление может выглядеть так:</p>





<pre name="code" class="cpp">#if defined(_WIN32) || defined(_WIN64)
#  define LOKI_SAFEFORMAT_SIGNED_LONG intptr_t
#if defined(_WIN64)
#  define LOKI_SAFEFORMAT_SIGNED_LONG_MIN_VALUE LLONG_MIN
#  define LOKI_SAFEFORMAT_SIGNED_LONG_MAX_VALUE LLONG_MAX
#else
#  define LOKI_SAFEFORMAT_SIGNED_LONG_MIN_VALUE LONG_MIN
#  define LOKI_SAFEFORMAT_SIGNED_LONG_MAX_VALUE LONG_MAX
#endif
...
#else
#  define LOKI_SAFEFORMAT_SIGNED_LONG signed long
#  define LOKI_SAFEFORMAT_SIGNED_LONG_MIN_VALUE LONG_MIN
#  define LOKI_SAFEFORMAT_SIGNED_LONG_MAX_VALUE LONG_MAX
...
#endif
</pre>
<p>И, соответственно, строку</p>





<pre name="code" class="cpp">if (n != LONG_MIN) {
</pre>
<p>надо заменить на строку</p>





<pre name="code" class="cpp">if (n != LOKI_SAFEFORMAT_SIGNED_LONG_MIN_VALUE) {
</pre>
<p>К счастью и к похвале создателей библиотеки Loki это единственное место, которое заслуживавет исправления. Описанные далее замечания могут быть интересны, но не имеют важности.</p>

<a name="IDAOZAI" id="IDAOZAI"></a><h3 class="sectionHeading">2 Некоторые виды магических чисел - ошибка или нет?</h3>
<p>Одной из проблем, выявляемой анализатором Viva64 являются магические числа. С точки зрения миграции кода с 32-битной на 64-битную платформу есть ряд чисел, использование которых наиболее опасно. Возможно, где-то в коде программист рассчитывает на определенный размер типа данных, что может вызвать проблему. Те, кто смотрят на сообщения от анализаторов кода, часто жалуются на бессмысленность подобных сообщений. Действительно, какой смысл ругаться анализатору кода на число 4 в подобных строках:</p>





<pre name="code" class="cpp">::Loki::ScopeGuard guard4 = ::Loki::MakeGuard( &amp;HasFour, 1, 2, 3, 4 );
::Loki::ScopeGuard guard5 = ::Loki::MakeGuard( &amp;HasFive, 1, 2, 3, 4, 5 );
</pre>
<p>Но бывают такие конструкции, которые надо анализировать очень тщательно. Например, в файле SafeFormat\main.cpp видим код:</p>





<pre name="code" class="cpp">case 'X':
    // TestCase(formatSpec, RandomInt(-10000, 10000));
    // don't test negative values on 64bit systems, because 
    // snprintf does not support 64 Bit values
    TestCase(formatSpec, 
        RandomInt( -10000 * (sizeof(size_t)&gt;4 ? 0 : 1) , 10000));   
    break;
case 'e':
</pre>
<p>Конкретно в этом месте проблемы, конечно же, нет. Но диагностика магических чисел в анализаторе кода нужна именно для нахождения таких конструкций в коде.</p>

<a name="IDAM0AI" id="IDAM0AI"></a><h3 class="sectionHeading">3 Прибавление int  к указателю - потенциальная проблема</h3>
<p>Файл flex\simplestringstorage.h содержит функцию:</p>





<pre name="code" class="cpp">void resize(size_type newSize, E fill)
{
  const int delta = int(newSize - size());
  if (delta == 0) return;
  if (delta &gt; 0)
  {
    if (newSize &gt; capacity())
    {
      reserve(newSize);
    }
    E* e = &amp;*end();
    flex_string_details::pod_fill(e, e + delta, fill);
  }
  pData_-&gt;pEnd_ = pData_-&gt;buffer_ + newSize;
}
</pre>
<p>Анализатор Viva64 сообщает о потенциальной проблеме здесь:</p>





<pre name="code" class="cpp">flex_string_details::pod_fill(e, e + delta, fill);
</pre>
<p>Недостаток кода заключается в том, что к указателю e прибавляется переменная delta типа int. Это потенциальная проблема, так как функция pod_fill не сможет обрабатывать объем данных более двух гигабайт (INT_MAX символов). Конечно же, конкретно здесь ошибки, видимо, нет, поскольку вряд ли бывают строки более двух гигабайт. Но лучше все-таки заменить тип переменной delta с int на <a href="http://www.viva64.com/terminology/ptrdiff_t_rus.html">ptrdiff_t</a>:</p>





<pre name="code" class="cpp">const ptrdiff_t delta = ptrdiff_t(newSize - size());
</pre>
<a name="IDAU1AI" id="IDAU1AI"></a><h3 class="sectionHeading">4 Использование int для индексации массивов некорректно</h3>
<p>Для доступа к большим массивам данным (более INT_MAX элементов) надо использовать типы ptrdiff_t или size_t. В файле SmallObj\SmallObjBench.cpp присутсвует довольно большой макрос LOKI_SMALLOBJ_BENCH_ARRAY, в котором работа с массивом ведется через индексацию по int. Хотя это лучше исправить, в данном месте это не является ошибкой.</p>

<a name="IDA21AI" id="IDA21AI"></a><h3 class="sectionHeading">5 Правильные аргументы функций</h3>
<p>Внутри файла CachedFactory\CachedFactoryTest.cpp объявлена функция:</p>





<pre name="code" class="cpp">template&lt; class Cache &gt;
milliSec typicalUse(Cache &amp;CC, unsigned objectKind, 
                    unsigned maxObjectCount, unsigned maxIteration)
</pre>
<p>Для параметра objectKind лучше использовать тип size_t. Но поскольку этот код относится к тестам, то какой-либо серьезного недостатка в этом нет.</p>

<a name="IDAT2AI" id="IDAT2AI"></a><h2>Библиотека Loki совместима с 64-битными системами - значит программа, использующая ее также совместима?</h2>
<p>Все немногие обнаруженные проблемы библиотеки Loki, перечисленные ранее, легко исправимы. Значит ли это, что если в Loki нет никаких 64-битных проблем (а это именно так), то и приложение, использующее эту библиотеку, безопасно с точки зрения 64-битного кода? К сожалению нет!</p>
<p>Все дело в том, что библиотека Loki крайне активно использует шаблоны. А когда анализатор кода разбирает код шаблона, он не всегда может диагностировать проблему. Для полной уверенности анализатору необходимо выполнить инстанцирование шаблонных классов и функций.</p>
<p>Приведем просто пример, не относящийся к библиотеке Loki. Анализатор Viva64 среди прочих проблем в коде может обнаруживать неоптимальные структуры данных:</p>





<pre name="code" class="cpp">template &lt;class T&gt;
struct TClass
{
  int m_a;
  T m_b;
  int m_c;
};
</pre>
<p>Если здесь T имеет тип int, то структура оптимальна. Если же T имеет тип size_t, то структура займет 24 байта, вместо возможных 16 байтах. Тогда в случае большого количества подобных объектов лучше было бы переписать:</p>





<pre name="code" class="cpp">template &lt;class T&gt;
struct TClass
{
  T m_b;
  int m_a;
  int m_c;
};
</pre>
<p>Но проверить это анализатор может только выполнив инстанцирование шаблона. То есть имея лишь объявление класса в заголовке выявит проблему нельзя.</p>
<p>Другой пример, опять же не относящийся к Loki, связанный с приведением типов:</p>





<pre name="code" class="cpp">template&lt;typename T1, typename T2&gt;
class TemplateClass
{
public:
	void test1()
	{
		m_a.m_value = m_b.m_value; // Есть ли здесь ошибка?
	}
private:
	T1 m_a;
	T2 m_b;
};
</pre>
<p>В данном коде ошибка приведения типов может быть, а может и не быть, в зависимости от того, с какими параметрами будет выполнено инстанцирование шаблона TemplateClass. Только анализируя код функции, не выполняя инстанцирование, анализатор не может сообщить об ошибке.</p>
<p>Перечисленные два примера шаблонных классов не относятся к библиотеке Loki, однако важны для понимания принципов работы анализаторов кода. Особенность шаблонных библиотек типа Loki заключается в том, что даже если библиотека полностью совместима с 64-битными системами, это не значит что код, который ее использует, корректен. Это в корне меняет подход к верификации приложений. Если при работе с обычной (не шаблонной) библиотекой достаточно быть уверенным, что библиотека корректна с точки зрения 64-бит для того, чтобы быть уверенным в корректности всего приложения, то с шаблонными библиотеками такой уверенности уже быть не может.</p>
<p>Все это означает, что хотя библиотека Loki и не содержит проблем 64-битного кода, пользовательское приложение, которое ее использует должно быть дополнительно проверено анализатором кода на отсутствие подобных проблем. Ведь ошибки зависят от того, с какими параметрами выполняется инстанцирование шаблонов.</p>

<a name="IDAV4AI" id="IDAV4AI"></a><h2>Выводы</h2>
<p>По результатам работы сотрудников компании ООО "СиПроВер" над проверкой на совместимость с 64-бибтными системами библиотеки Loki сделаны следующие выводы:</p>
<p>Библиотека полностью совместима с 64-битными системами и не содержит потенциальных ошибок. Указанные в данной статье ошибки, скорее всего, будут очень быстро исправлены.</p>
<p>Анализатор кода Viva64, предназначенный для разработки новых 64-битных и миграции старых 32-битных приложений показал себя очень хорошо при проверке сложного шаблонного кода библиотеки. Это говорит о высоком качестве анализатора кода.</p>
<p>Хотя библиотека Loki и не содержит 64-битных проблем, они  возможны в пользовательских приложениях, использующих Loki. Поскольку конечный код зависит от того, с какими параметрами инстанцировались шаблоны, то обязательно надо проверять пользовательские приложения анализатором кода. Только тогда можно быть уверенным в полной совместимости приложения с 64-битными системами.</p>

<h2>Благодарность</h2>
<p>Благодарим всех, кто принимал участие в анализе библиотеки Loki и в оценке этой работы в команде Loki:</p>
<ul>
<li>Спасибо компании ООО "СиПроВер", которая проанализировала код библиотеки Loki и выполнила верификацию ее для работы в 64-битном режиме, в частности: Андрею Карпову и Евгению Рыжкову.</li>
<li>Спасибо команде Loki: Андрею Александреску, Питеру Кюмелю, Ричу Спозато за совместную работу над проверкой и редактированием статьи, а также ценные замечания.</li>
<li>Спасибо Ричу Спозато, за координирование всей работы.</li>
</ul>

<h2>Библиографический список</h2>
<ol>
<li>Библиотека Loki. <a href="http://sourceforge.net/projects/loki-lib/">http://sourceforge.net/projects/loki-lib/</a>.</li>
<li>Анализатор кода Viva64. <a href="http://www.viva64.com/viva64-tool">http://www.viva64.com/viva64-tool</a>. </li>
</ol> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/64-bit-Loki-library</link>
      <pubDate>Tue, 13 Oct 2009 05:31:38 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/64-bit-Loki-library#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/64-bit-Loki-library</guid>
      <category>Open Source</category>
      <category>Сообщество разработчиков программного обеспечения</category>
    </item>
    <item>
      <title>Разработка бизнес-приложений с использованием Silverlight</title>
      <description><![CDATA[ <h2 class="sectionHeading">Аннотация</h2>
<p>Данная статья посвящена рассмотрению вопросов создания бизнес-приложений на основе технологии Silverlight. Рассматриваются следующие вопросы:</p>
<ul>
<li>применение существующих шаблонов бизнес-приложений к технологии Silverlight; </li>
<li>тестирование Silverlight приложений и техника разработки приложений через тестирование (TDD);</li>
<li>применение гибких методов разработки (Agile development: eXtreme Programming (XP), Scrum).</li>
</ul>
<h2 class="sectionHeading">Введение</h2>
<p>Этой весной я случайно наткнулась в интернете на интересную ссылку о возможности прохождения стажировки в Intel. Скачав список задач, я решила, что обязательно найду что-нибудь интересное для себя, однако, выбирать долго не пришлось: вторая же задача оказалась идеальным проектом для меня. А звучала она так: «Задача 2: Разработка интранет-приложения с использованием Silverlight». Заинтересовала она меня по двум причинам: во-первых, по своей трудовой деятельности я сталкивалась с технологией Silverlight, но, к сожалению, не было возможности подробно изучить её и попробовать, поэтому очень хотелось углубить свои знания в этой технологии и попробовать применить её. Во-вторых, прочитав описание задачи, я поняла, что речь пойдет о бизнес-приложениях. После окончания ВУЗа я видела себя именно в сфере разработки бизнес-приложений и опыт их создания это то, чего мне так не хватало как молодому специалисту. И конечно возможность посмотреть компанию Intel изнутри, пообщаться с людьми, которые там работают, научиться чему-то новому и перенять существующий опыт - это то, чего я не могла упустить. Поэтому недолго думая, я заполнила заявку на участие в Летней школе и так я оказалась в Intel Summer School 2009. Над проектом я работала вместе с Евгением Кречуном. Нашим менеджером и непревзойденным идеалом был Евгений Сорокин. В первый же день перед нами были четко сформулированы основная цель и задачи проекта.</p>
<p>Итак, основной целью проекта было исследование технологии Silverlight на предмет применимости для разработки бизнес-приложений. Для решения поставленной цели необходимо было решить следующие задачи:</p>
<ul>
<li>Исследовать существующие шаблоны проектирования бизнес-приложений для создания хорошо масштабируемой и гибкой архитектуры бизнес-приложения. </li>
<li>Освоить технику разработки приложений через тестирование (Test Driving Development), которая позволяет писать более качественный код и снизить риск появления ошибок. </li>
<li>Научиться методам гибкой разработки (Agile development: eXtreme Programming (XP), Scrum). </li>
<li>Разработать прототип Silverlight-приложения; Мы были полны одержимого интереса и энергии и хотелось, как можно быстрее приступить к работе.</li>
</ul>
<h2 class="sectionHeading">Разработка прототипа бизнес-приложения</h2>
<p>Итак, основной нашей задачей было исследовать технологию Silverlight и определить насколько она подходит для создания бизнес приложений, а также разработать прототип такого приложения. Кодовое название нашего проекта - Dashboard. Основные его функции:</p>
<ul>
<li>Сравнение программных проектов по заранее заданным характеристикам с использованием деловой графики.</li>
<li>Просмотр информации о программных проектах (общее описание и список разработчиков).</li>
<li>Просмотр и редактирование информации о разработчиках. </li>
</ul>
<p>Функционал у конечного приложения конечно небольшой, однако, главной целью разработки было показать возможность применения существующих шаблонов и методов к технологии Silverlight. Почему именно Silverlight?</p>
<p>В успехе любого проекта немаловажную роль играет UI, он же - пользовательский интерфейс. И технология Microsoft Silverlight создана именно для созданий интернет и интранет приложений с богатым пользовательским интерфейсом - RIA (Rich Internet application). 10 июля 2009 в 18:00 по московскому времени Microsoft выпустила Silverlight 3 и Microsoft Expression Studio 3, именно эту версию Silverlight мы и решили использовать в нашей разработке. Помимо этого нам понадобились MS Visual Studio 2008 и Silverlight Toolkit.</p>
<p>Начали мы разработку нашего приложения с изучения существующих шаблонов проектирования бизнес-приложений. Для разработки нашего приложения мы использовали шаблон Model View Presenter - шаблон проектирования пользовательского интерфейса, который был разработан для облегчения модульного тестирования и отделения логики от отображения. Помимо шаблона проектирования, немаловажными оказался сам подход к проектированию, а именно ориентация на предметную область - Domain Driven Design, который заключается в следующем:</p>
<ul>
<li>основное внимание при разработке должно уделяться предметной области и ее логике;</li>
<li>разработки в рамках сложных предметных областей должны базироваться на модели предметной области.</li>
</ul>
<p>Сочетая MVP с основными принципами Domain Driven Design, мы в конечном итоге получили архитектуру приложения, приведенную на рисунке 1.</p>
<p><img src="http://software.intel.com/file/22765" alt="Архитектура приложения" /></p>
<p><i>Рисунок 1. Архитектура приложения</i></p>
<p>Как видно наша система состоит из двух основных частей: клиентской и серверной.</p>
<p>Клиентская часть представлена Silverlight приложением. Вообще SIlverlight - это кроссплатфоменный и кроссбраузерный плагин, а Silverlight приложение - это .xap файл (по сути дела тот же zip архив, содержащий все ресурсы и сборки приложения), выполняющийся в плагине Silverlight. На момент разработки нашего приложения существовало три типа проектов для Silverlight:</p>
<ul>
<li>Silverlight Application - обычное приложение Silverlight.</li>
<li>Silverlight Class Library - библиотека классов Silverlight, этот тип проекта использовался для слоев: Presentation, Infrastructure, Domain.</li>
<li>Silverlight Navigation Application - приложение SIlverlight, поддерживающее навигацию, этот тип проекта использовался для слоя View.</li>
</ul>
<div></div>
<p>Серверная часть представлена WCF сервисом, взаимодействующим с БД через соответствующий слой Data Access. Рассмотрим подробнее слои приложения:</p>
<p><b>Domain</b><b>.</b> Этот слой находится в центре нашего приложения и инкапсулирует в себе всю бизнес-логику и знания предметной области. Данный слой содержит все основные бизнес-сущности и бизнес-правила предметной области. В нашем случае классы Solution (программный проект), Measure (показатель), Developer (разработчик), SolutionProfile (профиль проекта) относятся именно к доменным объектам. Вполне естественно, что в приложении должен быть единый домен, поскольку все, что относится к бизнесу описано именно в этом слое. Однако в архитектуре нашего приложения присутствует два домена. Это связано с ограничением технологии Silverlight. Поскольку технология Silverlight работает на своей платформe Silverlight .Net Framework, которая является сильно урезанной версией полноценного .Net Framework, то совместное использование доменных объектов сборками Silverlight и .Net Framework без перекомпиляции невозможно.</p>
<p>Для разрешения данной проблемы и были созданы две сборки Domain: одна используется Silverlight сборками клиентской части, а вторая используется WCF сервисом, работающим под управление полноценного .Net Framework. Однако практически все не так сложно:</p>
<ul>
<li>создаем одну сборку Silverlight Class Library;</li>
<li>создаем вторую сборку обычную .Net Class Library;</li>
<li>указываем у них одно и то же пространство имен;</li>
<li>создаем доменные классы в сборке Silverlight;</li>
<li>затем добавляем ссылки на доменные объекты из .Net Class Library.</li>
</ul>
<p>В итоге, фактически мы имеем файлы с доменными объектами только в одном экземпляре, а компилируются они в разные сборки. Тем самым наши доменные объекты мы можем использовать как в клиентских сборках Silverlight, так и в WCF сервисе (сборке полноценного .Net Framework).</p>
<p><b>Presentation</b><b>. </b>Этот слой содержит в себе всю логику работы интерфейса. В этом слое содержатся классы презентеров (Presenters), которые управляют видами (Views). В этом же слое объявлены интерфейсы IViews, которые реализуются в слое View. Также здесь содержатся плоские объекты, так называемы Presenter Entities, которые обеспечивают более удобное представление доменных объектов для отображения. Вся логика работы интерфейса заложена в этом слое. Поскольку он отделен непосредственно от представления (View), и реализован в виде библиотеки классов, то данный слой идеально поддается модульному тестированию, что естественно является одной из главных особенностей и преимуществ шаблона MVP.</p>
<p><b>View</b><b>. </b>Этот слой представляет собой UI нашего приложения. В нем реализуются интерфейсы, объявленные в слое Presenter (IViews). Сам View содержит в себе минимум логики (Passive View), и имеет только простые свойства, для отображения данных.</p>
<p><b>Infrastructure</b><b>.</b> Данный слой содержит классы адаптеров, которые предоставляют и сохраняют доменные объекты по запросу презентеров. Разнообразие адаптеров скрывается от слоя Presenter одним общим фасадом (GOF Facade pattern), что делает работу со слоем Infrastructure очень простой и удобной. Сами интерфейсы фасада и адаптеров объявлены в слое Domain, а в слое Infrastructure содержится реализация этих интерфейсов.</p>
<p>Серверная часть реализована в виде WCF сервиса, который с помощью слоя DataAccess (в нашем случае в качестве ORM мы использовали LINQ to SQL) получает данные из BD, формирует доменные объекты и передает их в клиентскую инфраструктуру. Однако для того чтобы передать доменные объекты необходимо их сериализовать, то есть преобразовать из сложных объектов в строку, а на клиенте в слое Infrastructure соответственно десериализовать строку в доменные объекты. Для сериализации мы использовали класс DataContractSerializer, доступный как в полноценных сборках .Net, так и в сборках Silverlight, что очень важно при интеграции приложения, поскольку сериализация и десериализация доменных объектов происходит как в клиентских сборках, так и в серверных сборках.</p>
<p>Так как мы разрабатывали, используя TDD - разработку приложений через тестирование, то вопросам тестирования Silverlight приложений мы уделили значительную часть нашего рабочего времени и провели исследование в этой области. В итоге мы рассмотрели преимущества и недостатки двух основных платформ для тестирования: MS Unit Test Framework (встроенный в MS Visual Studio) и Silverlight Unit Test Framework. Попробовав обе эти платформы, мы сделали следующие выводы:</p>
<p><strong>MS Unit Test Framework</strong></p>
<ul>
<li>Подходит для тестирования библиотек классов Silverlight (class libraries).</li>
<li>Для тестирования необходимо использовать системные сборки Silverlight (в частности заменять сборки System.Core на соответствующую сборки Silverlight System.Core). </li>
</ul>
<p><strong>MS Silverlight Unit Test Framework</strong></p>
<ul>
<li>Подходит для тестирования пользовательских контроллов, анимации непосредственно из браузера. </li>
<li>Позволяет тестировать асинхронные вызовы. Silverlight работает асинхронно, это означает что все вызовы, например к сервисам, будут происходить асинхронно, а это означает что это тоже необходимо протестировать. Поэтому MS Silverlight Unit Test Framework предоставляет простой способ написания асинхронных тестов. Необходимо просто наследовать класс с асинхронными тестами от класса SilverlightTest, затем применить соответствующие атрибуты [TestMethod, Asynchronous], указать таймаут, если необходимо, с помощью атрибута [Timeout(5000)] и далее управлять выполнением теста с помощью соответствующих методов EnqueueCallback(), EnqueueConditional(), EnqueueTestComplete(), EnqueueDelay(), TestComplete(). </li>
<li>Позволяет протестировать приложение в различных браузерах.</li>
</ul>
<h2 class="sectionHeading">Agile development</h2>
<p>Помимо самой разработки и исследования технологии SIlverlight, хочу рассказать о том, как у нас был поставлен процесс разработки. Как я уже писала выше, одной из наших задач было научиться методам гибкой разработки приложений. Вот о них сейчас и пойдет речь.</p>
<p>Начнем с того что же такое «Agile development»? Что же означает это модное выражение?</p>
<p>Многие программисты, работающие в реальных условиях бизнеса и разрабатывающие бизнес-приложения, знают, что программирование задача не из легких. Очень часто заказчик не может четко и понятно сформулировать свои требования. А многие, казалось бы, уже сформулированные требования противоречат друг другу. Да и правила бизнеса могут меняться в процессе разработки. Зачастую не разбираясь во всех деталях предметной области, программист видит стоящую перед ним проблему по-своему, и соответственно решает её, как он считает, правильным путем. Однако получая релиз программы, заказчик понимает, что программа делает не то, что хотелось бы. И непонимание между клиентом и заказчиком в итоге выливается в ненужный программный продукт. Тем самым, мы можем сделать вывод о том, что программа - это не монолитная и неизменная структура, мы не можем заранее предсказать все требования к конечному продукту, а значит, программисты должны своевременно реагировать на изменение требований. А для этого разрабатываемый программный продукт должен быть достаточно гибок для изменения направления развития и податлив для модификации в целом. Именно на это и нацелены практики гибкой разработки приложений (Agile development).</p>
<p>Существует множество методологий гибкой разработки, подробнее расскажу о тех, с которыми познакомились мы в летней школе Intel, а именно это практики экстремального программирования (XP) и методология Scrum. Практики гибкой разработки:</p>
<ul>
<li>Сидеть вместе - Sit together. Это простое правило способствует общению в команде, что является немаловажным фактором успеха команды в целом.</li>
<li>Информативное рабочее пространство - Informative workspace. Различного рода информационные доски помогают быстро и четко видеть весь проект в целом, а также, на каком этапе идет разработка. </li>
<li>Парное программирование - Pair programming. Парное программирование предполагает, что весь код создается парами программистов, работающих за одним компьютером. Один из них работает непосредственно с текстом программы (driver), другой (observer) наблюдает, но это не значит, что отдыхает. Он также активно участвует в разработке - следит за ошибками, одновременно делает code review и участвует в решении поставленных задач. В течение работы пары меняются, что усиливает взаимодействие внутри команды, и обеспечивает коллективное владение кодом - все это одни из факторов успеха команды, а соответственно и всего проекта. </li>
<li>Полная команда - Whole team. В терминах XP «заказчик» это не тот, кто платит за проект, а тот, кто действительно будет работать с создаваемым программным продуктом. Поэтому заказчик - это один из участников команды, он должен быть всегда доступен для ответа на возникающие у разработчиков вопросы. </li>
<li>Энергичная работа - Energized work. Вся команда работает энергично и не больше 40 часов в неделю. </li>
<li>Разработка через тестирование - Test Driven Development. Основное правило - пишем тесты до кода, что в результате приводит к созданию компактных и легковесных классов с требуемой функциональностью. Помимо этого, TDD позволяет быстро обнаруживать слабые места в дизайне и безопасно делать рефакторинг кода (code refactoring). </li>
</ul>
<p>Это основные практики, которые мы старались применять во время стажировки. Хотя команда у нас была небольшая, но, тем не менее, применяя практики XP и Scrum, мы сразу заметили, как повысилась эффективность нашей работы, и наши труды стали давать ощутимый результат. Весь процесс разработки разбивался на небольшие недельные итерации (sprints). Нашим «заказчиком» была группа разработчиков SMG, которой мы каждую неделю рассказывали о наших успехах и показывали достигнутые результаты. Они в свою очередь высказывали нам свои замечания и пожелания. После этого мы планировали работу на следующую итерацию: определяли список user stories, распределяли их по приоритетам, делили их на задачи (tasks) и в конечном итоге получали sprint backlog и так нами любимый burn down chart, который мы непременно хотели «сжечь» как можно быстрее. Казалось бы, ничего сложного, но насколько эти вещи мотивировали нас к работе и повышали нашу производительность. Вот в таких необыкновенно новых, но в то же время интересных условиях мы и работали.</p>
<h2 class="sectionHeading">Заключение</h2>
<p>Тем самым, за время прохождения стажировки были решены все поставленные задачи, а именно:</p>
<ul>
<li>Изучен шаблон проектирования бизнес приложений - Model View Presenter, а также подход к проектированию Domain Driven Design и показана возможность их применения к технологии Silverlight.</li>
<li>Освоена практика разработки приложений через тестирование (TDD) и продемонстрирована возможность её применения при разработке Silverlight приложений. При этом были изучены платформы для тестирования MS Unit Test Framework и MS Silverlight Unit Test Framework и даны рекомендации по их применению.</li>
<li>Спроектирован и разработан прототип приложения для сравнения показателей качества исходного кода программных решений SMG, а также для просмотра информации по проектам и их разработчикам.</li>
<li>И, конечно же, был получен неоценимый опыт в применение гибких методов разработки, в частности XP и Scrum.</li>
</ul>
<p>В заключение хочу выразить благодарность организаторам Летней Школы за подаренную возможность обучения и приобретения новых знаний с помощью компании Intel. Отдельно хочется выразить огромную благодарность Евгению Сорокину, Антону Бевзюку и всей команде разработчиков SMG за те знания и опыт, которыми они с нами так просто и открыто делились. Спасибо. Все знания, полученные в Летней Школе, уже находят применение в моей трудовой деятельности.</p>
<h2 class="sectionHeading">Об авторе</h2>
<p>Федореева Мария Александровна. В 2009 году закончила Ивановский Государственный Энергетический Университет по специальности Компьютерное обеспечение вычислительной техники и автоматизированных систем. Летом 2009 года принимала участие в Летней молодежной школе-стажировке Intel в городе Нижний Новгород. Сфера профессиональных интересов: интеллектуальный анализ данных, OLAP технологии, разработка бизнес-приложений на основе SOA технологий, web-технологии, мобильные технологии.</p> ]]></description>
      <link>http://software.intel.com/ru-ru/articles/silverlight-business-application-development</link>
      <pubDate>Tue, 13 Oct 2009 04:24:03 -0700</pubDate>
      <comments>http://software.intel.com/ru-ru/articles/silverlight-business-application-development#comments</comments>
      <guid isPermaLink="true">http://software.intel.com/ru-ru/articles/silverlight-business-application-development</guid>
      <category>Сообщество разработчиков программного обеспечения</category>
    </item>
  </channel></rss>