вторник, 23 февраля 2010 г.

Многопроцессорная компиляция проектов на VC++ 2008

Одной из отличительных особенностей процесса компиляции исходных кодов на языке программирования С++ (по сравнению с языками с «двухфазной» компиляцией) является относительно большое время компиляции. Так, если длительность процесса компиляции проекта на C#, состоящего из пары сотен файлов занимает не более пары десятков секунд (естественно, что этот показатель зависит от аппаратной конфигурации компьютера), то процесс компиляции (и линковки) аналогичного по размеру проекта на С++ может занимать в десять раз больше времени.

Второй особенностью компиляции в языках С и С++ является независимая компиляция каждого модуля трансляции (.cpp (или .c) файла) с последующей линковкой всех объектных файлов в единую библиотеку или исполняемый файл. Из-за этой особенности возникает естественный вопрос о возможности параллельной компиляции нескольких .cpp файлов параллельно на многопроцессорном или многоядерном компьютере.

Начиная с Visual C++ 2008, компилятор поддерживает дополнительный ключ компиляции (/MP и /MPn), с помощью которого можно указать использовать параллельную компиляцию нескольких .cpp файлов одновременно. Распараллеливание процесса компиляции осуществляется за счет запуска нескольких процессов cl.exe для нескольких .cpp файлов одновременно.

Количество процессов cl.exe, которые будут запущены одновременно для компиляции различных модулей трансляции можно указать явно, используя ключ /MPn (например, /MP4, для запуска 4-х процессов), либо ключ /MP (в этом случае будет запущено количество процессов, равное количеству логических процессоров).

По словам членов команды Visual C++ Team прирост производительности (точнее уменьшение времени компиляции) может составлять порядка 30%, но это очень сильно зависит от того, какой процент времени будет занят линковкой объектных файлов. Так, при полной перекомпиляции проекта этот выигрыш будет заметнее, а при перекомпиляции только нескольких файлов – менее заметен.

Я проверял влияние ключа /MP на 4-х ядерном процессоре, на проекте C++/CLI, состоящий приблизительно из 350 файлов. Результаты откровенно порадовали:

4 ядра (без ключа /MP): общее время компиляции 9:16, линковка – 1:37
4 ядра (ключ /MP): общее время компиляции 4:47, линковка – 1:40

Как видите, при полной перекомпиляции результаты весьма впечатляющие.

Помимо параллельной компиляции отдельных .cpp файлов, Visual Studio 2008 поддерживает также параллельную сборку С++ проектов (возможна параллельная сборка только независимых проектов). Хотя это также уменьшает время компиляции, существенной разницы на своих проектах я не почувствовал, поэтому и особого впечателения на меня эта возможность не произвела.

Дополнительные ссылки
1. Multi-processor builds in Orcas, Visual C++ Team Blog
2. Building projects in parallel, MSBuild Team Blog
3. /MP (Build with Multiple Processes), MSDN,
4. Using Multiple Processors to Build Projects, MSDN
5. Enabling multiprocessor support in an MSBuild host, MSBuild Team Blog
6. Did you know… Only VC supports parallel building within the IDE - #324 , Sara Ford’s Weblog,
7. MPCL Plugin

воскресенье, 14 февраля 2010 г.

Что нового в третьем издании книги Джеффри Рихтера “CLR via C#”

CLR

До официального выхода третьего издания знаменитой книги Джеффри Рихтера “CLR via C#” остался еще один день (официальная дата выхода – 15 февраля 2010 года), а доброжелатели уже постарались над тем, чтобы эта книга стала достоянием широкой общественности.

Но речь здесь пойдет не об этом, а о тех нововведениях, которые появились в третьем издании по сравнению со вторым (хотя содержание третьего издания уже давно доступно, но сравнивать вручную содержания двух книг – дело хлопотное, поверьте, я только что этим занимался:)). Кроме того, некоторые темы я пробежался глазами, чтобы определить в чем именно заключаются отличия.

Первое, что бросается в глаза, так это то, что третье издание "подросло" в объеме чуть более чем на 150 страниц. Второе издание содержит 736 страниц, а третье - 896.

Первое изменение в содержании (не путать с содержимым, хотя есть сомнения в изменении содержимого первых четырех глав) найдена только лишь главе 5. Primitive, Reference, and Value Types): в третьем издании появился раздел: The dynamic Primitive type.

Глава 8 второго издания: Methods: Constructors, Operators, Conversions, and Parameters... разбита на две главы в третьем: Глава 8. Methods, Глава 9. Parameters.

Глава 8. Methods. В третьем издании добавлены нововведения из C#3.0:
Extension Methods
   Rules and Guidelines
   Extending Various Types with Extension Methods
   The Extension Attribute
Partial Methods
   Rules and Guidelines

Главе 9. Parameters. В третьем издании добавлены нововведения C# 3.0 и 4.0:
Optional and Named Parameters
   Rules and Guidelines
   The DefaultParameterValue and Optional Attributes
Implicitly Typed Local Variables
Parameter and Return Type Guidelines

Главе 10. Properties. В третьем издании добавлены нововведения C# 3.0 и C#4.0 :
Parameterless Properties
   Automatically Implemented Properties
   Object and Collection Initializers
   Anonymous Types
   The System.Tuple Type

Глава по обобщениям (Generics) теперь стала 12-й (во втором издании, была 16-й). Содержание этих глав совпадает, но размер этой главы в третьем издании увеличился с 28 страниц до 32. Буду рад, если кто-то скажет об отличиях:)

Глава 15. Enumerated Types and Bit Flags. В третьем издании добавлен раздел Adding Methods to Enumerated Types, в котором рассказывается о добавлении методов перечислениям с помощью методов расширения.

Глава 16. Arrays. В третьем издании добавлен раздел Initializing Array Elements, в котором говорится о новых возможностях инициализации массивов, появившихся в C# 3.0

Глава 17. Delegates. В третьем издании раздел Syntactical Shortcut #2: No Need to Define a Callback Method переписан с применением лямбда-выражений.

Глава 20. Exceptions and State Management. Теперь глава об обработке исключений называется именно так. В ней многие разделы имеют немного другие названия и слегка перетасованы (сложно сказать, как это отразилось на содержимом), но появились два новых раздела: Constrained Execution Regions (CERs) и Code Contracts

Глава 21. Automatic Memory Management (Garbage Collection). Содержание глав во втором и третьем издании практически совпадают, единственное отличие состоит в том, что в третьем издании эта глава на 8 страниц длиннее (72 в третьем издании, 64 - во втором).

Глава 22. CLR Hosting and AppDomains. Появились следующие разделы: AppDomain Monitoring, AppDomain First-Chance Exception Notifications, отличается раздел How Hosts Use AppDomains.

Глава 24. Runtime Serialization. Это новая глава, которая появилась в только в третьем издании книги.
Serialization/Deserialization Quick Start
Making a Type Serializable
Controlling Serialization and Deserialization
How Formatters Serialize Type Instances
Controlling the Serialized/Deserialized Data
   How to Define a Type That Implements ISerializable when the Base Type Doesn’t Implement This Interface
Streaming Contexts
Serializing a Type as a Different Type and Deserializing an Object as a Different Object
Serialization Surrogates
   Surrogate Selector Chains
Overriding the Assembly and/or Type When Deserializing an Object

Часть 5 теперь называется Threading. Наконец-то Рихтер оказался в своей стихии. В третьем издании вопросам многопоточности уделено 180 (!) страниц, а во втором издании только 64(!). Т.е. эту часть можно читать целиком.

Глава 25. Thread Basics
Why Does Windows Support Threads?
Thread Overhead
Stop the Madness
CPU Trends
NUMA Architecture Machines
CLR Threads and Windows Threads
Using a Dedicated Thread to Perform an Asynchronous Compute-Bound Operation
Reasons to Use Threads
Thread Scheduling and Priorities
Foreground Threads versus Background Threads
What Now?

Глава 26 Compute-Bound Asynchronous Operations
Introducing the CLR’s Thread Pool
Performing a Simple Compute-Bound Operation
Execution Contexts
Cooperative Cancellation
Tasks
   Waiting for a Task to Complete and Getting Its Result
   Cancelling a Task
   Starting a New Task Automatically When Another Task Completes
   A Task May Start Child Tasks
   Inside a Task
   Task Factories
   Task Schedulers
Parallel’s Static For, ForEach, and Invoke Methods
Parallel Language Integrated Query
Performing a Periodic Compute-Bound Operation
   So Many Timers, So Little Time
How the Thread Pool Manages Its Threads
   Setting Thread Pool Limits
   How Worker Threads Are Managed
Cache Lines and False Sharing

Глава 27. I/O-Bound Asynchronous Operations
How Windows Performs I/O Operations
The CLR’s Asynchronous Programming Model (APM)
The AsyncEnumerator Class
The APM and Exceptions
Applications and Their Threading Models
Implementing a Server Asynchronously
The APM and Compute-Bound Operations
APM Considerations
   Using the APM Without the Thread Pool
   Always Call the EndXxx Method, and Call It Only Once
   Always Use the Same Object When Calling the EndXxx Method
   Using ref, out, and params Arguments with BeginXxx and EndXxx Methods
   You Can’t Cancel an Asynchronous I/O-Bound Operation
   Memory Consumption
   Some I/O Operations Must Be Done Synchronously
   FileStream-Specific Issues
I/O Request Priorities
Converting the IAsyncResult APM to a Task
The Event-Based Asynchronous Pattern
   Converting the EAP to a Task
   Comparing the APM and the EAP
Programming Model Soup

Глава 28. Primitive Thread Synchronization Constructs
Class Libraries and Thread Safety
Primitive User-Mode and Kernel-Mode Constructs
User-Mode Constructs
   Volatile Constructs
   Interlocked Constructs
   Implementing a Simple Spin Lock
   The Interlocked Anything Pattern
Kernel-Mode Constructs
   Event Constructs
   Semaphore Constructs
   Mutex Constructs
   Calling a Method When a Single Kernel Construct Becomes Available

Глава 29 Hybrid Thread Synchronization Constructs
A Simple Hybrid Lock
Spinning, Thread Ownership, and Recursion
A Potpourri of Hybrid Constructs
   The ManualResetEventSlim and SemaphoreSlim Classes
   The Monitor Class and Sync Blocks
   The ReaderWriterLockSlim Class
   The OneManyLock Class
   The CountdownEvent Class
   The Barrier Class
Thread Synchronization Construct Summary
The Famous Double-Check Locking Technique
The Condition Variable Pattern
Using Collections to Avoid Holding a Lock for a Long Time
The Concurrent Collection Classes

Более подробное мое мнение по поводу содержимого этих изменений, я надеюсь можно будет узнать в ближайшем будущем, когда я с этими самыми изменениями познакомлюсь. Но в одном можно быть уверенным, Рихтер знает свое дело, поэтому если у вас возникнет вопрос о хорошей книге по платформе .NET, первой в этом списке должна быть именно эта книга!

UPDATE:

Только сейчас заметил, что "все уже украдено до нас" и Джеффри Рихтер в своем блоге уже писал об изменениях между вторым и третьим изданиями (почитать об этом можно здесь).

Ко всему, что я написал выше стоит добавить следующее:

Chapter 1-The CLR’s Execution Model
Added about discussion about C#’s /optimize and /debug switches and how they relate to each other.

Chapter 2-Building, Packaging, Deploying, and Administering Applications and Types
Improved discussion about Win32 manifest information and version resource information.

Chapter 3-Shared Assemblies and Strongly Named Assemblies
Added discussion of TypeForwardedToAttribute and TypeForwardedFromAttribute.

Chapter 5-Primitive, Reference, and Value Types
Enhanced discussion of checked and unchecked code and added discussion of new BigInteger type

Chapter 19-Nullable Value Types
Added discussion on performance.

Chapter 20-Exception Handling and State Management
This chapter has been completely rewritten. It is now about exception handling and state management.

Chapter 21-Automatic Memory Management
Added discussion of C#’s fixed state and how it works to pin objects in the heap. Rewrote the code for weak delegates so you can use them with any class that exposes an event (the class doesn’t have to support weak delegates itself). Added discussion on the new ConditionalWeakTable class, GC Collection modes, Full GC notifications, garbage collection modes and latency modes. I also include a new sample showing how your application can receive notifications whenever Generation 0 or 2 collections occur.

Chapter 23-Assembly Loading and Reflection
Added section on how to deploy a single file with dependent assemblies embedded inside it. Added section comparing reflection invoke vs bind/invoke vs bind/create delegate/invoke vs C#’s dynamic type.

Часть 5. Threading таки является полностью новой.

пятница, 12 февраля 2010 г.

Книга Джоэла Спольски “Джоэл. И снова о программировании”

More_Joel_on_Software Так уж получается, что к продолжениям аудитория всегда предъявляет более высокие требования, чем к первым частям произведений. Классическим примером является кинематограф, ведь каждый раз после выхода продолжения очередного (особенно удачного) фильма постоянно слышишь что-то вроде: "Да, фильм/актер/актриса/режиссер/продюсер/мальчик, заваривающий кофе уже не тот, вот первая часть фильма была ого-го, а вторая - так себе. Вполне естественно, что подобное отношение к продолжениям касается не только голливудских блокбастеров, но и компьютерной литературы.

Сегодня речь пойдет о продолжении известной книги Джоэла Спольски "Джоэл. И снова о программировании", вышедшей вслед за первой частью с не менее завораживающим названием "Джоэл о программировании". Обе книги являются представителями относительно нового течения в компьютерной литературе, которое называется "блуком" (от английского book (книга) и blog). По-сути, Джоэл является родоначальником этого течения и, наверное, одним из самых ярких его представителей. Вторая книга Джоэла представляет собой минимально измененный набор статей, опубликованных на сайте Джоэла joelonsoftware.com, по самой различной тематике, начиная от вопросов образования, заканчивая проблемами служб технической поддержки и ценовой политикой для коробочных программных продуктов.

Если сравнивать эту книгу с первой частью (что, как было сказано, является вполне естественным стремлением), то складывается впечатление, что большая часть наиболее известных, популярных и интересных статей, все же досталась первой книге. Я не говорю, что "Джоэл уже не тот", нет, это не так. Но нужно сказать, что средний уровень второй книги все же значительно ниже. Если первая часть книги в основном состоит из ярких и очень сильных статей, нескольких средних и нескольких откровенно спорных, то вторая часть книги более ровная. Там практически нет откровений, но и спорных моментов меньше (за исключением главы об обработке исключений, Джоэл является ярым противником этого явления, как средства обработки ошибок). Но, не смотря на это в книге достаточно много сильных глав. Мне очень понравилась серия глав об образовании, главы о различных способах управления, о практическом применении качественной офисной среды в компании Fog Creek, о том, как создать компанию, в которой вы бы сами хотели работать и многое другое. В целом Джоэл остается самим собой, как всегда ироничный, то критикующий Майкрософт, то восхищающийся ею, восхищенный Стивом Джобсом и продуктами Apple, немного рекламирующий свою компанию, ее продукты и методы управления, иногда повторяющийся (а иногда и противоречащий себе), но всегда интересный. Достаточно прочитать несколько страниц, чтобы оценить стоит дальше читать или нет.

Отдельно хочется коснуться перевода. Перевод адекватный, есть небольшие ляпы (но у кого их нет), но чего мне особенно не хватало, так это научного редактора первой части книги. В первой части в примечаниях редактор не только проливали свет на некоторые вопросы (что упрощало понимание материала), но и, не стесняясь, спорил с мнением автора. Хотя многим подобные вольности редактора могли не понравиться, мне читать такие диспуты было интересно. Здесь же ничего такого нет, а очень бы хотелось.

Напоследок, хочется коснуться классического вопроса, который задает себе практически каждый, когда речь идет о блуке: "зачем мне нужна книга, все содержимое которой я могу свободно найти в интернете?". На этот вопрос я отвечу цитатой редактора этой книги Гэри Корнелла, который говорит об успехе первой части книги Джоэла следующее: "Мне кажется, секрет здесь в том, что смаковать деликатес вроде статей Джоэла гораздо приятнее в печатном виде, чем в окне броузера". И в этом вопросе я с ним полностью согласен.

Оценка: твердая четверка.

понедельник, 8 февраля 2010 г.

Диагностика проблем загрузки сборок

Практически каждый разработчик сталкивался с неприятной ситуацией, когда во время загрузки приложения, разработанного с использованием .NET Framework, возникают какие-то ошибки, связанные с поиском или загрузкой сборок и запуск приложения завершается предложением отправить отчет в Майкрософт. Кроме того, практически каждый, кто читал замечательную книгу Джеффри Рихтера, ужаснулся тому многообразию вариантов, откуда может быть загружена сборка, а также богатым возможностям администрирования .Net приложений (probing, dependend assemblies, codebase, Publisher Policy и др.) [1], [2]. Помимо проблем с поиском нужной сборки подливают масла в огонь вероятные ошибки загрузки сборок, связанные с вопросами безопасности (в результате чего генерируется SecurityException), а также форматом сборки (исключение BadImageFormat).

Хотя многие разработчики относительно быстро справляются с подобными проблемами, потому что прекрасно знают архитектуру своего приложения и за несколько минут могут определить, что у пользователя не установлен сторонний компонент, не хватает нужных сборок в подпапке приложения или еще что-либо в этом духе, для неподготовленных (или утомленных) умов диагностика подобных проблем может занимать довольно много времени и отнимать массу нервов.

За загрузку сборок в CLR отвечает специальный загрузчик, получивший кодовое имя Fusion. Если в процессе загрузки сборок возникают проблемы, то для упрощения диагностики существует возможность включить логгирование этого процесса. Для этого необходимо задать следующие параметры в реесте: установить параметр HKLM\Software\Microsoft\Fusion\ForceLog в 1, а в значении параметра HKLM\Software\Microsoft\Fusion\LogPath указать путь хранения лог-файла (по умолчанию, этих параметров в реестре нет, соответственно, диагностика загрузки сборок не производится).

Для проверки ошибок загрузки сборок я создал простое решение (solution) с двумя проектами: консольным приложением TestFusion и библиотекой классов TestFusionLib. Я добавил в библиотеку класс TestClass, а в TestFusion добавил использование этого класса. После компиляции обоих проектов, я удалил из папки bin файл TestFustionLib и запустил TestFustion.exe.

D:\Sources\VS2008\TestFusion\TestFusion\bin\Debug>TestFusion.exe

Необработанное исключение: System.IO.FileNotFoundException: Невозможно загрузить файл или сборку "TestFusionLib, Version

=1.0.0.0, Culture=neutral, PublicKeyToken=null" или один из зависимых от них компонентов. Не удается найти указанный файл.

Имя файла: "TestFusionLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

   в TestFusion.Program.Main(String[] args)

Диспетчер сборки загружен с:  C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll

Выполняется в контексте исполняемого файла  D:\Sources\VS2008\TestFusion\TestFusion\bin\Debug\TestFusion.exe

--- Подробный журнал ошибок.

=== Информация о состоянии предварительной привязки ===

Журнал: User = HOME\Sergey

Журнал: DisplayName = TestFusionLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

 (Fully-specified)

Журнал: Appbase = file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/

Журнал: Initial PrivatePath = NULL

Вызов сборки: TestFusion, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.

===

Журнал: данная привязка начинается в контексте загрузки default.

Журнал: файл конфигурации приложения не найден.

Журнал: используется файл конфигурации компьютера из C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config

.

Журнал: политика в данный момент не применяется к ссылке (личная, пользовательская, частичная привязка сборки или привязка по местоположению).

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib.DLL.

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib/TestFusionLi

b.DLL.

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib.EXE.

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib/TestFusionLib.EXE.

Помимо просмотра и конфигурирования процесса логирования загрузки сборок вручную, в составе .NET Framework SDK поставляется полезная утилита с названием Fuslogvw.exe (Fusion Log Viewer) [3], которая в значительной степени упрощает подобный процесс диагностики.

Внимание! Утилиту Fuslogvw.exe необходимо запускать с правами Администратора, в противном случае вы не сможете изменить ни какие параметры.

Примечание. Путь к Fuslogvw.exe: %ProgramFiles%\MicrosoftSDKs\Windows\v6.0A\bin\Fuslogvw.exe

Если после неудачного запуска приложения (в нашем случае TestFusion.exe) запустить Fuslogvw.exe (или нажать кнопку Refresh, если эта утилита уже была запущена), то мы увидим следующую картину:

Fuslogvw

Нас интересует вторая строка. Если на ней нажать View Log, получим следующие данные:

*** Запись журнала привязки сборки  (06.02.2010 @ 17:47:02) ***

Операция выполнена со сбоем.

Результат привязки: hr = 0x80070002. Не удается найти указанный файл.

Диспетчер сборки загружен с:  C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll

Выполняется в контексте исполняемого файла  D:\Sources\VS2008\TestFusion\TestFusion\bin\Debug\TestFusion.exe

--- Подробный журнал ошибок.

=== Информация о состоянии предварительной привязки ===

Журнал: User = HOME\Sergey

Журнал: DisplayName = TestFusionLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

 (Fully-specified)

Журнал: Appbase = file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/

Журнал: Initial PrivatePath = NULL

Журнал: Dynamic Base = NULL

Журнал: Cache Base = NULL

Журнал: AppName = NULL

Вызов сборки: TestFusion, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.

===

Журнал: данная привязка начинается в контексте загрузки default.

Журнал: файл конфигурации приложения не найден.

Журнал: используется файл конфигурации компьютера из C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.

Журнал: политика в данный момент не применяется к ссылке (личная, пользовательская, частичная привязка сборки или привязка по местоположению).

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib.DLL.

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib/TestFusionLib.DLL.

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib.EXE.

Журнал: попытка загрузки нового URL file:///D:/Sources/VS2008/TestFusion/TestFusion/bin/Debug/TestFusionLib/TestFusionLib.EXE.

Журнал: все попытки проверки URL закончились неудачно.

Конечно же, приведенный пример надуман, и здесь даже в описании исходного исключения видно, в чем проблема, но реальные сценарии очень часто являются достаточно сложными, и в таких случаях механизм просмотра логов загрузки сборок может сэкономить огромное количество времени вам и вашим пользователям.

Отступление от темы. Диагностика проблем загрузки неуправляемых библиотек

Обсуждая вопрос диагностики загрузки управляемых библиотек, нельзя оставить без внимания вопросы загрузки неуправляемых библиотек (native dll). На различных форумах очень часто задают вопросы подобного рода: «Мое приложение при переносе с моей машины на какую-то другую, перестает запускаться. В чем может быть проблема?». Подобная проблема очень часто связана с тем, что при компиляции неуправляемого кода (например, mixed сборок, разработанных с помощью C++/CLI) помимо стандартных управляемых библиотек приложение использует C Runtime Library в виде отдельной dll. Поскольку на машине разработчика эта библиотека существует в папке System32 с тех самых пор, как на этот компьютер установлена среда разработки, это не вызывает никаких проблем у разработчика, но этой библиотеки очень часто не бывает на машине пользователя. Многие разработчики справляются с этой проблемой путем включения в свой инсталляционный пакет Visual С++ Redistributable Package  

В отладке подобных проблем первое, что нужно сделать, это скачать замечательную утилиту под названием Dependency Walker [5] и попытаться открыть ваш exe-файл с помощью этой утилиты на машине пользователя (или на машине с идентичной конфигурацией). Dependency Walker рекурсивно проходится по всем неуправляемым библиотекам и сразу же покажет вам, какой именно неуправляемой библиотеки не хватает.

Дополнительные ссылки

1.     Джеффри Рихтер, CLR via C#, Глава 2, раздел “Простое средство администрирования (конфигурационный файл)”.

2.     Джеффри Рихтер, CLR via C#, Глава 3, раздел “Дополнительные конфигурационные средства (конфигурационные файлы)”.

3.     Assembly Binding Log Viewer (Fuslogvw.exe) in MSDN documentation

4.     Debugging Assembly Loading Failures by Suzanne Cook (http://blogs.msdn.com/suzcook/archive/2003/05/29/57120.aspx)

5.     Утилита Dependency Walker

пятница, 5 февраля 2010 г.

[ANN] Бесплатные тесты на Brainbench по C#3.0 и .NET framework 3.5

Я прекрасно осознаю ограниченность подхода оценивать способностей разработчика по подобным синтетическим тестам. Этот вопрос не раз обсуждался на различных блогах и форумах, но не смотря на это очень часто в резюме люди оставляют ссылки на свой public transcript или явно указывают оценки, полученные при cдаче того или иного теста.

Конечно же не факт, что кто-то из работодателей возьмет вас на работу именно благодаря пачке таких сертификатов, но с другой стороны, это вполне может быть той небольшой каплей, которая перевесит чашу весов в вашу сторону. Кроме того, всегда интересно попробовать свои силы и сравнить свои знания (хотя и понятно, что это и не очень-то корректно) с другими представителями своей профессии (причем, со всего мира).

Любопытно то, что сейчас на brainbench доступны 3 новых теста по платформе .NET, это C#3.0, C#3.0 Fundamentals (я честно говоря не особо понимаю, в чем отличия между подобными тестами) и .NET Framework 3.5 (есть еще C#1.0, который висит в общедоступных тестах, кажется уже около года). Насколько эти тесты являются адекватными (если такое понятие, как адекватность вообще можно применить к подобному тестированию) я не знаю, т.к. сам я их еще не сдавал, но, как выдастся время обязательно попробую.

Будете ли вы сдавать подобные тесты или нет, я не знаю, но хотя бы знать, что сейчас такая возможность есть, думаю, будет полезно.

среда, 20 января 2010 г.

Выбор типа возвращаемого значения

Не так давно на форуме rsdn.ru был поднят вопрос о выборе типа возвращаемого значения для некоторого метода, возвращающего коллекцию объектов. Какой тип выбрать: более абстрактный (например, для коллекциях в C# это может быть IEnumerable<T>), более конкретный (например, List<T>) или остановиться на каком-либо промежуточном варианте (например, IList<T>)?

К этому вопросу можно подойти с двух сторон. С чисто теоретической точки зрения и с прагматично-практической.

Начнем по-порядку.

Теоретические обоснования выбора типа возвращаемого значения

Теоретическое обоснование выбора типа возвращаемого значения восходит к формальной теории верификации программ и проектированию по контракту Бертрана Мейера.

Существует формальная математическая нотация определяющая корректность некоторых программ, которая представлена формулой корректности.

{P} A {Q}

Определение этой формулы звучит так:

Любое выполнение A, начинающееся в состоянии, где P истинно, завершится и в заключительном состоянии будет истинно Q.

Эта формула также называется триадой Хоара, а P и Q представляет собой утверждения, называемые предусловие и постусловие соответственно.

Рассмотрим пример триады Хоара для некоторой функции, например, возведения в квадрат.

{x = 5} x = x ^ 2 {x > 0}

Эта триада корректна, т.к. если перед выполнением операции x^2, предусловие выполняется и значение x равно 5, то после выполнения этой операции, постусловие (x больше нуля) будет гарантировано выполняться (при условии корректной реализации целочисленной арифметики). Из этого примера видно, что приведенное постусловие не является самым сильным. В приведенном примере самым сильным постусловием при заданном предусловии является {x = 25}, а самым слабым предусловием при заданном постусловии является {x > 0}. Из выполняемой формулы корректности всегда можно породить новые выполняемые формулы, путем ослабления постусловия или усиления предусловия.

Бертран Мейер в своей книге "Объектно-ориентированное конструирование программных систем" описал значение сильных и слабых условий на примере контракта человека.

Давайте взглянем на формулу корректности с позиции человека, собирающегося наняться на работу по выполнению операции A. Каковы с его точки зрения наилучшие предусловие P и постусловие Q, если у него есть возможность выбора? Возможность усиления предусловия означает, что можно предъявлять более жесткие требования к работодателю, что можно уменьшить число ситуаций, в которых следует приступать к выполнению работы. Так что сильное предусловие это "хорошие новости" для работника. Наилучшей для него работой — синекурой является работа, чья спецификация выражается формулой:

{False} A {...}

Постусловие здесь не специфицировано, поскольку не имеет значения каково оно. К выполнению работы можно вообще не приступать, поскольку нет ни одного начального состояния, в котором предусловие было бы истинным. Так что если вам предложат такую синекуру, немедленно соглашайтесь, не глядя на постусловие — требования, предъявляемые к выполненной работе.
Для постусловия ситуация меняется на противоположную. Лучшими для работника являются более слабые условия — это "хорошие новости"; в этом случае хорошо нужно уметь делать очень немногое. Наилучшей работой — второй синекурой является работа, заданная спецификацией:

{...} A {True}

Как бы не была выполнена работа, постусловие в этом случае будет истинным по определению.
Обсуждение того, будет ли усиление или ослабление утверждений "хорошей" или "плохой" новостью, шло с позиции работника, нанимающегося для выполнения работы. Обратим ситуацию, и рассмотрим ее с позиции работодателя. В этом случае слабое предусловие станет "хорошей" новостью, поскольку означает выполнение работы для большего множества входных случаев; более предпочтительным теперь является сильное постусловие, поскольку оно расширяет получение важных результатов.

Понятно, что тип возвращаемого значения на прямую связан с силой постусловия (т.к. возвращаемое значение является результатом выполнения некоторой операции). Более конкретный тип возвращаемого значения усиливает постусловие, а более базовый тип — наоборот ослабляет.

Теперь, чтобы ответить на вопрос о том, какой тип возвращаемого значения выбрать, нужно знать, на какой стороне (с точки зрения операции) вы находитесь или какая из двух сторон важнее? Например, если важно получить максимальную функциональность на стороне клиента (потребителя услуги), то необходимо сильное постусловие и возврат наиболее конкретного типа возвращаемого значения (если речь идет о коллекциях, то List<T> или другой конкретный тип). Если же необходимо обеспечить минимальную стоимость эволюции и сопровождения поставщика услуги, то выгоднее использовать слабое постусловие и возвращать базовые типы в качестве типа возвращаемого значения (например, IEnumerable<T>). Если же речь идет о разумном компромиссе, между минимизацией затрат на эволюцию и сопровождение кода и функциональностью, то нужно рассматривать контекст использования возвращаемого значения и выбирать некоторый промежуточный вариант.

Прагматично-практический способ выбора типа возвращаемого значения

Прагматичный подход к вопросу выбора типа возвращаемого значения зависит прежде всего от способа использования метода, а точнее от того, является ли класс, реализующий этот метод, библиотечным или же имеется ввиду сущность бизнес-приложения. Если речь идет о библиотеке, которую будут использовать сотни тысяч пользователей, то цена ошибки в открытом интерфейсе класса возрастает многократно, что требует значительно более высокой квалификации проектировщика и серьезное смещение акцентов в сторону удобства использования пользователями даже в ущерб принципам декомпозиции или стоимости сопровождения. Чтобы оценить сложность проектирования крупных библиотек (framework-ов), стоит обратить внимание на количество недочетов у таких монстров как Microsoft или Sun и почитать замечательную книгу Framework Design Guidelines, 2nd edition by Krzysztof Cwalina and Brad Abrams. Кроме того, большинство пользователей все же сталкиваются с вопросами выбора типа возвращаемого значения при проектировании бизнес-приложений, поэтому далее я буду рассчитывать, что речь идет не о библиотеках, а о коде бизнес-логики логики.

Для любого кода в приложении характерны два важных показателя: сцепление (cohesion) и связанность (coupling). При проектировании очень важно максимизировать связи внутри компонентов (высокое сцепление, high cohesion) и минимизировать связи между компонентами (низкая связанность, low coupling). Если проектировщику удастся обеспечить эти показатели, то количество изменений, которые придется внести в код при ошибочном выборе типа возвращаемого значения (при изменении интерфейса, если рассматривать более общий случай) будут минимальны. В случае, если один человек отвечает и за поставщика услуг (за сам компонент), и за клиента (который этими услугами пользуется), то подобное изменение сложно назвать катастрофическим. Если же за разные компоненты отвечают разные люди, то изменение интерфейса взаимодействия будет неприятным, но не смертельным (кроме того, всегда можно добавить новый метод не удаляя старый, при этом старый пометить как устаревший (Obsolete), если ваша среда это позволяет).

Хотя часто изменения интерфейса бизнес-объектов не являются чем-то катастрофическим, каждый проектировщик стремится минимизировать подобные проблемы. Для этого в ходе анализа предметной области необходимо оценить то, какие задачи выполняет этот класс и какое наиболее вероятное (и, возможно, удобное) применение результатов выполнения операции. Если речь идет о возврате коллекции, то нужно подумать над следующими вопросами: следует ли возвращать копию коллекции, или можно вернуть ссылку на внутреннее поле или свойство; какое предполагаемое количество элементов будет в коллекции; как часто будет вызываться этот метод; будет ли он вызываться несколько раз подряд; нужно ли будет изменять полученную коллекцию; нужен ли доступ по индексу и т.д. Так, в случае возможности применения ленивых вычислений стоит обратить внимание на IEnumerable<T>, в случае необходимости доступа к элементам по индексу - IList<T>. Совершенно ясно, что на все эти вопросы невозможно дать ответ на этапе проектирования, а также вполне вероятно, что ответы могут меняться в процессе разработки или эволюции программы, но нужно же как-то получить отправную точку. При этому задача проектировщика заключается в том, чтобы выбрать такой тип возвращаемого значения, который будет минимальным образом отвечать потребностям вызывающей стороны.

Вместо заключения

Выбор правильного типа возвращаемого значения - задача непростая, а на ранних этапах разработки программного обеспечения и не всегда выполнимая. Но даже в этом случае принципы, заложенные в проектировании по контракту могут быть весьма полезными для формализации отношений между различными классами, что позволит использовать эту информацию в качестве документации, а также во время статического анализа кода. В любом случае главное помнить следующее: ваша задача - получить удовлетворительное решение за конечное время, а не идеальное решение, но за бесконечное время.

четверг, 14 января 2010 г.

Шаблоны проектирования. История успеха

Эта статья опубликована в 3-м номере журнала RSDN Magazine за 2009 год.

Введение

Шаблоны проектирования уже длительное время занимают почетное место в арсенале многих разработчиков. Одни их используют в повседневной практике, другие пишут о них статьи или книги, третьи собирают вместе коллективный опыт и создают новые шаблоны. Шаблоны проникли в самые разные области разработки программного обеспечения, о них написаны тысячи статей и выпущены десятки книг. Но, несмотря на свою популярность и распространенность, далеко не все знают историю появления этого феномена, причины возникновения и тех героев, благодаря которым наша индустрия пополнилась столь полезным инструментом. В этой статье я хочу восполнить этот пробел и рассказать о той роли, которую сыграли труды архитектора и философа Кристофера Александера, как его идеи переняли представители компьютерного сообщества и описать путь, который проделали шаблоны, прежде чем они получили признание и стали достоянием широкой общественности.

История зарождения

Труды архитектора и философа Кристофера Александера (Christopher Alexander), такие как “The Pattern Language” (1977), “The Timeless Way of Building” (1979), “Notes On The Synthesis Of Form” (1964) упоминаются в компьютерной литературе не реже, чем труды Дейкстры, Хоара или Кнута. Практически в каждой книге о шаблонах проектирования говорится о серьезном влиянии трудов Александера на область разработки программного обеспечения и в частности на идею создания шаблонов проектирования. Однако шаблоны проектирования – это не единственная область, на которую оказали влияние труды этого замечательного человека. Еще в 1979 году в своей книге “Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design” Эд Йордон (Ed Yourdon) и Ларри Константайн (Larry L. Constantine),одни из первых определили фундаментальные понятия модульности ПО, такие как сцепление (cohesion) и связность (coupling), основываясь на понятиях, приведенных в книге Алексендера “Notes On The Synthesis Of Form”.

По мнению Александера основной задачей при декомпозиции системы является осуществление следующих двух условий:

  • максимизация связей внутри компонентов (высокое сцепление, high cohesion) и
  • минимизация связей между компонентами (низкая связанность, low coupling).

Сегодня эти проблемы широко известны в программной индустрии и описаны в любой книге по проектированию и построению программных систем, но, если верить Александеру, они присущи проектированию любых сложных систем.

Следующим упоминанием трудов Александера, на этот раз книг “The Timeless Way of Building” и “The Pattern Language” стали Том ДеМарко (Tom DeMarco) и Тим Листер (Tim Lister) в своей знаменитой книге “Peopleware: Productive Projects and Teams”, вышедшей в свет в 1987 году. Авторы считают, что окружение является существенным фактором, влияющим на продуктивность человека, занятого интеллектуальным трудом, и в попытке определить идеальное рабочее место ДеМарко и Листер обратили свои взоры на труды знаменитого архитектора. Анализируя удачные рабочие места различных организаций, авторы создали четыре шаблона, которые, по их мнению, существенно повышают комфорт сотрудника и позволяют выполнять свою работу максимально продуктивно.

Идея шаблонов проектирования в области разработки ПО появилась в умах сторонников объектно-ориентированного программирования во второй половине 80-х годов прошлого века. Как и многие другие хорошие идеи, идея повторного использования не только кода, но и архитектурных и проектных решений, пришла в голову одновременно разным экспертам в области разработки программного обеспечения.

Одним из таких экспертов был Кент Бек. Вот что он пишет:

«Впервые я услышал о шаблонах будучи студентом Университета Орегона. Многие студенты, с которыми я жил в общежитии на первом курсе, были из Школы Архитектуры. И поскольку я рисовал планы домов с шести или семи лет, они указали мне на труды Кристофера Александера. Я прочитал его книгу “The Timeless Way of Building” от корки до корки в течение семи месяцев.

Я работал на Tektronix в течение полутора лет, когда я снова столкнулся с трудами Александера. Я нашел потертую старую книгу “Notes on the Synthesis of Form”. Объяснение методологии Александера во вступлении ко второму изданию нашло отражение с моей точкой зрения, что снова меня привело к книге “The Timeless Way of Building”. Мне показалось, что все, что он не любит в архитекторах, я не люблю в разработчиках ПО. Я убедил Варда Каннингема, что мы нашли что-то важное».

В 1987 году Вард Каннингем (Ward Cunningham) и Кент Бек (Kent Beck) поделились своим первым опытом применения языка шаблонов на практике на конференции OOPSLA-87. В то время они оба работали над одним проектом и столкнулись со сложностью проектирования пользовательского интерфейса. Тогда они решили воспользоваться идеей языка шаблонов Кристофера Александера. Александер считал, что наиболее оптимальным является проектирования жилища теми, кто будет в них проживать, т.к. именно они наиболее точно осознают собственные потребности. Кент Бек и Вард Каннингем посчитали эту идею весьма заманчивой и разработали язык шаблонов для проектирования пользовательских интерфейсов пользователями.

«Мы предлагаем радикальное изменение проектирования и реализации, используя концепцию адаптированную из работы Кристофера Александера, архитектора и основателя Центра по изучению структуры окружающей среды (Center for Environmental Structure). Александер предлагает, чтобы дома и офисы проектировались и строились их жителями. Он считает, что эти люди лучше всего знают свои потребности в определенной структуре. Мы согласны с этим мнением и считаем, что тоже самое относится и к компьютерным программам. Пользователь сам должен писать свои программы. Идея выглядит глупой, если взглянуть на размер и сложность зданий и программ, а также множество лет обучения профессии проектировщика. Однако Александер предлагает убедительный сценарий, который основывается на концепции «языка шаблонов».

Язык шаблонов предоставляет проектировщику работающие решения всех известных проблем в области проектирования. Это последовательность кусочков знаний, которые изложены и организованы в таком виде, что позволяют проектировщику задавать правильные вопросы и получать ответы в нужные моменты времени. Александер организовывал эти кусочки знаний в письменные шаблоны, в определенной однотипной структуре. Каждый шаблон имел выражение, описывающее проблему, условия, которые привели к этой проблеме и, самое главное, решение, которое работает в этих условиях. Язык шаблонов содержит шаблоны целого строения, например, жилого здания, или интерактивной компьютерной программы. В языке шаблонов, шаблоны связаны с другими шаблонами и решения применяются на основании этой взаимосвязи. Письменные шаблоны включают эти взаимосвязи в качестве пролога и эпилога».

Несмотря на успех применения языка шаблонов на практике, Кент Бек и Вард Каннингем были первыми и последними, кто попытался разработать и применить полноценный язык шаблонах, в таком виде, в котором он представлен в труде Кристофера Александера.

В это же время, начиная с 1988 года Эриx Гамма (Erich Gamma), Андре Веинанд (Andre Weinand) и Рудольф Марти (Rudolf Marty) начали работу над объектно-ориентированной библиотекой на С++ под названием “ET++”. В этом же году на конференции OOPSLA’88 они выступают с докладом об этой библиотеке. Эрих также задумался о важности повторного использования проектных решений (или шаблонов) своей библиотеки. В 1991 году Эриху приходит идея написании диссертации на тему шаблонов проектирования и он начинает сотрудничать с другими членами «Банды четырех» для дополнительного изучения этой темы. Именно в 1991 году перед конференцией European Conference of Object-Oriented Programming (ECOOP) Ерих Гамма и Ричард Хелм собрались вместе для создания первого каталога шаблонов проектирования, которые, в конечном счете, стали основой знаменитой книгой “Design Patterns”. Они определили множество шаблонов, включая следующие:

  1. Composite
  2. Decider
  3. Observer
  4. Constrainer

Многие из этих шаблонов попали в книгу “Design Patterns”, в то время, как многие другие так и остались неизвестными.

В конце 1988 года Джеймс Коплиен (James Coplien) начал каталогизировать специфичные для С++ низкоуровневые шаблоны, которые он называл идиомами. Ранние черновики этой работы использовались для обучения объектно-ориентированному программированию и С++ в AT&T с начала 1989 года. В 1991 года вышла книга Джеймса Комплиена “Advanced C++ Programming Styles and Idioms”, которую можно считать первой книгой по С++ для продолжающих программистов и первой книгой по шаблонам проектирования.

Питер Код (Peter Coad) также параллельно исследовал вопрос шаблонов проектирования, в результате чего на свет появилась статья в Communications of the ACM в 1992 году.

К этому времени (начало 1990-х годов) интерес к шаблонам проектирования вырос достаточно, чтобы ключевые специалисты в этом вопросе взялись за объединение опыта каждого из них. В 1993 году проводится множество конференций и семинаров, на которых основное внимание уделяется шаблонам проектирования. Поворотным событием в истории шаблонов проектирования становится знаменитая книга Банды четырех, которая впервые была представлена на конференции OOPSLA’94, где было продано 750 копий этой книги, что более чем в семь раз превосходит количество любых технических книг, когда-либо проданных на этой конференции.

История успеха

Книга Design Patterns является одной из самых популярных технических книг за всю историю, о ней вот уже пятнадцать лет пишут статьи, ссылаются в других книгах, обсуждают, критикуют, хвалят. По некоторым сведениям эта книга является самой продаваемой компьютерной книгой за всю историю (на начало 2009 года продано более 350 000 экземпляров и ежемесячно продается более 1000).

После выхода книги банды четырех тема шаблонов проектирования стала доступна не только в академических кругах, но и стала активно обсуждаться и применяться простыми разработчиками. Тема шаблонов проектирования активно затрагивается на различных коференциях и семинарах, включая OOPSLA (Object-Oriented Programming, Systems, Languages & Applications) и PLoP (Pattern Languages of Programs). О шаблонах проектирования вышло множество книг. Часть из них продолжают исследование классических шаблонов проектирования, поднятых бандой четырех в своей книге, но помимо этого, шаблоны стали заполнять все большее и большее количество смежных областей. Сегодня шаблоны практически повсюду: существуют шаблоны кодирования, шаблоны рефакторинга, шаблоны реализации корпоративных приложений, шаблоны работы с базами данных, шаблоны распределенных приложений, шаблоны работы с многопоточностью и множество других.

Шаблоны проектирования получили весьма широкое распространение, но несмотря на это они в течение длительного времени оставались прерогативой архитектора и разработчика, а не менеджера. Но за последние несколько лет эта картина начала меняться. Вслед за книгой  Джеймса Коплиена и Нила Харрисона “Organizational Patterns of Agile Software Development” вышла книга Тома ДеМарко, Тима Листера и др. “Adrenaline Junkies and Template Zombies: Understanding Patterns of Project Behavior”, посвященная шаблонам поведения программных проектов.

Рассматривая историю возникновения шаблонов проектирования и их непосредственную близость к языку шаблонов Кристофера Александера нельзя не отметить существенные различия. Несмотря на распространения идеи шаблонов, язык шаблонов в понимании Александера так и не был создан. Существуют шаблоны, предназначенные для решения различных задач, шаблоны для различных уровней абстракции и различных уровней иерархии системы. Эти шаблоны каким-то образом связаны с другими шаблонами более высокого и более низкого уровней, но при этом эта связь не является жесткой и формальной, в результате даже опытный разработчик не может описать всю систему с помощью какого-либо языка шаблонов. О шаблонах проектирования знают многие разработчики, проектировщики, архитекторы, но об этом явлении совершенно ничего не известно пользователям, хотя именно эта идея лежит в основе языка шаблонов Александера. Александер считал, что пользователь лучше всего знает свои нужды и если им дать формальный инструмент, то с его помощью они сами смогут построить лучшие прототипы, которые впоследствии будут реализованы командой разработчиков. Эту идею пытались развить Кент Бек и Вард Каннингем в самом начале своей работы с шаблонами, и даже получили положительные результаты, но идея шаблонов пошла по другому пути, который привел к повторному использованию идей командой разработчиков, но без участия в этом процессе конечного пользователя. На сегодняшний день основная роль шаблонов – это повторное использование опыта в различных областях разработки ПО, устранение коммуникационного барьера внутри команды разработчиков и между ними, повышение качества создаваемого продукта, за счет использования проверенных годами решений. Шаблоны не стали «серебряной пулей», но они сделали достаточно для компьютерного сообщества, чтобы к этому явлению относились с уважением и знали не только, что из себя представляют шаблоны проектирования, но и знали историю этого феномена.

Литература
  1. Christopher Alexander, Notes On The Synthesis Of Form, 1964
  2. Christopher Alexander, The Pattern Language, 1977
  3. Christopher Alexander, The Timeless Way of Building, 1979
  4. Edward Yourdon, Larry L. Constantine, Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design, 1979
  5. Tom DeMarco, Timothy Lister, Peopleware: Productive Projects and Teams, 1999
  6. Kent Beck, Ward Cunningham, Using Pattern Languages for Object-Oriented Programs, OOPSLA-87
  7. James Coplien, Software Patterns, 1996
  8. Jonathan Erickson, Dr. Dobb's Journal, March 1998
  9. James Coplien, Advanced C++ Programming Styles and Idioms, 1991
  10. Erich Gamma et al. Design Patterns: Elements of Reusable Object-Oriented Software, 1994