Your SlideShare is downloading. ×
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Разработка ресурсоемких приложений в среде Visual C++
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Разработка ресурсоемких приложений в среде Visual C++

610

Published on

Статья познакомит разработчиков прикладного программного обеспечения с задачами, которые ставит перед ними массовое внедрение многоядерных 64-битных вычислительных систем, знаменующих революционное …

Статья познакомит разработчиков прикладного программного обеспечения с задачами, которые ставит перед ними массовое внедрение многоядерных 64-битных вычислительных систем, знаменующих революционное увеличение вычислительной мощности, доступное рядовому пользователю. Будут рассмотрены вопросы эффективного использования аппаратных ресурсов для решения повседневных прикладных задач в рамках операционной системы Windows x64.

Published in: Technology, News & Politics
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
610
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  1. Разработка ресурсоемких приложенийв среде Visual C++Авторы: Андрей Карпов, Евгений РыжковДата: 10.02.2008АннотацияСтатья познакомит разработчиков прикладного программного обеспечения с задачами, которыеставит перед ними массовое внедрение многоядерных 64-битных вычислительных систем,знаменующих революционное увеличение вычислительной мощности, доступное рядовомупользователю. Будут рассмотрены вопросы эффективного использования аппаратных ресурсовдля решения повседневных прикладных задач в рамках операционной системы Windows x64.Информация читателюПо умолчанию в статье под операционной системой будет пониматься Windows. Под 64-битнымисистемами следует понимать архитектуру x86-64 (AMD64). Под средой разработки - Visual Studio2005/2008. Скачать демонстрационный пример, о котором будет говориться в статье можно поадресу: http://www.viva64.com/articles/testspeedexp.zip.ВведениеПараллельные вычисления и большой объем оперативной памяти перестают быть привилегиейбольших программно-аппаратных комплексов, предназначенных для масштабных научныхвычислений, и переходят на службу в решении повседневных задач, связанных с работой, учебой,развлечениями и компьютерными играми.Возможность распараллеливания и большой объем оперативной памяти с одной стороныоблегчает разработку ресурсоемких приложений, но с другой требует от программиста большейквалификации и знаний в области параллельного программирования. К сожалению, сейчас этойквалификацией и знаниями обладают далеко не все разработчики. Но не потому, что они плохиеразработчики, а лишь в силу того, что им не приходилось сталкиваться с подобными задачами.Это не удивительно, так как созданием параллельных систем обработки информации донедавнего времени занимались в основном в научных институтах, решая задачи моделирования ипрогнозирования. Параллельные вычислительные комплексы с большим объемом памятиприменялись и в прикладных целях на предприятиях, в банках и так далее, но до недавнеговремени они были весьма дороги и не многие разработчики имели возможность познакомиться сособенностями разработки программного обеспечения для таких систем.Авторам статьи довелось участвовать в разработке ресурсоемких программных продуктов,связанных с визуализацией и моделированием физических процессов, и на себе почувствоватьвсю специфичность разработки, тестирования и отладки систем подобного типа. Подресурсоемким программным обеспечением понимается программный код, эффективноиспользующий возможности многопроцессорных систем и большой объем памяти (от гигабайта и
  2. более). Поэтому, хочется по возможности снабдить разработчиков знаниями, которые могутпригодиться им в ближайшее время при освоении современных параллельных 64-битных систем.Справедливо отметим, что вопросы, связанные с параллельным программированием, уже давнои подробно проработаны и нашли свое отражение во многих книгах, статьях и учебных курсах. Всвязи с этим акцент в статье будет смещен в сторону организационных и практических вопросовразработки высокопроизводительных приложений и использованию 64-битных технологий.Говоря о 64-битных системах, мы будем считать, что они используют модель данных LLP64 (см.таблицу N1). Именно такая модель данных используется в 64-битных версиях операционнойсистемы Windows. Но приведенная информация может быть полезной и при работе с системами сотличной от LLP64 моделью данных. Таблица 1. Модели данных и их использование в различных операционных системах.
  3. 1. Смело используйте параллельность и 64-битностьОсознавая всю консервативность в разработке больших программных систем, тем не менее,хочется порекомендовать использовать те возможности, которые представляют многоядерные64-битные процессоры. Это может стать большим конкурентным преимуществом переданалогичными системами, а так же стать хорошим новостным поводом в рекламных кампаниях.Нет смысла откладывать 64-битность и параллельность на потом, так как их освоение неизбежно.Можно безболезненно пропустить повсеместное увлечение новым языком программированияили не оптимизировать программу под технологию MMX. Но нельзя уйти от роста объемаобрабатываемых данных и замедления скорости роста тактовой частоты. Давайте остановимся наэтом утверждении более подробно.Параллелизм становится основой роста производительности, что связано с замедлением темповроста тактовой частоты современных микропроцессоров. В то время как количество транзисторовна кристалле неуклонно растет, с 2005 года наметился резкий спад темпов роста тактовой частоты(смотри рисунок 1). Интересной работой по этой теме является статья "The Free Lunch Is Over. AFundamental Turn Toward Concurrency in Software" [1].
  4. Рисунок 1. Рост тактовой частоты и количества транзисторов на кристалле.В последние 30 лет производительность определялась тактовой частотой, оптимизациейисполнения команд и увеличением кэша. В ближайшие годы она будет определятьсямногоядерностью. Основным направлением развития технологий программирования станетразвитие средств параллельного программирования.Параллельное программирование позволит не только обойти проблему замедления скоростироста тактовой частоты, но и принципиально перейти к созданию масштабируемогопрограммного обеспечения, полностью использующего увеличение количества вычислительныхузлов в процессоре. То есть, программное обеспечение будет получать приростпроизводительности не только от увеличения тактовой частоты микропроцессора, но и от ростаколичества ядер. За такими системами будущее программного обеспечения. И тот, кто быстрееосвоит новые технологи, сможет существенно перераспределить рынок программногообеспечения в свою пользу.Использование 64-битных технологий, хотя и не выглядит столь внушительно по сравнению спараллельностью, тем не менее, также открывает много новых возможностей. Во-первых, этобесплатный прирост производительности на 5-15%. Во-вторых, большое адресное пространстворешает проблему фрагментации оперативной памяти при работе с большими объектами.
  5. Решение этой задачи является головной болью для многих разработчиков, чьи программыаварийно завершаются из-за нехватки памяти после нескольких часов работы. В-третьих, этовозможность легко работать с массивами данных в несколько гигабайт. Иногда это приводит кпоразительному приросту производительности, за счет исключения операций доступа к жесткомудиску.Если вышесказанное не убедило Вас в преимуществах 64-битных систем, то посмотритевнимательнее, чем занимаются Ваши коллеги или Вы сами. Кто-то оптимизирует код, повышаяпроизводительность функции на 10%, хотя эти 10% можно получить простой перекомпиляциейпрограммы под 64-битную архитектуру? Кто-то создает свой класс для работы с массивами,подгружаемыми из файлов, так как полностью эти массивы не помещаются в память? Вы делаетесвой менеджер распределения памяти, чтобы не фрагментировать память? Если Вы ответите наодин из вопросов - "Да", то следует остановиться и подумать. Вероятно, Вы ведете бесполезноесражение. И возможно будет выгоднее потратить время на перенос вашего приложения на 64-битную систему, где все эти вопросы исчезнут сами собой. Тем более, что рано или поздно Вы всеравно потратите на это время.Подытожим сказанное. Нет смысла тратить время, чтобы выжать последние возможности из 32-битной архитектуры. Экономьте свое время. Используйте для повышения производительностипараллельность и 64-битное адресное пространство. Сделайте новый информационный повод иопередите своих конкурентов при освоении рынка высокопроизводительных приложений.2. Вооружитесь хорошим аппаратным обеспечениемИтак, Вы приняли решение использовать параллельность и 64-битные технологии в вашихпрограммных разработках. Замечательно. Тогда давайте вначале рассмотрим некоторыеорганизационные вопросы.Не смотря на то, что Вам приходится сталкиваться с разработкой сложных программ,обрабатывающих огромные объемы данных, руководство все равно часто не понимаетнеобходимость обеспечения своих разработчиков наиболее мощными компьютерами. Хотя Вашиприложения могут быть предназначены для мощных рабочих станций, Вам, возможно, все равнопридется их тестировать и отлаживать на своей рабочей машине. Конечно, потребность в мощнойвычислительной технике существует у всех программистов, пусть даже их программы необрабатывают гигабайты данных. Всем приходится их компилировать, запускать вспомогательныетяжеловесные инструменты и так далее. Но только разработчик ресурсоемкого программногообеспечения может во всех деталях ощутить неприятности от недостаточного количестваоперативной памяти или медлительности дисковой подсистемы.Покажите эту часть статьи своему руководителю. Сейчас мы попробуем объяснить, почемувыгодно вложить средства в Ваши инструменты - компьютеры.Это может прозвучать неоригинально, но быстрый процессор и быстрая дисковая подсистемамогут существенно ускорить процесс компиляции приложений! Кажется между двумя или однойминутой компиляции части кода нет разницы? Она огромна! Минуты выливаются в часы, дни,месяцы. Попросите своих программистов посчитать, сколько времени они проводят в ожиданиикомпиляции кода. Поделите это время хотя бы в 1.5 раза и затем сможете подсчитать, как быстроокупится вложение в новую технику. Уверяю Вас - Вы будете приятно удивлены.
  6. Помните и про следующий эффект, который будет экономить рабочее время. Если какое-тодействие длится 5 минут, то человек подождет. Если 10 - то он пойдет готовить кофе, читатьфорумы или играть в пинг-понг, что займет гораздо больше 10 минут! И, не потому что он злодей,или очень хочет кофе - ему будет просто скучно. Не давайте прерываться действию: нажал -получил результат. Сделайте так, чтобы те процессы, которые занимали 10 минут - начализанимать меньше 5.Еще раз хочу обратить внимание - цель не занять свободное время программиста полезнымделом, а убыстрить процессы в целом. Установка второго компьютера (двухпроцессорнойсистемы) с целью, чтобы программист переключался в минуты ожидания на другие задачи - вкорне ошибочна. Труд программиста, это не труд дворника, где в перекурах между колкой льдаможно почистить лавку от снега. Труд программиста требует концентрации над задачей иудержание в памяти множества ее элементов. Не старайтесь переключить программиста,старайтесь сделать так, чтобы он как можно быстрее мог продолжить решать задачу, над которойсейчас работает. Никакой пользы от такой попытки не будет, Вы только еще больше утомитеразработчика, при меньшей эффективности труда. Согласно статье "Стрессы многозадачнойработы: как с ними бороться" [2], необходимое время на погружение в другую или прерваннуюзадачу составляет 25 минут. Если не обеспечить непрерывность процесса, половина временибудет уходить на это самое переключение. Не важно, что это - игра в пинг-понг, или поиск ошибкив другой программе.Не жалейте купить несколько лишних гигабайт памяти. Эта покупка окупится после несколькихэтапов отладки программы, выделяющей большой объем памяти. Знайте, что недостатокоперативной памяти приводит к выгрузке данных на диск (swapping) и может замедлить процессотладки с минут до часов.Не жалейте снабдить машину RAID подсистемой. Не будем теоретиками, вот пример из личнойпрактики (таблица N2).Конфигурация (обратите внимание на RAID) Время сборки среднего проекта, использующего большое количество внешних библиотек.AMD Athlon(tm) 64 X2 Dual Core Processor 95 минут3800+, 2 GB of RAM,2 x 250Gb HDD SATA - RAID 0AMD Athlon(tm) 64 X2 Dual Core Processor 140 минут4000+, 4 GB of RAM,500 Gb HDD SATA (No RAID)Таблица 2. Пример влияние RAID на скорость сборки приложения.Уважаемые руководители! Поверьте, что экономия на вычислительной технике с лихвойокупается простоями в работе программистов. Такие компании как Microsoft обеспечиваютразработчиков последними моделями вычислительной техники не от щедрости ирасточительности. Они как раз хорошо умеют считать деньги, и их пример не следуетигнорировать.На этом текст, посвященный руководителям, закончен, и мы вновь хотим обратиться к создателямпрограммных решений. Требуйте, требуйте для себя той техники, которую считаете себенеобходимой. Не стесняйтесь, в конце концов Ваш начальник, скорее всего, может просто непонимать, что это выгодно всем. Нужно заниматься просветительской работой. Тем более вслучае отставания в планах, виновным будете казаться Вы. Проще выбить новую технику, чемпытаться объяснить, на что Вы тратите время. Сами представьте, как может звучать Вашеоправдание о правке одной единственной ошибки в течение всего дня: "Так ведь проект большой
  7. прислали. Я запустил под отладчиком, долго ждал. А памяти у меня только 1 гигабайт. А большеничем параллельно заниматься невозможно. Windows в своп ушел. Нашел ошибку, поправил, нотак ведь опять снова запустить и проверить нужно....". Ваш начальник возможно промолчит, нобудет считать Вас просто лентяем. Не доводите до этого.Ваша первоочередная задача при разработке ресурсоемкого приложения - это не проектированиебудущей системы и даже не изучение теории, а заблаговременное требование о закупке всегонеобходимого аппаратного и программного обеспечения. Только после того можно смело иэффективно приступать к созданию ресурсоемких программных решений. Невозможно писать ипроверять параллельные программы без многоядерных процессоров. И невозможно писатьсистему для обработки больших объемов данных без необходимого объема оперативной памяти.Прежде чем перейти к следующей теме, хочется поделиться еще некоторыми мыслями, которыепомогут сделать работу более комфортной.Проблему медленной сборки проекта можно попробовать решить путем использованияспециальных средств параллельной сборки подобной, например системе IncrediBuild by XoreaxSoftware (http://www.xoreax.com). Естественно существуют и другие подобные системы, которыеможно поискать в сети.Проблему тестирования приложений на огромных массивах данных (запуск пакетов с тестами),для которых рабочие машины недостаточно производительны, можно решить использованиемнескольких специальных мощных машин с удаленным доступом. Примером удаленного доступаможет служить Remote Desktop или X-Win. Обычно одновременно тестовые запуски осуществляеттолько малое количество разработчиков. И для коллектива из 5-7 человек вполне может хватить2-х мощных выделенных машин. Это будет не самое удобное решение, но весьма экономичное,по сравнению с приобретением таких рабочих станций каждому разработчику.3. Меняем отладчик на систему протоколирования (логирования)Следующим препятствием, которое станет на Вашем пути, в разработке систем для обработкибольшого объема данных, будет то, что Вам, скорее всего, придется пересмотреть своюметодологию работы с отладчиком или даже полностью отказаться от его использования.Ряд специалистов предлагает отказаться от этой методологии тестирования по идеологическимсоображениям. Основной аргумент состоит в том, что отладчик провоцирует использованиеметода проб и ошибок. Человек, видя некорректное поведение алгоритма на каком то из этаповего выполнения, тут же производит правки, не вникая в суть, почему эта ошибка была допущена ине задумывается над способом ее исправления. Если он не угадал с исправлением, то приследующем выполнении кода он это заметит и внесет новые правки. Результатом становитсяменее качественный код. Причем автор этого кода далеко не всегда уверен, что понимает, как онработает. Противники отладки предлагают заменять ее более строгой дисциплиной разработкиалгоритмов, использованием как можно более мелких функций, чтобы принципы их работы былиочевидны. Также они предлагают уделять большее внимание юнит-тестированию и использоватьсистемы логирования (протоколирования) для анализа корректности работы программы.В описанной критике систем отладки есть рациональные зерна, но, как и во многих другихслучаях, следует все взвесить и не впадать в крайности. Использование отладчика часто удобно иможет сэкономить много сил и времени.
  8. 3.1. Причины, снижающие привлекательность отладчикаПлохая применимость отладчиков при работе с системами, обрабатывающими большие объемыданных, связана, к сожалению, не с идеологическими, а практическими сложностями. Хочетсяпознакомить читателей с этими сложностями, чтобы сэкономить их время на борьбу синструментом-отладчиком, когда он уже малопригоден и подвигнуть их к поиску альтернативныхрешений.Рассмотрим причины, требующие использования альтернативных средств вместо классическогоотладчика (например, встроенного в среду Visual C++).1) Медленное выполнение программы.Выполнение программы под отладчиком, обрабатывающей миллионы или миллиарды элементовможет стать практически неосуществимым из-за временных затрат. Во-первых, необходимоиспользовать отладочный вариант кода с выключенной оптимизацией, что уже существеннозамедляет скорость работы алгоритма. Во-вторых, в отладочном варианте происходит выделениебольшего объема памяти для контроля выхода за пределы массивов, заполнение памяти привыделении/удалении и так далее, что еще более замедляет время работы программы.Можно резонно заметить, что отлаживать программу вовсе не обязательно на больших рабочихобъемах данных, а обойтись тестовыми задачами. К сожалению, это не так. Неприятный сюрприззаключается в том, что при разработке 64-битных систем Вы не можете быть уверены вкорректности работы алгоритмов, тестируя их на небольших объемах данных, а не на рабочихобъемах размером в гигабайты.Приведем один простой пример, демонстрирующий проблему необходимости тестирования набольшом объеме данных.#include <vector>#include <boost/filesystem/operations.hpp>#include <fstream>#include <iostream>int main(int argc, char* argv[]){ std::ifstream file; file.open(argv[1], std::ifstream::binary); if (!file) return 1; boost::filesystem::path fullPath(argv[1], boost::filesystem::native); boost::uintmax_t fileSize =
  9. boost::filesystem::file_size(fullPath); std::vector<unsigned char> buffer; for (int i = 0; i != fileSize; ++i) { unsigned char c; file >> c; if (c >= A && c <= Z) buffer.push_back(c); } std::cout << "Array size=" << buffer.size() << std::endl; return 0;}Данная программа читает файл и сохраняет в массиве все символы, относящиеся к заглавныманглийским буквам. Если все символы в выходном файле будут заглавными английскимибуквами, то на 32-битной системе мы не сможем поместить в массив более 2*1024*1024*1024символов, а следовательно и обработать файл более 2 гигабайт. Представим, что такая программакорректно использовалась на 32-битной системе с учетом этого ограничения и никаких ошибок невозникало.На 64-битной системе возникнет желание обрабатывать файлы большего размера, так какснимается ограничение на размер массива в 2 гигабайта. К сожалению, программа написананекорректно с точки зрения модели данных LLP64 (см. таблицу N1), используемой в 64-битнойоперационной системе Windows. В цикле используется переменная типа int, размер которой по-прежнему составляет 32 бита. В случае если размер файла будет равен 6 гигабайт, то условие "i !=fileSize" никогда не будет выполнено и возникнет вечный цикл.Данный код приведен, чтобы продемонстрировать сложность поиска с помощью отладчикаошибок, которые возникают только на большом объеме памяти. Получив зацикливание приобработке файла в 64-битной системе можно взять для обработки файл размером в 50 байт ипросмотреть работу функции под отладчиком. Но ошибка на таком объеме данных не возникнет,а смотреть в отладчике обработку 6 миллиардов элементов невозможно.Естественно следует понимать, что это всего лишь пример, и что его легко можно отладить ипонять причину зацикливания. В сложных системах, к сожалению, такое часто становитсяпрактически нереализуемым из-за медленности обработки большого объема данных.Более подробно с подобными неприятными примерами Вы можете познакомиться в статье"Забытые проблемы разработки 64-битных программ" [3] и " 20 ловушек переноса Си++ - кода на64-битную платформу" [4].
  10. 2) Многопоточность.Использование нескольких параллельно выполняемых потоков команд для ускорения обработкибольшого объема данных давно и успешно используется в кластерных системах ивысокопроизводительных серверах. Но только с приходом на массовый рынок многоядерныхмикропроцессоров, возможность параллельной обработки данных начинает широкоиспользоваться прикладным программным обеспечением. И актуальность разработкипараллельных систем со временем будет только расти.К сожалению, не просто объяснить, в чем состоит сложность отладки параллельных программ.Только столкнувшись с задачей поиска и исправления ошибок в параллельных системах можнопочувствовать и понять беспомощность инструмента под названием отладчик. Но в целомпроблемы можно свести к невозможности воспроизведения многих ошибок и влиянию процессаотладки на последовательность работы параллельных алгоритмов.Более подробно с вопросами отладки параллельных систем Вы можете познакомиться вследующих статьях: "Технология отладки программ для машин с массовым параллелизмом" [5],"Multi-threaded Debugging Techniques" [6], "Detecting Potential Deadlocks" [7].Перечисленные трудности решаются использованием специализированных методологий иинструментов. С некорректным 64-битным кодом можно бороться, используя статическиеанализаторы, работающие с исходным кодом программы и не требующим его запуска. Примеромможет служить статический анализатор Viva64 [8].Для отладки параллельных систем следует обратить внимание в сторону таких инструментов какTotalView Debugger (TVD) [9]. TotalView это отладчик для языков Си, Си++ и фортран, которыйработает на Юникс-совместимых ОС и Mac OS X. Он позволяет контролировать нити исполения(потоки, thread), показывать данные одного или всех потоков, может синхронизировать нитичерез точки останова. Он поддерживает также параллельные программы, использующие MPI иOpenMP.Другими интересными приложениями является средства анализа многопоточности Intel®Threading Analysis Tools [10].3.2. Использование системы протоколирования (логирования)Перечисленные и оставшиеся за кадром инструменты, безусловно, полезны и могут статьхорошим подспорьем при разработке высокопроизводительных приложений. Но не стоитзабывать и о такой проверенной временем методологии, как использование систем логирования.Отладка методом логирования за несколько десятилетий ничуть не утратила актуальности иостается верным средством, о котором мы поговорим более подробно. Единственное изменение,которое накладывает время на системы логирования, это возросшие к ним требования.Попробуем перечислить свойства, которыми должна обладать современная система логирования,для высокопроизводительных систем: • Код, обеспечивающий логирование данных в отладочной версии, должен отсутствовать в конечной версии программного продукта. Во-первых, это связано с увеличением быстродействия и уменьшением размера программного продукта. Во-вторых, не позволяет использовать отладочную информацию для взлома приложения или иных несанкционированных действий.
  11. • Интерфейсы системы логирования должны быть лаконичны, чтобы не загромождать основной код программы. • Сохранение данных должно осуществляться как можно быстрее, чтобы вносить минимальное изменение во временные характеристики параллельных алгоритмов. • Полученный лог должен быть наглядным и легко поддаваться анализу. Должна существовать возможность разделить информацию, полученную от различных потоков, а также варьировать ее уровень подробности.Система логирования, отвечающая таким качествам позволяет универсально решать как задачуотладки параллельных алгоритмов, так и отлаживать алгоритмы обрабатывающие огромныемассивы данных.В статье не будет приведен конкретный код системы логирования. Такую систему трудно сделатьуниверсальной, так как она сильно зависит от среды разработки, особенностей проекта,предпочтений разработчика и многого другого. Вместо этого будет рассмотрен ряд техническихрешений, которые помогут Вам создать удобную и эффективную систему логирования, если в томвозникнет необходимость.Самым простым способом осуществить логирование является использование функции,аналогичной printf, как показано в примере: int x = 5, y = 10; ... printf("Coordinate = (%d, %d)n", x, y);Естественным недостатком является то, что информация будет выводиться как в отладочномрежиме, так и в конечном продукте. Поэтому, следует модернизировать код следующим образом:#ifdef DEBUG_MODE #define WriteLog printf#else #define WriteLog(a)#endif WriteLog("Coordinate = (%d, %d)n", x, y);Это уже лучше. Причем обратите внимание, что мы используем для выбора реализации функцииWriteLog не стандартный макрос _DEBUG, а собственный макрос DEBUG_MODE. Это позволяетвключать отладочную информацию в Release-версии, что важно при отладке на большом объемеданных.К сожалению, теперь при компиляции не отладочной версии в среде Visual C++ возникаетпредупреждение: "warning C4002: too many actual parameters for macro WriteLog". Можноотключить это предупреждение, но это является плохим стилем. Можно переписать код, какпоказано ниже:#ifdef DEBUG_MODE
  12. #define WriteLog(a) printf a#else #define WriteLog(a)#endif WriteLog(("Coordinate = (%d, %d)n", x, y));Приведенный код не является элегантным, так как приходится использовать двойные парыскобок, что часто забывается. Поэтому внесем новое усовершенствование:#ifdef DEBUG_MODE #define WriteLog printf#else inline int StubElepsisFunctionForLog(...) { return 0; } static class StubClassForLog { public: inline void operator =(size_t) {} private: inline StubClassForLog &operator =(const StubClassForLog &) { return *this; } } StubForLogObject; #define WriteLog StubForLogObject = sizeof StubElepsisFunctionForLog#endif WriteLog("Coordinate = (%d, %d)n", x, y);Этот код выглядит сложным, но он позволяет писать одинарные скобки. При выключенномDEBUG_MODE этот код превращается в ничто, и его можно смело использовать в критическихучастках кода.Следующим усовершенствованием может стать добавление к функции логирования такихпараметров, как уровень детализации и тип выводимой информации. Уровень детализацииможно задать как параметр, например:enum E_LogVerbose { Main, Full
  13. };#ifdef DEBUG_MODE void WriteLog(E_LogVerbose, const char *strFormat, ...) { ... }#else ...#endifWriteLog (Full, "Coordinate = (%d, %d)n", x, y);Этот способ удобен тем, что решение отфильтровать или не отфильтровать маловажныесообщения, можно принять уже после завершения работы программы, используя специальнуюутилиту. Недостаток такого метода в том, что всегда происходит вывод всей информации, какважной, так и второстепенной, что может снижать производительность. Поэтому можно создатьнесколько функций вида WriteLogMain, WriteLogFull и так далее, реализация которых будетзависеть от режима сборки программы.Мы упоминали о том, что запись отладочной информации должна как можно меньше влиять наскорость работы алгоритма. Этого можно достичь, создав систему накопления сообщений, записькоторых происходит в параллельно выполняемом потоке. Схематично этот механизм представленна рисунке N2.
  14. Рисунок N2. Система логирования с отложенной записью данных.Как можно видеть на рисунке, запись очередной порции данных происходит в промежуточныймассив строк фиксированной длины. Фиксированный размер массива и строк в нем позволяетисключить дорогостоящие операции выделения памяти. Это нисколько не снижает возможноститакой системы. Достаточно выбрать длину строк и размер массива с запасом. Например, 5000строк длиной в 4000 символов будет достаточно для отладки практически любой системы. Аобъем памяти в 20 мегабайт, необходимый для этого, согласитесь, не критичен для современныхсистем. Если же массив все равно будет переполнен, то несложно предусмотреть механизмдосрочной записи информации в файл.Приведенный механизм обеспечивает практически моментальное выполнение функции WriteLog.Если в системе присутствуют ненагруженные процессорные ядра, то и запись в файл будетпрактически прозрачна для основного кода программы.Преимущество описываемой системы в том, что она практически без изменений способнафункционировать при отладке параллельной программы, когда в лог пишут сразу несколькопотоков. Следует только добавить сохранение идентификатора процесса, чтобы потом можнобыло узнать, от каких потоков были получены сообщения (смотри рисунок N3).
  15. Рисунок N3. Система логирования при отладке многопоточных приложений.Последнее усовершенствование, которое хочется предложить, это организация показа уровнявложенности сообщений при вызове функций или начале логического блока. Это можно легкоорганизовать, используя специальный класс, который в конструкторе записывает в логидентификатор начала блока, а в деструкторе - идентификатор конца блока. Написав небольшуюутилитку, можно трансформировать лог, опираясь на информацию об идентификаторах.Попробуем показать это на примере.
  16. Код программы:class NewLevel {public: NewLevel() { WriteLog("__BEGIN_LEVEL__n"); } ~NewLevel() { WriteLog("__END_LEVEL__n"); }};#define NEW_LEVEL NewLevel tempLevelObject;void MyFoo() { WriteLog("Begin MyFoo()n"); NEW_LEVEL; int x = 5, y = 10; printf("Coordinate = (%d, %d)n", x, y); WriteLog("Begin Loop:n"); for (unsigned i = 0; i != 3; ++i) { NEW_LEVEL; WriteLog("i=%un", i); }}Содержимое лога:Begin MyFoo()__BEGIN_LEVEL__Coordinate = (5, 10)Begin Loop:__BEGIN_LEVEL__i=0__END_LEVEL____BEGIN_LEVEL__i=1__END_LEVEL__
  17. __BEGIN_LEVEL__i=2__END_LEVEL__Coordinate = (5, 10)__END_LEVEL__Лог после трансформации:Begin MyFoo() Coordinate = (5, 10) Begin Loop: i=0 i=1 i=2 Coordinate = (5, 10)Пожалуй, на этом можно закончить. Последнее о чем хочется еще упомянуть, это статья "LoggingIn C++" [11], которая также может Вам пригодиться. Желаем Вам удачной отладки.4. Использование правильных типов данных с точки зрения 64-битных технологийИспользование соответствующих аппаратной платформе базовых типов данных в языке Си/Си++является важным элементом для создания качественных и высокопроизводительныхпрограммных решений. С приходом 64-битных систем начали использоваться новые моделиданных - LLP64, LP64, ILP64 (см. таблицу N1), что изменило правила и рекомендациииспользования базовых типов данных. К таким типам можно отнести int, unsigned, long, unsignedlong, ptrdiff_t, size_t и указатели. К сожалению, вопросы выбора типов практически не освещены впопулярной литературе и статьях. А те источники, в которых они освещены, например "SoftwareOptimization Guide for AMD64 Processors" [12], редко читают прикладные программисты.Актуальность правильного выбора базовых типов для обработки данных обусловлена двумяважными причинами: корректностью работы кода и его эффективностью.Исторически сложилось, что базовым и наиболее используемым целочисленным типом в языкеСи и Си++ является int или unsigned int. Принято считать, что использование типа int являетсянаиболее оптимальным, так как его размер совпадает с длиной машинного слова процессора.Машинное слово - это группа разрядов оперативной памяти, выбираемая процессором за однообращение (или обрабатываемая им как единая группа), обычно содержит 16, 32 или 64 разряда.Традиция делать размер типа int равным размеру машинного слова до недавнего временинарушалась редко. На 16-битных процессорах int состоял из 16 бит. На 32-битных процессорах - 32
  18. бита. Конечно, существовали и иные соотношения размера int и машинного слова, но онииспользовались редко и не представляют сейчас для нас интереса.Нас интересует тот факт, что с приходом 64-битных процессоров размер типа int в большинствесистем остался равен 32-битам. Тип int имеет размер 32 бита в моделях данных LLP64 и LP64,которые используются в 64-битных операционных системах Windows и большинстве Unix систем(Linux, Solaris, SGI Irix, HP UX 11).Оставить размер типа int равным 32-м битам является плохим решением по многим причинам, нооно является обоснованным выбором меньшего среди других зол. В первую очередь оно связанос вопросами обеспечения обратной совместимости. Более подробно о причинах такого выбораможно прочесть в блоге "Why did the Win64 team choose the LLP64 model?" [13] и статью "64-BitProgramming Models: Why LP64?" [14].Для разработчиков 64-битных приложений все вышесказанное является предпосылкойпридерживаться двух новых рекомендаций в процессе разработки программного обеспечения.Рекомендация 1. Использовать для счетчиков циклов и адресной арифметики типы ptrdiff_t иsize_t, вместо int и unsigned.Рекомендация 2. Использовать для индексации в массивах типы ptrdiff_t и size_t, вместо int иunsigned.Другими словами, по возможности использовать типы данных, которые на 64-битной системеимеют размер 64-бита. Отсюда следует утверждение, что не следует больше использоватьконструкции вида:for (int i = 0; i != n; i++) array[i] = 0.0;Да, это канонический пример кода. Да, его много во множестве программ. Да, с него начинаютобучению языку Си и Си++. Но больше его использовать не рекомендуется. Используйте либоитераторы, либо типы данных ptdriff_t и size_t, как показано в улучшенном примере:for (size_t i = 0; i != n; i++) array[i] = 0.0;Разработчики Unix-приложений могут сделать замечание, что уже достаточно давно возниклапрактика использования типа long для счетчиков и индексации массивов. Тип long является 64-битным в 64-битных Unix-системах и его использование выглядит более элегантным, чем ptdriff_tили size_t. Да это так, но следует учесть два важных обстоятельства.1) В 64-битных операционных системах Windows размер типа long остался 32-битным (см. таблицуN1). И, следовательно, он не может быть использован вместо типов ptrdiff_t и size_t.2) Использование типов long и unsigned long еще больше усложняет жизнь разработчиков кросс-платформенных приложений для Windows и Linux систем. Тип long имеет в этих системах разныйразмер и только добавляет путаницы. Лучше придерживаться типов, имеющих одинаковыйразмер в 32-битных и 64-битных Windows и Linux системах.
  19. Пришло время на примерах пояснить, почему так настойчиво рекомендуется отказаться отпривычного использования типа int/unsigned в пользу ptrdiff_t/size_t.Начнем мы с примера, демонстрирующего классическую ошибку использования типа unsignedдля счетчика цикла в 64-битном коде. Мы уже описывали выше аналогичный пример, ноповторим его еще раз в силу распространенности данной ошибки:size_t Count = BigValue;for (unsigned Index = 0; Index != Count; ++Index){ ... }Это типичный код, варианты которого можно встретить во многих программах. Он корректновыполняется в 32-битных системах, где значение переменной Count не может превыситьSIZE_MAX (который равняется в 32-битной системе UINT_MAX). В 64-битной системе диапазонвозможных значений для Count может быть увеличен и тогда при значении Count > UINT_MAXвозникнет вечный цикл. Корректным исправлением данного кода использование вместо типаunsigned типа size_t.Следующий пример демонстрирует ошибку использования типа int для индексации большихмассивов:double *BigArray;int Index = 0;while (...) BigArray[Index++] = 3.14f;Этот код обычно не вызывает никаких подозрений у прикладного разработчика, привыкшего кпрактике использования в качестве индексов массивов переменные типа int или unsigned. Ксожалению, приведенный код на 64-битной системе будет неработоспособен, если объемобрабатываемого массива BigArray превысит размер в четыре миллиарда элементов. В этомслучае произойдет переполнение переменной Index, и результат работы программы будетнекорректен (будет заполнен не весь массив). Корректировка кода вновь заключена виспользовании для индексов типа ptrdiff_t или size_t.В качестве последнего примера, хочется продемонстрировать потенциальную опасностьсмешенного использования 32-битных и 64-битных типов, которого следует по возможностиизбегать. К сожалению не многие разработчики задумываются, к чему может привестинеаккуратная смешенная арифметика и для многих следующий пример оказываетсянеожиданностью (результаты получены с использованием Microsoft Visual C++ 2005, 64-битныйрежим компиляции):int x = 100000;int y = 100000;int z = 100000;intptr_t size = 1; // Результат:
  20. intptr_t v1 = x * y * z; // -1530494976intptr_t v2 = intptr_t(x) * y * z; // 1000000000000000intptr_t v3 = x * y * intptr_t(z); // 141006540800000intptr_t v4 = size * x * y * z; // 1000000000000000intptr_t v5 = x * y * z * size; // -1530494976intptr_t v6 = size * (x * y * z); // -1530494976intptr_t v7 = size * (x * y) * z; // 141006540800000intptr_t v8 = ((size * x) * y) * z; // 1000000000000000intptr_t v9 = size * (x * (y * z)); // -1530494976Хочется обратить внимание, что выражение вида "intptr_t v2 = intptr_t(x) * y * z;" вовсе негарантирует правильный результат. Оно гарантирует только то, что выражение "intptr_t(x) * y * z"будет иметь тип intptr_t. Более подробно с этими вопросами поможет разобраться статья "20ловушек переноса Си++ - кода на 64-битную платформу" [4].Теперь перейдем к примеру, демонстрирующему преимущества использования типов ptrdiff_t иsize_t с точки зрения производительности. Для демонстрации возьмем простой алгоритмвычисления минимальной длины пути. С полным кодом программы можно познакомиться поссылке: http://www.Viva64.com/articles/testspeedexp.zip.В статье для краткости приведен только текст функций FindMinPath32 и FindMinPath64. Обе этифункции высчитывают длину минимального пути между двумя точками в лабиринте. Остальнойкод не представляет сейчас интереса.typedef char FieldCell;#define FREE_CELL 1#define BARRIER_CELL 2#define TRAVERSED_PATH_CELL 3unsigned FindMinPath32( FieldCell (*field)[ArrayHeight_32], unsigned x, unsigned y, unsigned bestPathLen, unsigned currentPathLen){ ++currentPathLen; if (currentPathLen >= bestPathLen)
  21. return UINT_MAX;if (x == FinishX_32 && y == FinishY_32) return currentPathLen;FieldCell oldState = field[x][y];field[x][y] = TRAVERSED_PATH_CELL;unsigned len = UINT_MAX;if (x > 0 && field[x - 1][y] == FREE_CELL) { unsigned reslen = FindMinPath32(field, x - 1, y, bestPathLen, currentPathLen); len = min(reslen, len);}if (x < ArrayWidth_32 - 1 && field[x + 1][y] == FREE_CELL) { unsigned reslen = FindMinPath32(field, x + 1, y, bestPathLen, currentPathLen); len = min(reslen, len);}if (y > 0 && field[x][y - 1] == FREE_CELL) { unsigned reslen = FindMinPath32(field, x, y - 1, bestPathLen, currentPathLen); len = min(reslen, len);}if (y < ArrayHeight_32 - 1 && field[x][y + 1] == FREE_CELL) { unsigned reslen = FindMinPath32(field, x, y + 1, bestPathLen, currentPathLen); len = min(reslen, len);}field[x][y] = oldState;if (len >= bestPathLen) return UINT_MAX;
  22. return len;}size_t FindMinPath64( FieldCell (*field)[ArrayHeight_64], size_t x, size_t y, size_t bestPathLen, size_t currentPathLen){ ++currentPathLen; if (currentPathLen >= bestPathLen) return SIZE_MAX; if (x == FinishX_64 && y == FinishY_64) return currentPathLen; FieldCell oldState = field[x][y]; field[x][y] = TRAVERSED_PATH_CELL; size_t len = SIZE_MAX; if (x > 0 && field[x - 1][y] == FREE_CELL) { size_t reslen = FindMinPath64(field, x - 1, y, bestPathLen, currentPathLen); len = min(reslen, len); } if (x < ArrayWidth_64 - 1 && field[x + 1][y] == FREE_CELL) { size_t reslen = FindMinPath64(field, x + 1, y, bestPathLen, currentPathLen); len = min(reslen, len); } if (y > 0 && field[x][y - 1] == FREE_CELL) { size_t reslen = FindMinPath64(field, x, y - 1, bestPathLen, currentPathLen);
  23. len = min(reslen, len); } if (y < ArrayHeight_64 - 1 && field[x][y + 1] == FREE_CELL) { size_t reslen = FindMinPath64(field, x, y + 1, bestPathLen, currentPathLen); len = min(reslen, len); } field[x][y] = oldState; if (len >= bestPathLen) return SIZE_MAX; return len;}Функция FindMinPath32 написана в классическом 32-бином стиле с использованием типовunsigned. Функция FindMinPath64 отличается от нее только тем, что в ней все типы unsignedзаменены на типы size_t. Других отличий нет! Согласитесь, что это не является сложноймодификацией программы. А теперь сравним скорости выполнения этих двух функций (см.таблицу N2). Режим и функция. Время работы функции1 32-битный режим сборки. Функция FindMinPath32 12 32-битный режим сборки. Функция FindMinPath64 1.0023 64-битный режим сборки. Функция FindMinPath32 0.934 64-битный режим сборки. Функция FindMinPath64 0.85Таблица N2. Время выполнения функций FindMinPath32 и FindMinPath64.В таблице N2 показано приведенное время относительно скорости выполнения функцииFindMinPath32 на 32-битной системе. Это сделано для большей наглядности.В первой строке время работы функции FindMinPath32 на 32-битной системе равно 1. Это вызванотем, что мы взяли как раз ее время работы за единицу измерения.Во второй строке мы видим, что время работы функции FindMinPath64 на 32-битной системетакже равно 1. Это не удивительно, так как на 32-битной системе тип unsigned совпадает с типомsize_t и никакой разницы между функцией FindMinPath32 и FindMinPath64 нет. Небольшоеотклонение (1.002) говорит только о небольшой погрешности в измерениях.В третьей строке мы видим прирост производительности, равный 7%. Это вполне ожидаемыйрезультат от перекомпиляции кода для 64-битной системы.
  24. Наибольший интерес представляет 4 строка. Прирост производительности составляет 15%. Этозначит, что простое использование типа size_t вместо unsigned позволяет компилятору построитьболее эффективный код, работающий еще на 8% быстрее!Это простой и наглядный пример, когда использование данных, не равных размеру машинногослова, снижает производительность алгоритма. Простая замена типов int и unsigned на типыptrdiff_t и size_t может дать существенный прирост производительности. В первую очередь этоотносится к использованию этих типов данных для индексации массивов, адресной арифметики иорганизации циклов.Хочется надеяться после всего вышесказанного, Вы задумаетесь, стоит ли продолжать писать:for (int i = 0; i !=n; i++) array[i] = 0.0;Для автоматизации поиска ошибок в 64-бином коде разработчики Windows-приложений могутобратить внимание в сторону статического анализатора кода Viva64 [8]. Во-первых, егоиспользование позволит выявить большинство ошибок. Во-вторых, разрабатывая программы подего контролем, Вы станете реже использовать 32-битных переменные, будете избегатьсмешанной арифметики с 32-битными и 64-битными типами данных, что автоматически увеличитпроизводительность Вашего кода. Для разработчиков под Unix системы интерес могутпредставлять статические анализаторы Gimpel Software PC-Lint [15] и Parasoft C++test [16]. Ониспособны диагностировать ряд 64-битных ошибок в коде с моделью данных LP64, используемой вбольшинстве Unix-систем.Более подробно Вы можете познакомиться с вопросами разработки качественного иэффективного 64-битного кода в следующих статьях: "Проблемы тестирования 64-битныхприложений" [17], "24 Considerations for Moving Your Application to a 64-bit Platform" [18], "Portingand Optimizing Multimedia Codecs for AMD64 architecture on Microsoft Windows" [19], "Porting andOptimizing Applications on 64-bit Windows for AMD64 Architecture" [20].5. Дополнительные способы повышения производительностипрограммных системВ последней части этой статьи хочется рассмотреть еще несколько технологий, которые могутбыть Вам полезны при разработке ресурсоемких программных решений.5.1. Intrinsic-функцииIntrinsic-функции - это специальные системно-зависимые функции, выполняющие действия,которые невозможно выполнить на уровне Си/Си++ кода или которые выполняют эти действиянамного эффективнее. По сути, они позволяют избавиться от использования inline-ассемблера, т.к.его использование часто нежелательно или невозможно.Программы могут использовать intrinsic-функции для создания более быстрого кода за счетотсутствия накладных расходов на вызов обычного вида функций. При этом, естественно, размеркода будет чуть-чуть больше. В MSDN приводится список функций, которые могут быть замененыих intrinsic-версией. Это, например, memcpy, strcmp и другие.
  25. В компиляторе Microsoft Visual C++ есть специальная опция "/Oi", которая позволяетавтоматически заменять вызовы некоторых функций на intrinsic-аналоги.Помимо автоматической замены обычных функций на intrinsic-варианты, можно явноиспользовать в коде intrinsic-функции. Вот для чего это может быть нужно: • Встроенный (inline) ассемблерный код не поддерживается компилятором Visual C++ в 64- битном режиме. Intrinsic-код поддерживается. • Intrinsic-функции проще использовать, так как они не требуют знания регистров и других подобных низкоуровневых конструкций. • Intrinsic-функции обновляются в компиляторах. Ассемблерный же код придется обновлять вручную. • Встроенный оптимизатор не работает с ассемблерным кодом, поэтому требуется внешняя линковка модуля. Для intrinsic-кода такого не нужно. • Intrinsic-код легче переносить, чем ассемблерный.Использование intrinsic-функций в автоматическом режиме (с помощью ключа компилятора)позволяет получить бесплатно несколько процентов прироста производительности, а "ручное" -даже больше. Поэтому использование intrinsic-функций вполне оправдано.Более подробно с применением intrinsic-функций можно ознакомиться в блоге команды VisualC++ [21].5.2. Упаковка и выравнивание данныхВыравнивание данных в последнее время не так сильно сказывается на производительности кода,как, скажем, 10 лет назад. Однако иногда и тут можно получить дополнительный выигрыш вэкономии памяти и производительности.Рассмотрим пример:struct foo_original {int a; void *b; int c; };В 32-битном режиме данная структура занимает 12 байт, но в 64-битном - уже 24 байта. Для того,чтобы в 64-битном режиме структура занимала положенные ей 16 байт, следует изменитьпорядок следования полей:struct foo_new { void *b; int a; int c; };В некоторых случаях полезно явно помогать компилятору, задавая выравнивание вручную, чтобыувеличить производительность. Например, данные SSE должны быть выровнены по границе 16байт. Вот как этого можно добиться:// 16-byte aligned data__declspec(align(16)) double init_val [3.14, 3.14];// SSE2 movapd instruction_m128d vector_var = __mm_load_pd(init_val);
  26. Источники "Porting and Optimizing Multimedia Codecs for AMD64 architecture on Microsoft Windows"[19], " Porting and Optimizing Applications on 64-bit Windows for AMD64 Architecture" [20] даютподробный обзор данных вопросов.5.3. Файлы, отображаемые в памятьС появлением 64-битных систем технология отображения файлов в память стала болеепривлекательной в использовании, так как увеличилось окно доступа к данным. Для некоторыхприложений это может быть очень полезным приобретением. Не забывайте о нем.5.4. Ключевое слово __restrictОдна из наиболее серьезных проблем для компилятора - это совмещение (aliasing) имен. Когдакод читает и пишет память, часто на этапе компиляции невозможно определить, получает ли кданной области памяти доступ более чем один указатель. То есть, может ли более чем одинуказатель быть "синонимом" для одной и той же области памяти. Поэтому, например, внутрицикла, в котором и читается, и пишется память, компилятор должен быть очень осторожен схранением данных в регистрах, а не в памяти. Это недостаточно активное использованиерегистров может существенно повлиять на производительность.Ключевое слово __restrict используется для того, чтобы облегчить компилятору принятиерешения. Оно говорит компилятору "быть смелее" с использованием регистров.Ключевое слово __restrict позволяет компилятору не считать отмеченные указателисинонимичными (aliased), то есть ссылающимися на одну и ту же область памяти. Компилятор втаком случае может произвести более эффективную оптимизацию. Рассмотрим пример:int * __restrict a;int *b, *c;for (int i = 0; i < 100; i++){ *a += *b++ - *c++ ; // no aliases exist}В данном коде компилятор может безопасно хранить сумму в регистре, связанном с переменной"a", избегая записи в память. Хорошим источником информации об использовании ключевогослова __restrict является MSDN.5.5. SSE-инструкцииПриложения, запускаемые на 64-битных процессорах (независимо от режима) будут работатьболее эффективно, если в них используются SSE-инструкции вместо MMX/3DNow. Это связанно сразрядностью обрабатываемых данных. SSE/SSE2 инструкции оперируют 128-битными данными, вто время как MMX/3DNow - только лишь 64-битными. Поэтому код, использующих MMX/3DNow,лучше переписать с ориентацией на SSE.В данной статье мы не будем останавливаться на SSE-инструкциях, отсылая интересующихсячитателей к документации от разработчиков процессорных архитектур.
  27. 5.6. Определенные правила использования языковых инструкций64-битная архитектура приносит новые возможности для оптимизации на уровне отдельныхоператоров языка программирования. Это уже ставшие традиционными приемы по"переписыванию" кусочков программы с тем, чтобы компилятор еще лучше их оптимизировал.Рекомендовать к массовому использованию эти приемы, конечно же, не стоит, но знать о нихможет быть полезно.На первом месте из целого списка данных оптимизаций стоит ручное разворачивание циклов(unroll the loop). Суть данного метода легко увидеть из примера:double a[100], sum, sum1, sum2, sum3, sum4;sum = sum1 = sum2 = sum3 = sum4 = 0.0;for (int i = 0; i < 100; I += 4){sum1 += a[i];sum2 += a[i+1];sum3 += a[i+2];sum4 += a[i+3];}sum = sum1 + sum2 + sum3 + sum4;Во многих случаях, компилятор сам может развернуть цикл до такого представления (ключ /fp:fastдля Visual C++), но не всегда.Другой синтаксической оптимизацией является использование массивной (от слова "массив")нотации вместо указательной (от слова "указатель").Множество подобных приемов приведено в "Software Optimization Guide for AMD64 Processors"[12].ЗаключениеНесмотря на то, что при создании программных систем, эффективно использующих аппаратныевозможности современной вычислительной техники, придется столкнуться с большимколичеством трудностей, игра стоит свеч. Параллельные 64-битные системы открывают новыевозможности в построении настоящих масштабируемых решений. Они позволяют поднять нановый уровень возможности современных программных средств в обработке данных, будь тоигры, CAD-системы или распознавание образов. Желаем Вам удачи в освоении новых технологий!Библиографический список 1. Herb Sutter. The Free Lunch Is Over. A Fundamental Turn Toward Concurrency in Software. http://www.viva64.com/go.php?url=53
  28. 2. Стрессы многозадачной работы: как с ними бороться. http://www.viva64.com/go.php?url=543. Андрей Карпов. Забытые проблемы разработки 64-битных программ. http://www.viva64.com/art-1-1-1609701563.html4. Андрей Карпов, Евгений Рыжков. 20 ловушек переноса Си++ - кода на 64-битную платформу. http://www.viva64.com/art-1-1-1958348565.html5. Самофалов В.В., Коновалов А.В. Технология отладки программ для машин с массовым параллелизмом // "Вопросы атомной науки и техники". Сер. Математическое моделирование физических процессов. 1996. Вып. 4. С. 52-56.6. Shameem Akhter and Jason Roberts. Multi-threaded Debugging Techniques // Dr. Dobbs Journal, April 23, 2007, http://www.viva64.com/go.php?url=557. Tomer Abramson. Detecting Potential Deadlocks // Dr. Dobbs Journal, January 01, 2006, http://www.viva64.com/go.php?url=568. Viva64 Tool Overview. http://www.viva64.com/viva64.php9. The TotalView Debugger (TVD). http://www.viva64.com/go.php?url=5710. Intel Threading Analysis Tools. http://www.viva64.com/go.php?url=5811. Petru Marginean. Logging In C++ // Dr. Dobbs Journal, September 05, 2007, http://www.viva64.com/go.php?url=3712. Software Optimization Guide for AMD64 Processors. http://www.viva64.com/go.php?url=5913. Blog "The Old New Thing": "Why did the Win64 team choose the LLP64 model?". http://www.viva64.com/go.php?url=2514. "64-Bit Programming Models: Why LP64?" http://www.viva64.com/go.php?url=2415. Gimpel Software PC-Lint. http://www.gimpel.com16. Parasoft C++test. http://www.parasoft.com17. Андрей Карпов. Проблемы тестирования 64-битных приложений. http://www.viva64.com/art-1-1-929024801.html18. John Paul Mueller. 24 Considerations for Moving Your Application to a 64-bit Platform. http://www.viva64.com/go.php?url=3019. Harsha Jaquasia. Porting and Optimizing Multimedia Codecs for AMD64 architecture on Microsoft Windows. http://www.viva64.com/go.php?url=6020. Mike Wall. Porting and Optimizing Applications on 64-bit Windows for AMD64 Architecture. http://www.viva64.com/go.php?url=6121. Dylan Birtolo. Visual C++ Team Blog: New Intrinsic Support in Visual Studio 2008. http://www.viva64.com/go.php?url=62

×